/**
 * @copyright Sister Software
 * @license AGPL-3.0
 * @author Teffen Ellis, et al.
 */
/**
 * A set-like collection that maintains the order of its elements.
 */
export class Sequence extends Set {
    #first;
    #last;
    /**
     * The first node in the sequence.
     */
    get first() {
        return this.#first;
    }
    /**
     * The last node in the sequence.
     */
    get last() {
        return this.#last;
    }
    constructor(nodes = []) {
        super(nodes);
        this.#first = nodes[0] ?? null;
        this.#last = nodes[nodes.length - 1] ?? null;
    }
    /**
     * Clear the sequence.
     */
    clear() {
        super.clear();
        this.#first = null;
        this.#last = null;
    }
    /**
     * Add a node to the sequence.
     */
    add(...nodes) {
        if (!nodes.length)
            return this;
        for (const node of nodes) {
            if (typeof node === "undefined" || node === null) {
                throw new Error("Attempted to add an undefined or null node to a sequence.");
            }
            super.add(node);
        }
        if (!this.#first) {
            this.#first = nodes[0];
        }
        this.#last = nodes[nodes.length - 1];
        return this;
    }
    /**
     * Delete a node from the sequence.
     */
    delete(node) {
        if (typeof node === "undefined" || node === null) {
            throw new Error("Attempted to delete an undefined or null node from a sequence.");
        }
        const deleted = super.delete(node);
        if (!deleted)
            return false;
        if (this.size === 0) {
            this.#first = null;
            this.#last = null;
        }
        else if (this.#first === node) {
            this.#first = this.values().next().value ?? null;
        }
        else if (this.#last === node) {
            this.#last = Array.from(this).pop() ?? null;
        }
        return true;
    }
    /**
     * Sort the sequence in place.
     */
    sort(callback) {
        const currentEntries = Array.from(this);
        currentEntries.sort(callback);
        this.clear();
        for (const node of currentEntries) {
            super.add(node);
        }
        this.#first = currentEntries[0] ?? null;
        this.#last = currentEntries[currentEntries.length - 1] ?? null;
        return this;
    }
    /**
     * Serialize the sequence to JSON.
     */
    toJSON() {
        return Array.from(this);
    }
    /**
     * Given a property key of the items in the sequence, return an iterator of the values of that
     * property.
     *
     * This is useful when building nested maps of properties.
     */
    pluck(property) {
        return Iterator.from(this).map((node) => node[property]);
    }
    toString() {
        return `Sequence(${this.size})`;
    }
    [Symbol.for("nodejs.util.inspect.custom")]() {
        const { first } = this;
        if (!first)
            return "Sequence(0)";
        const constructorName = first.constructor.name;
        return `${constructorName}Sequence(${this.size})`;
    }
}
export function disposeOf(set) {
    for (const node of set) {
        if (!node || typeof node !== "object")
            continue;
        node[Symbol.dispose]?.();
    }
    set.clear();
}
/**
 * A set that cleans up its elements when disposed.
 */
export class DisposableSet extends Set {
    [Symbol.dispose]() {
        for (const node of this) {
            if (!node || typeof node !== "object")
                continue;
            node[Symbol.dispose]?.();
        }
        this.clear();
    }
}
/**
 * A set that asynchronously cleans up its elements when disposed.
 */
export class AsyncDisposableSet extends Set {
    async [Symbol.asyncDispose]() {
        for (const node of this) {
            await node[Symbol.asyncDispose]?.();
        }
        this.clear();
    }
}
//# sourceMappingURL=set.js.map