Skip to content

Commit 46e8bc4

Browse files
authored
feat(BitField): add BitField base class (#3759)
* feat(BitField): add BitField base class * fix(Permissions): properly deprecate the getters/setters
1 parent b7ccf9a commit 46e8bc4

File tree

7 files changed

+235
-125
lines changed

7 files changed

+235
-125
lines changed

src/client/Client.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ class Client extends EventEmitter {
418418
* .catch(console.error);
419419
*/
420420
generateInvite(permissions) {
421-
permissions = typeof permissions === 'undefined' ? 0 : Permissions.resolve(permissions);
421+
permissions = Permissions.resolve(permissions);
422422
return this.fetchApplication().then(application =>
423423
`https://discordapp.com/oauth2/authorize?client_id=${application.id}&permissions=${permissions}&scope=bot`
424424
);

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module.exports = {
99
WebhookClient: require('./client/WebhookClient'),
1010

1111
// Utilities
12+
BitField: require('./util/BitField'),
1213
Collection: require('./util/Collection'),
1314
Constants: require('./util/Constants'),
1415
DiscordAPIError: require('./client/rest/DiscordAPIError'),

src/structures/Presence.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ class Game {
192192
return new Date(this.createdTimestamp);
193193
}
194194

195+
/**
196+
* Flags that describe the activity
197+
* @type {ActivityFlags[]}
198+
*/
195199
get flags() {
196200
const flags = [];
197201
for (const [name, flag] of Object.entries(ActivityFlags)) {

src/util/BitField.js

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/**
2+
* Data structure that makes it easy to interact with a bitfield.
3+
*/
4+
class BitField {
5+
/**
6+
* @param {BitFieldResolvable} [bits=0] Bits(s) to read from
7+
*/
8+
constructor(bits) {
9+
/**
10+
* Bitfield of the packed bits
11+
* @type {number}
12+
*/
13+
this.bitfield = this.constructor.resolve(bits);
14+
}
15+
16+
/**
17+
* Checks whether the bitfield has a bit, or any of multiple bits.
18+
* @param {BitFieldResolvable} bit Bit(s) to check for
19+
* @returns {boolean}
20+
*/
21+
any(bit) {
22+
return (this.bitfield & this.constructor.resolve(bit)) !== 0;
23+
}
24+
25+
/**
26+
* Checks if this bitfield equals another
27+
* @param {BitFieldResolvable} bit Bit(s) to check for
28+
* @returns {boolean}
29+
*/
30+
equals(bit) {
31+
return this.bitfield === this.constructor.resolve(bit);
32+
}
33+
34+
/**
35+
* Checks whether the bitfield has a bit, or multiple bits.
36+
* @param {BitFieldResolvable} bit Bit(s) to check for
37+
* @returns {boolean}
38+
*/
39+
has(bit) {
40+
if (Array.isArray(bit)) return bit.every(p => this.has(p));
41+
bit = this.constructor.resolve(bit);
42+
return (this.bitfield & bit) === bit;
43+
}
44+
45+
/**
46+
* Gets all given bits that are missing from the bitfield.
47+
* @param {BitFieldResolvable} bits Bits(s) to check for
48+
* @param {...*} hasParams Additional parameters for the has method, if any
49+
* @returns {string[]}
50+
*/
51+
missing(bits, ...hasParams) {
52+
if (!Array.isArray(bits)) bits = new this.constructor(bits).toArray(false);
53+
return bits.filter(p => !this.has(p, ...hasParams));
54+
}
55+
56+
/**
57+
* Freezes these bits, making them immutable.
58+
* @returns {Readonly<BitField>} These bits
59+
*/
60+
freeze() {
61+
return Object.freeze(this);
62+
}
63+
64+
/**
65+
* Adds bits to these ones.
66+
* @param {...BitFieldResolvable} [bits] Bits to add
67+
* @returns {BitField} These bits or new BitField if the instance is frozen.
68+
*/
69+
add(...bits) {
70+
let total = 0;
71+
for (const bit of bits) {
72+
total |= this.constructor.resolve(bit);
73+
}
74+
if (Object.isFrozen(this)) return new this.constructor(this.bitfield | total);
75+
this.bitfield |= total;
76+
return this;
77+
}
78+
79+
/**
80+
* Removes bits from these.
81+
* @param {...BitFieldResolvable} [bits] Bits to remove
82+
* @returns {BitField} These bits or new BitField if the instance is frozen.
83+
*/
84+
remove(...bits) {
85+
let total = 0;
86+
for (const bit of bits) {
87+
total |= this.constructor.resolve(bit);
88+
}
89+
if (Object.isFrozen(this)) return new this.constructor(this.bitfield & ~total);
90+
this.bitfield &= ~total;
91+
return this;
92+
}
93+
94+
/**
95+
* Gets an object mapping field names to a {@link boolean} indicating whether the
96+
* bit is available.
97+
* @param {...*} hasParams Additional parameters for the has method, if any
98+
* @returns {Object}
99+
*/
100+
serialize(...hasParams) {
101+
const serialized = {};
102+
for (const flag of Object.keys(this.constructor.FLAGS)) {
103+
serialized[flag] = this.has(this.constructor.FLAGS[flag], ...hasParams);
104+
}
105+
return serialized;
106+
}
107+
108+
/**
109+
* Gets an {@link Array} of bitfield names based on the bits available.
110+
* @param {...*} hasParams Additional parameters for the has method, if any
111+
* @returns {string[]}
112+
*/
113+
toArray(...hasParams) {
114+
return Object.keys(this.constructor.FLAGS).filter(bit => this.has(bit, ...hasParams));
115+
}
116+
117+
toJSON() {
118+
return this.bitfield;
119+
}
120+
121+
valueOf() {
122+
return this.bitfield;
123+
}
124+
125+
*[Symbol.iterator]() {
126+
yield* this.toArray();
127+
}
128+
129+
/**
130+
* Data that can be resolved to give a bitfield. This can be:
131+
* * A string (see {@link BitField.FLAGS})
132+
* * A bit number
133+
* * An instance of BitField
134+
* * An Array of BitFieldResolvable
135+
* @typedef {string|number|BitField|BitFieldResolvable[]} BitFieldResolvable
136+
*/
137+
138+
/**
139+
* Resolves bitfields to their numeric form.
140+
* @param {BitFieldResolvable} [bit=0] - bit(s) to resolve
141+
* @returns {number}
142+
*/
143+
static resolve(bit = 0) {
144+
if (typeof bit === 'number' && bit >= 0) return bit;
145+
if (bit instanceof BitField) return bit.bitfield;
146+
if (Array.isArray(bit)) return bit.map(p => this.resolve(p)).reduce((prev, p) => prev | p, 0);
147+
if (typeof bit === 'string' && typeof this.FLAGS[bit] !== 'undefined') return this.FLAGS[bit];
148+
throw new RangeError('Invalid bitfield flag or number.');
149+
}
150+
}
151+
152+
/**
153+
* Numeric bitfield flags.
154+
* <info>Defined in extension classes</info>
155+
* @type {Object}
156+
* @abstract
157+
*/
158+
BitField.FLAGS = {};
159+
160+
module.exports = BitField;

src/util/Constants.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,17 @@ exports.ActivityTypes = [
386386
'CUSTOM_STATUS',
387387
];
388388

389+
/**
390+
* Numeric activity flags. All available properties:
391+
* * `INSTANCE`
392+
* * `JOIN`
393+
* * `SPECTATE`
394+
* * `JOIN_REQUEST`
395+
* * `SYNC`
396+
* * `PLAY`
397+
* @typedef {string} ActivityFlag
398+
* @see {@link https://discordapp.com/developers/docs/topics/gateway#activity-object-activity-flags}
399+
*/
389400
exports.ActivityFlags = {
390401
INSTANCE: 1 << 0,
391402
JOIN: 1 << 1,

0 commit comments

Comments
 (0)