/**
* @since 1.0.0
*/
import * as Chunk from "@effect/data/Chunk"
import * as Dual from "@effect/data/Function"
import { type Inspectable, NodeInspectSymbol, toJSON, toString } from "@effect/data/Inspectable"
import * as MutableList from "@effect/data/MutableList"
import type { Pipeable } from "@effect/data/Pipeable"
import { pipeArguments } from "@effect/data/Pipeable"
const TypeId: unique symbol = Symbol.for("@effect/data/MutableQueue") as TypeId
/**
* @since 1.0.0
* @category symbol
*/
export type TypeId = typeof TypeId
/**
* @since 1.0.0
* @category symbol
*/
export const EmptyMutableQueue = Symbol.for("@effect/data/mutable/MutableQueue/Empty")
/**
* @since 1.0.0
* @category model
*/
export interface MutableQueue extends Iterable, Pipeable, Inspectable {
readonly [TypeId]: TypeId
/** @internal */
queue: MutableList.MutableList
/** @internal */
capacity: number | undefined
}
/**
* @since 1.0.0
*/
export declare namespace MutableQueue {
/**
* @since 1.0.0
*/
export type Empty = typeof EmptyMutableQueue
}
const MutableQueueProto: Omit, "queue" | "capacity"> = {
[TypeId]: TypeId,
[Symbol.iterator](this: MutableQueue): Iterator {
return Array.from(this.queue)[Symbol.iterator]()
},
toString() {
return toString(this.toJSON())
},
toJSON() {
return {
_id: "MutableQueue",
values: Array.from(this).map(toJSON)
}
},
[NodeInspectSymbol]() {
return this.toJSON()
},
pipe() {
return pipeArguments(this, arguments)
}
}
const make = (capacity: number | undefined): MutableQueue => {
const queue = Object.create(MutableQueueProto)
queue.queue = MutableList.empty()
queue.capacity = capacity
return queue
}
/**
* Creates a new bounded `MutableQueue`.
*
* @since 1.0.0
* @category constructors
*/
export const bounded = (capacity: number): MutableQueue => make(capacity)
/**
* Creates a new unbounded `MutableQueue`.
*
* @since 1.0.0
* @category constructors
*/
export const unbounded = (): MutableQueue => make(undefined)
/**
* Returns the current number of elements in the queue.
*
* @since 1.0.0
* @category getters
*/
export const length = (self: MutableQueue): number => MutableList.length(self.queue)
/**
* Returns `true` if the queue is empty, `false` otherwise.
*
* @since 1.0.0
* @category getters
*/
export const isEmpty = (self: MutableQueue): boolean => MutableList.isEmpty(self.queue)
/**
* Returns `true` if the queue is full, `false` otherwise.
*
* @since 1.0.0
* @category getters
*/
export const isFull = (self: MutableQueue): boolean =>
self.capacity === undefined ? false : MutableList.length(self.queue) === self.capacity
/**
* The **maximum** number of elements that a queue can hold.
*
* **Note**: unbounded queues can still implement this interface with
* `capacity = Infinity`.
*
* @since 1.0.0
* @category getters
*/
export const capacity = (self: MutableQueue): number => self.capacity === undefined ? Infinity : self.capacity
/**
* Offers an element to the queue.
*
* Returns whether the enqueue was successful or not.
*
* @since 1.0.0
*/
export const offer: {
(self: MutableQueue, value: A): boolean
(value: A): (self: MutableQueue) => boolean
} = Dual.dual<
(value: A) => (self: MutableQueue) => boolean,
(self: MutableQueue, value: A) => boolean
>(2, (self: MutableQueue, value: A) => {
const queueLength = MutableList.length(self.queue)
if (self.capacity !== undefined && queueLength === self.capacity) {
return false
}
MutableList.append(value)(self.queue)
return true
})
/**
* Enqueues a collection of values into the queue.
*
* Returns a `Chunk` of the values that were **not** able to be enqueued.
*
* @since 1.0.0
*/
export const offerAll: {
(values: Iterable): (self: MutableQueue) => Chunk.Chunk
(self: MutableQueue, values: Iterable): Chunk.Chunk
} = Dual.dual<
(values: Iterable) => (self: MutableQueue) => Chunk.Chunk,
(self: MutableQueue, values: Iterable) => Chunk.Chunk
>(2, (self: MutableQueue, values: Iterable) => {
const iterator = values[Symbol.iterator]()
let next: IteratorResult | undefined
let remainder = Chunk.empty()
let offering = true
while (offering && (next = iterator.next()) && !next.done) {
offering = offer(next.value)(self)
}
while (next != null && !next.done) {
remainder = Chunk.prepend(next.value)(remainder)
next = iterator.next()
}
return Chunk.reverse(remainder)
})
/**
* Dequeues an element from the queue.
*
* Returns either an element from the queue, or the `def` param.
*
* **Note**: if there is no meaningful default for your type, you can always
* use `poll(MutableQueue.EmptyMutableQueue)`.
*
* @since 1.0.0
*/
export const poll: {
(def: D): (self: MutableQueue) => D | A
(self: MutableQueue, def: D): A | D
} = Dual.dual<
(def: D) => (self: MutableQueue) => A | D,
(self: MutableQueue, def: D) => A | D
>(2, (self, def) => {
if (MutableList.isEmpty(self.queue)) {
return def
}
return MutableList.shift(self.queue)!
})
/**
* Dequeues up to `n` elements from the queue.
*
* Returns a `List` of up to `n` elements.
*
* @since 1.0.0
*/
export const pollUpTo: {
(n: number): (self: MutableQueue) => Chunk.Chunk
(self: MutableQueue, n: number): Chunk.Chunk
} = Dual.dual<
(n: number) => (self: MutableQueue) => Chunk.Chunk,
(self: MutableQueue, n: number) => Chunk.Chunk
>(2, (self: MutableQueue, n: number) => {
let result = Chunk.empty()
let count = 0
while (count < n) {
const element = poll(EmptyMutableQueue)(self)
if (element === EmptyMutableQueue) {
break
}
result = Chunk.prepend(element)(result)
count += 1
}
return Chunk.reverse(result)
})