refactor(BitField): base class for Permissions, ActivityFlags, Speaking (#2765)

* abstract BitField from Permissions

* reduce useless code, improve docs

* add a ReadOnly identifier to the return type of Bitfield#freeze()

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html#partial-readonly-record-and-pick

* fix the RangeError

* update docs, convert Speaking and ActivityFlags to bitfields

* fix some docs

* Fix Speaking BitField oops

* docs for oops

* more incorrect docs

* Fix incorrectly named property

* add new classes to index

* fix missing @extends docs

* default bitfield resolve to 0, and cleanup defaulting everywhere

Also removes GuildMember#missiongPermissions() alias that had incorrect behavior

* Breaking: Rename Overwrite allowed and denied to allow and deny

To be consistent with the api's naming

* fix setSpeaking usage to bitfields instead of booleans

* fix speaking bug in playChunk

* docs: Updated typings

* fix: BitFieldResolvable should use RecursiveArray

* bugfix/requested change

* typings: Cleanup (#2)

* typings: Fix BitField#{toArray,@@iterator} output type

* typings: correct PermissionOverwrites property names and nitpicks
This commit is contained in:
bdistin
2018-08-21 04:56:41 -05:00
committed by SpaceEEC
parent 6be8172539
commit c62f01f0e4
18 changed files with 339 additions and 242 deletions

View File

@@ -1,120 +1,12 @@
const { RangeError } = require('../errors');
const BitField = require('./BitField');
/**
* Data structure that makes it easy to interact with a permission bitfield. All {@link GuildMember}s have a set of
* permissions in their guild, and each channel in the guild may also have {@link PermissionOverwrites} for the member
* that override their default permissions.
* @extends {BitField}
*/
class Permissions {
/**
* @param {PermissionResolvable} permissions Permission(s) to read from
*/
constructor(permissions) {
/**
* Bitfield of the packed permissions
* @type {number}
*/
this.bitfield = this.constructor.resolve(permissions);
}
/**
* Checks whether the bitfield has a permission, or multiple permissions.
* @param {PermissionResolvable} permission Permission(s) to check for
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
* @returns {boolean}
*/
has(permission, checkAdmin = true) {
if (permission instanceof Array) return permission.every(p => this.has(p, checkAdmin));
permission = this.constructor.resolve(permission);
if (checkAdmin && (this.bitfield & this.constructor.FLAGS.ADMINISTRATOR) > 0) return true;
return (this.bitfield & permission) === permission;
}
/**
* Gets all given permissions that are missing from the bitfield.
* @param {PermissionResolvable} permissions Permission(s) to check for
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
* @returns {string[]}
*/
missing(permissions, checkAdmin = true) {
if (!(permissions instanceof Array)) permissions = new this.constructor(permissions).toArray(false);
return permissions.filter(p => !this.has(p, checkAdmin));
}
/**
* Freezes these permissions, making them immutable.
* @returns {Permissions} These permissions
*/
freeze() {
return Object.freeze(this);
}
/**
* Adds permissions to these ones.
* @param {...PermissionResolvable} permissions Permissions to add
* @returns {Permissions} These permissions or new permissions if the instance is frozen.
*/
add(...permissions) {
let total = 0;
for (let p = permissions.length - 1; p >= 0; p--) {
const perm = this.constructor.resolve(permissions[p]);
total |= perm;
}
if (Object.isFrozen(this)) return new this.constructor(this.bitfield | total);
this.bitfield |= total;
return this;
}
/**
* Removes permissions from these.
* @param {...PermissionResolvable} permissions Permissions to remove
* @returns {Permissions} These permissions or new permissions if the instance is frozen.
*/
remove(...permissions) {
let total = 0;
for (let p = permissions.length - 1; p >= 0; p--) {
const perm = this.constructor.resolve(permissions[p]);
total |= perm;
}
if (Object.isFrozen(this)) return new this.constructor(this.bitfield & ~total);
this.bitfield &= ~total;
return this;
}
/**
* Gets an object mapping permission name (like `VIEW_CHANNEL`) to a {@link boolean} indicating whether the
* permission is available.
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
* @returns {Object}
*/
serialize(checkAdmin = true) {
const serialized = {};
for (const perm in this.constructor.FLAGS) serialized[perm] = this.has(perm, checkAdmin);
return serialized;
}
/**
* Gets an {@link Array} of permission names (such as `VIEW_CHANNEL`) based on the permissions available.
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
* @returns {string[]}
*/
toArray(checkAdmin = true) {
return Object.keys(this.constructor.FLAGS).filter(perm => this.has(perm, checkAdmin));
}
toJSON() {
return this.bitfield;
}
valueOf() {
return this.bitfield;
}
*[Symbol.iterator]() {
const keys = this.toArray();
while (keys.length) yield keys.shift();
}
class Permissions extends BitField {
/**
* Data that can be resolved to give a permission number. This can be:
* * A string (see {@link Permissions.FLAGS})
@@ -125,16 +17,14 @@ class Permissions {
*/
/**
* Resolves permissions to their numeric form.
* @param {PermissionResolvable} permission - Permission(s) to resolve
* @returns {number}
* Checks whether the bitfield has a permission, or multiple permissions.
* @param {PermissionResolvable} permission Permission(s) to check for
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
* @returns {boolean}
*/
static resolve(permission) {
if (typeof permission === 'number' && permission >= 0) return permission;
if (permission instanceof Permissions) return permission.bitfield;
if (permission instanceof Array) return permission.map(p => this.resolve(p)).reduce((prev, p) => prev | p, 0);
if (typeof permission === 'string') return this.FLAGS[permission];
throw new RangeError('PERMISSIONS_INVALID');
has(permission, checkAdmin = true) {
if (checkAdmin && super.has(this.constructor.FLAGS.ADMINISTRATOR)) return true;
return super.has(permission);
}
}