"use strict"; // https://github.com/discordjs/discord.js/blob/master/src/util/BitField.js // Apache License Version 2.0 Copyright 2015 - 2021 Amish Shah export type BitFieldResolvable = number | BigInt | BitField | string | BitFieldResolvable[]; /** * Data structure that makes it easy to interact with a bitfield. */ export class BitField { public bitfield: bigint = BigInt(0); public static FLAGS: Record; constructor(bits: BitFieldResolvable = 0) { this.bitfield = BitField.resolve(bits); } /** * Checks whether the bitfield has a bit, or any of multiple bits. */ any(bit: BitFieldResolvable): boolean { return (this.bitfield & BitField.resolve(bit)) !== 0n; } /** * Checks if this bitfield equals another */ equals(bit: BitFieldResolvable): boolean { return this.bitfield === BitField.resolve(bit); } /** * Checks whether the bitfield has a bit, or multiple bits. */ has(bit: BitFieldResolvable): boolean { if (Array.isArray(bit)) return bit.every((p) => this.has(p)); const BIT = BitField.resolve(bit); return (this.bitfield & BIT) === BIT; } /** * Gets all given bits that are missing from the bitfield. */ missing(bits: BitFieldResolvable) { if (!Array.isArray(bits)) bits = new BitField(bits).toArray(); return bits.filter((p) => !this.has(p)); } /** * Freezes these bits, making them immutable. */ freeze(): Readonly { return Object.freeze(this); } /** * Adds bits to these ones. * @param {...BitFieldResolvable} [bits] Bits to add * @returns {BitField} These bits or new BitField if the instance is frozen. */ add(...bits: BitFieldResolvable[]): BitField { let total = 0n; for (const bit of bits) { total |= BitField.resolve(bit); } if (Object.isFrozen(this)) return new BitField(this.bitfield | total); this.bitfield |= total; return this; } /** * Removes bits from these. * @param {...BitFieldResolvable} [bits] Bits to remove */ remove(...bits: BitFieldResolvable[]) { let total = 0n; for (const bit of bits) { total |= BitField.resolve(bit); } if (Object.isFrozen(this)) return new BitField(this.bitfield & ~total); this.bitfield &= ~total; return this; } /** * Gets an object mapping field names to a {@link boolean} indicating whether the * bit is available. * @param {...*} hasParams Additional parameters for the has method, if any */ serialize() { const serialized: Record = {}; for (const [flag, bit] of Object.entries(BitField.FLAGS)) serialized[flag] = this.has(bit); return serialized; } /** * Gets an {@link Array} of bitfield names based on the bits available. */ toArray(): string[] { return Object.keys(BitField.FLAGS).filter((bit) => this.has(bit)); } toJSON() { return this.bitfield; } valueOf() { return this.bitfield; } *[Symbol.iterator]() { yield* this.toArray(); } /** * Data that can be resolved to give a bitfield. This can be: * * A bit number (this can be a number literal or a value taken from {@link BitField.FLAGS}) * * An instance of BitField * * An Array of BitFieldResolvable * @typedef {number|BitField|BitFieldResolvable[]} BitFieldResolvable */ /** * Resolves bitfields to their numeric form. * @param {BitFieldResolvable} [bit=0] - bit(s) to resolve * @returns {number} */ static resolve(bit: BitFieldResolvable = 0n): bigint { if ((typeof bit === "number" || typeof bit === "bigint") && bit >= 0n) return BigInt(bit); if (bit instanceof BitField) return bit.bitfield; if (Array.isArray(bit)) return bit.map((p) => this.resolve(p)).reduce((prev, p) => BigInt(prev) | BigInt(p), 0n); if (typeof bit === "string" && typeof this.FLAGS[bit] !== "undefined") return this.FLAGS[bit]; throw new RangeError("BITFIELD_INVALID: " + bit); } }