Skip to content

Commit 1398c85

Browse files
feat(bitfield): port DJS bitfield module
1 parent 772340b commit 1398c85

File tree

2 files changed

+238
-0
lines changed

2 files changed

+238
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//reference => https://github.com/discordjs/discord.js/blob/14.22.1/packages/discord.js/src/util/MessageFlagsBitField.js
2+
3+
import { MessageFlags } from "discord-api-types/v10";
4+
import BitField from "./base.js";
5+
import { NonReverseEnumFlag } from "../utils/type.js";
6+
7+
/**
8+
* Data structure that makes it easy to interact with a Discord message flags bitfield.
9+
*/
10+
11+
export default class MessageFlagsBitField extends BitField<
12+
NonReverseEnumFlag<typeof MessageFlags>
13+
> {
14+
/**
15+
* Numeric message flags.
16+
* @memberof MessageFlagsBitField
17+
*/
18+
static Flags = MessageFlags as NonReverseEnumFlag<typeof MessageFlags>;
19+
}
20+
// Maybe, should keep it for documentation… not planning to have a doc anytime soon
21+
22+
/**
23+
* @name MessageFlagsBitField
24+
* @kind constructor
25+
* @memberof MessageFlagsBitField
26+
* @param {BitFieldResolvable} [bits=0] Bit(s) to read from
27+
*/
28+
29+
/**
30+
* Data that can be resolved to give a message flags bit field. This can be:
31+
* * A string (see {@link MessageFlagsBitField.Flags})
32+
* * A message flag
33+
* * An instance of {@link MessageFlagsBitField}
34+
* * An array of `MessageFlagsResolvable`
35+
* @typedef {string|number|MessageFlagsBitField|MessageFlagsResolvable[]} MessageFlagsResolvable
36+
*/
37+
38+
/**
39+
* Bitfield of the packed bits
40+
* @type {number}
41+
* @name MessageFlagsBitField#bitfield
42+
*/

src/structures/Bitfield/base.ts

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
//@ts-nocheck
2+
// reference => https://github.com/discordhttps/discord.https/blob/main/src/structures/util/Bitfield.ts
3+
4+
import {
5+
DiscordHttpsRangeError,
6+
DiscordHttpsErrorCodes,
7+
} from "../errors/index.js";
8+
9+
export type BitFieldResolvable<T extends number | bigint = number> =
10+
| T
11+
| string
12+
| BitField<T>
13+
| BitFieldResolvable<T>[];
14+
15+
/**
16+
* Data structure that makes it easy to interact with a bitfield.
17+
*/
18+
export default abstract class BitField<
19+
TEnum extends Record<string, number | bigint> = Record<string, number>,
20+
TValue = TEnum[keyof TEnum]
21+
> {
22+
/**
23+
* Numeric bitfield flags.
24+
*/
25+
static Flags: Record<string, number | bigint> = {};
26+
27+
/**
28+
* Default bit value
29+
*/
30+
static DefaultBit: number | bigint = 0;
31+
32+
/**
33+
* Bitfield of the packed bits
34+
*/
35+
bitfield: TValue;
36+
37+
/**
38+
* Bit(s) to read from
39+
*/
40+
constructor(
41+
bits: BitFieldResolvable<TValue> = new.target.DefaultBit as number
42+
) {
43+
this.bitfield = (
44+
new.target as typeof BitField & {
45+
resolve(bits: BitFieldResolvable<TValue>): TValue;
46+
}
47+
).resolve(bits);
48+
}
49+
50+
/**
51+
* Checks whether the bitfield has a bit, or any of multiple bits.
52+
* @param {BitFieldResolvable} bit Bit(s) to check for
53+
* @returns {boolean}
54+
*/
55+
any(bit: BitFieldResolvable): boolean {
56+
return (
57+
(this.bitfield & (this.constructor as typeof BitField).resolve(bit)) !==
58+
(this.constructor as typeof BitField).DefaultBit
59+
);
60+
}
61+
62+
/**
63+
* Checks if this bitfield equals another
64+
* @param {BitFieldResolvable} bit Bit(s) to check for
65+
* @returns {boolean}
66+
*/
67+
equals(bit: BitFieldResolvable): boolean {
68+
return this.bitfield === (this.constructor as typeof BitField).resolve(bit);
69+
}
70+
71+
/**
72+
* Checks whether the bitfield has a bit, or multiple bits.
73+
* @param {BitFieldResolvable} bit Bit(s) to check for
74+
* @returns {boolean}
75+
*/
76+
has(bit: BitFieldResolvable): boolean {
77+
bit = (this.constructor as typeof BitField).resolve(bit);
78+
return (this.bitfield & bit) === bit;
79+
}
80+
81+
/**
82+
* Gets all given bits that are missing from the bitfield.
83+
* @param {BitFieldResolvable} bits Bit(s) to check for
84+
* @param {...*} hasParams Additional parameters for the has method, if any
85+
* @returns {string[]}
86+
*/
87+
missing(bits: BitFieldResolvable, ...hasParams: any[]): string[] {
88+
return new (this.constructor as typeof BitField)(bits)
89+
.remove(this)
90+
.toArray(...hasParams);
91+
}
92+
93+
/**
94+
* Freezes these bits, making them immutable.
95+
*/
96+
freeze(): Readonly<this> {
97+
return Object.freeze(this);
98+
}
99+
100+
/**
101+
* Adds bits to these ones.
102+
*/
103+
add(...bits) {
104+
let total = (this.constructor as typeof BitField).DefaultBit;
105+
for (const bit of bits) {
106+
total |= (this.constructor as typeof BitField).resolve(bit);
107+
}
108+
if (Object.isFrozen(this))
109+
return new (this.constructor as typeof BitField)(this.bitfield | total);
110+
this.bitfield |= total;
111+
return this;
112+
}
113+
114+
/**
115+
* Removes bits from these.
116+
*/
117+
remove(...bits: BitFieldResolvable[]): this {
118+
let total = (this.constructor as typeof BitField).DefaultBit;
119+
for (const bit of bits) {
120+
total |= (this.constructor as typeof BitField).resolve(bit);
121+
}
122+
if (Object.isFrozen(this))
123+
return new (this.constructor as typeof BitField)(this.bitfield & ~total);
124+
this.bitfield &= ~total;
125+
return this;
126+
}
127+
/**
128+
* Gets an object mapping field names to a {@link boolean} indicating whether the
129+
* bit is available.
130+
* @param {...*} hasParams Additional parameters for the has method, if any
131+
* @returns {Object}
132+
*/
133+
serialize(...hasParams: any[]): Record<string, boolean> {
134+
const serialized: Record<string, boolean> = {};
135+
for (const [flag, bit] of Object.entries(
136+
(this.constructor as typeof BitField).Flags
137+
)) {
138+
if (isNaN(flag)) serialized[flag] = this.has(bit, ...hasParams);
139+
}
140+
return serialized;
141+
}
142+
143+
/**
144+
* Gets an {@link Array} of bitfield names based on the bits available.
145+
*/
146+
toArray(...hasParams: any[]): string[] {
147+
return [...this[Symbol.iterator](...hasParams)];
148+
}
149+
150+
toJSON(): number | string {
151+
return typeof this.bitfield === "number"
152+
? this.bitfield
153+
: this.bitfield.toString();
154+
}
155+
156+
valueOf(): number | bigint {
157+
return this.bitfield;
158+
}
159+
160+
*[Symbol.iterator](...hasParams: any[]): IterableIterator<string> {
161+
for (const bitName of Object.keys(
162+
(this.constructor as typeof BitField).Flags
163+
)) {
164+
if (isNaN(bitName) && this.has(bitName, ...hasParams)) yield bitName;
165+
}
166+
}
167+
168+
/**
169+
* Data that can be resolved to give a bitfield. This can be:
170+
* * A bit number (this can be a number literal or a value taken from {@link BitField.Flags})
171+
* * A string bit number
172+
* * An instance of BitField
173+
* * An Array of BitFieldResolvable
174+
*/
175+
static resolve(
176+
this: typeof BitField,
177+
bit: BitFieldResolvable = this.DefaultBit
178+
): number | bigint {
179+
const { DefaultBit } = this;
180+
if (typeof bit === typeof DefaultBit && bit >= DefaultBit) return bit;
181+
if (bit instanceof BitField) return bit.bitfield;
182+
if (Array.isArray(bit))
183+
return bit
184+
.map((b) => this.resolve(b))
185+
.reduce((prev, curr) => prev | curr, DefaultBit);
186+
if (typeof bit === "string") {
187+
if (!isNaN(Number(bit)))
188+
return typeof DefaultBit === "bigint" ? BigInt(bit) : Number(bit);
189+
if (this.Flags[bit] !== undefined) return this.Flags[bit];
190+
}
191+
throw new DiscordHttpsRangeError(
192+
DiscordHttpsErrorCodes.BitFieldInvalid,
193+
bit
194+
);
195+
}
196+
}

0 commit comments

Comments
 (0)