veffect
Version:
powerful TypeScript validation library built on the robust foundation of Effect combining exceptional type safety, high performance, and developer experience. Taking inspiration from Effect's functional principles, VEffect delivers a balanced approach tha
268 lines (245 loc) • 8.76 kB
text/typescript
import * as Chunk from "../../Chunk.js"
import { dual, pipe } from "../../Function.js"
import * as Option from "../../Option.js"
import type * as Order from "../../Order.js"
import type { Predicate } from "../../Predicate.js"
import * as ReadonlyArray from "../../ReadonlyArray.js"
import * as SortedMap from "../../SortedMap.js"
import type * as STM from "../../STM.js"
import type * as TPriorityQueue from "../../TPriorityQueue.js"
import type * as TRef from "../../TRef.js"
import * as core from "./core.js"
import * as tRef from "./tRef.js"
/** @internal */
const TPriorityQueueSymbolKey = "effect/TPriorityQueue"
/** @internal */
export const TPriorityQueueTypeId: TPriorityQueue.TPriorityQueueTypeId = Symbol.for(
TPriorityQueueSymbolKey
) as TPriorityQueue.TPriorityQueueTypeId
const tPriorityQueueVariance = {
/* c8 ignore next */
_A: (_: any) => _
}
/** @internal */
export class TPriorityQueueImpl<in out A> implements TPriorityQueue.TPriorityQueue<A> {
readonly [TPriorityQueueTypeId] = tPriorityQueueVariance
constructor(readonly ref: TRef.TRef<SortedMap.SortedMap<A, [A, ...Array<A>]>>) {}
}
/** @internal */
export const empty = <A>(order: Order.Order<A>): STM.STM<TPriorityQueue.TPriorityQueue<A>> =>
pipe(
tRef.make(SortedMap.empty<A, [A, ...Array<A>]>(order)),
core.map((ref) => new TPriorityQueueImpl(ref))
)
/** @internal */
export const fromIterable =
<A>(order: Order.Order<A>) => (iterable: Iterable<A>): STM.STM<TPriorityQueue.TPriorityQueue<A>> =>
pipe(
tRef.make(
Array.from(iterable).reduce(
(map, value) =>
pipe(
map,
SortedMap.set(
value,
pipe(
map,
SortedMap.get(value),
Option.match({
onNone: () => ReadonlyArray.of(value),
onSome: ReadonlyArray.prepend(value)
})
)
)
),
SortedMap.empty<A, [A, ...Array<A>]>(order)
)
),
core.map((ref) => new TPriorityQueueImpl(ref))
)
/** @internal */
export const isEmpty = <A>(self: TPriorityQueue.TPriorityQueue<A>): STM.STM<boolean> =>
core.map(tRef.get(self.ref), SortedMap.isEmpty)
/** @internal */
export const isNonEmpty = <A>(self: TPriorityQueue.TPriorityQueue<A>): STM.STM<boolean> =>
core.map(tRef.get(self.ref), SortedMap.isNonEmpty)
/** @internal */
export const make = <A>(order: Order.Order<A>) => (...elements: Array<A>): STM.STM<TPriorityQueue.TPriorityQueue<A>> =>
fromIterable(order)(elements)
/** @internal */
export const offer = dual<
<A>(value: A) => (self: TPriorityQueue.TPriorityQueue<A>) => STM.STM<void>,
<A>(self: TPriorityQueue.TPriorityQueue<A>, value: A) => STM.STM<void>
>(2, (self, value) =>
tRef.update(self.ref, (map) =>
SortedMap.set(
map,
value,
Option.match(SortedMap.get(map, value), {
onNone: () => ReadonlyArray.of(value),
onSome: ReadonlyArray.prepend(value)
})
)))
/** @internal */
export const offerAll = dual<
<A>(values: Iterable<A>) => (self: TPriorityQueue.TPriorityQueue<A>) => STM.STM<void>,
<A>(self: TPriorityQueue.TPriorityQueue<A>, values: Iterable<A>) => STM.STM<void>
>(2, (self, values) =>
tRef.update(self.ref, (map) =>
Array.from(values).reduce(
(map, value) =>
SortedMap.set(
map,
value,
Option.match(SortedMap.get(map, value), {
onNone: () => ReadonlyArray.of(value),
onSome: ReadonlyArray.prepend(value)
})
),
map
)))
/** @internal */
export const peek = <A>(self: TPriorityQueue.TPriorityQueue<A>): STM.STM<A> =>
core.withSTMRuntime((runtime) => {
const map = tRef.unsafeGet(self.ref, runtime.journal)
return Option.match(
SortedMap.headOption(map),
{
onNone: () => core.retry,
onSome: (elements) => core.succeed(elements[0])
}
)
})
/** @internal */
export const peekOption = <A>(self: TPriorityQueue.TPriorityQueue<A>): STM.STM<Option.Option<A>> =>
tRef.modify(self.ref, (map) => [
Option.map(SortedMap.headOption(map), (elements) => elements[0]),
map
])
/** @internal */
export const removeIf = dual<
<A>(predicate: Predicate<A>) => (self: TPriorityQueue.TPriorityQueue<A>) => STM.STM<void>,
<A>(self: TPriorityQueue.TPriorityQueue<A>, predicate: Predicate<A>) => STM.STM<void>
>(2, (self, predicate) => retainIf(self, (a) => !predicate(a)))
/** @internal */
export const retainIf = dual<
<A>(predicate: Predicate<A>) => (self: TPriorityQueue.TPriorityQueue<A>) => STM.STM<void>,
<A>(self: TPriorityQueue.TPriorityQueue<A>, predicate: Predicate<A>) => STM.STM<void>
>(
2,
<A>(self: TPriorityQueue.TPriorityQueue<A>, predicate: Predicate<A>) =>
tRef.update(
self.ref,
(map) =>
SortedMap.reduce(map, SortedMap.empty(SortedMap.getOrder(map)), (map, value, key) => {
const filtered: ReadonlyArray<A> = ReadonlyArray.filter(value, predicate)
return filtered.length > 0 ?
SortedMap.set(map, key, filtered as [A, ...Array<A>]) :
SortedMap.remove(map, key)
})
)
)
/** @internal */
export const size = <A>(self: TPriorityQueue.TPriorityQueue<A>): STM.STM<number> =>
tRef.modify(
self.ref,
(map) => [SortedMap.reduce(map, 0, (n, as) => n + as.length), map]
)
/** @internal */
export const take = <A>(self: TPriorityQueue.TPriorityQueue<A>): STM.STM<A> =>
core.withSTMRuntime((runtime) => {
const map = tRef.unsafeGet(self.ref, runtime.journal)
return Option.match(SortedMap.headOption(map), {
onNone: () => core.retry,
onSome: (values) => {
const head = values[1][0]
const tail = values[1].slice(1)
tRef.unsafeSet(
self.ref,
tail.length > 0 ?
SortedMap.set(map, head, tail as [A, ...Array<A>]) :
SortedMap.remove(map, head),
runtime.journal
)
return core.succeed(head)
}
})
})
/** @internal */
export const takeAll = <A>(self: TPriorityQueue.TPriorityQueue<A>): STM.STM<Array<A>> =>
tRef.modify(self.ref, (map) => {
const builder: Array<A> = []
for (const entry of map) {
for (const value of entry[1]) {
builder.push(value)
}
}
return [builder, SortedMap.empty(SortedMap.getOrder(map))]
})
/** @internal */
export const takeOption = <A>(self: TPriorityQueue.TPriorityQueue<A>): STM.STM<Option.Option<A>> =>
core.effect<never, Option.Option<A>>((journal) => {
const map = pipe(self.ref, tRef.unsafeGet(journal))
return Option.match(SortedMap.headOption(map), {
onNone: (): Option.Option<A> => Option.none(),
onSome: ([key, value]) => {
const tail = value.slice(1)
tRef.unsafeSet(
self.ref,
tail.length > 0 ?
SortedMap.set(map, key, tail as [A, ...Array<A>]) :
SortedMap.remove(map, key),
journal
)
return Option.some(value[0])
}
})
})
/** @internal */
export const takeUpTo = dual<
(n: number) => <A>(self: TPriorityQueue.TPriorityQueue<A>) => STM.STM<Array<A>>,
<A>(self: TPriorityQueue.TPriorityQueue<A>, n: number) => STM.STM<Array<A>>
>(2, <A>(self: TPriorityQueue.TPriorityQueue<A>, n: number) =>
tRef.modify(self.ref, (map) => {
const builder: Array<A> = []
const iterator = map[Symbol.iterator]()
let updated = map
let index = 0
let next: IteratorResult<readonly [A, [A, ...Array<A>]], any>
while ((next = iterator.next()) && !next.done && index < n) {
const [key, value] = next.value
const [left, right] = pipe(value, ReadonlyArray.splitAt(n - index))
for (const value of left) {
builder.push(value)
}
if (right.length > 0) {
updated = SortedMap.set(updated, key, right as [A, ...Array<A>])
} else {
updated = SortedMap.remove(updated, key)
}
index = index + left.length
}
return [builder, updated]
}))
/** @internal */
export const toChunk = <A>(self: TPriorityQueue.TPriorityQueue<A>): STM.STM<Chunk.Chunk<A>> =>
tRef.modify(self.ref, (map) => {
const builder: Array<A> = []
for (const entry of map) {
for (const value of entry[1]) {
builder.push(value)
}
}
return [Chunk.unsafeFromArray(builder), map]
})
/** @internal */
export const toArray = <A>(self: TPriorityQueue.TPriorityQueue<A>): STM.STM<Array<A>> =>
tRef.modify(self.ref, (map) => {
const builder: Array<A> = []
for (const entry of map) {
for (const value of entry[1]) {
builder.push(value)
}
}
return [builder, map]
})