Skip to content

Commit

Permalink
refactor(bitfields): 💥 use funcitonal api for bitfields
Browse files Browse the repository at this point in the history
  • Loading branch information
lukadev-0 committed Apr 4, 2023
1 parent c79b666 commit 7c04976
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 241 deletions.
6 changes: 6 additions & 0 deletions .changeset/lazy-frogs-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@discokit/bitfields": minor
"discokit": minor
---

Switch to functional API for bitfields
16 changes: 0 additions & 16 deletions packages/discokit-bitfields/README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,3 @@
# @discokit/bitfields

Library for interacting with bitfields from the Discord API.

```ts
import { Permissions, Permission } from "@discokit/bitfields";

const permissions = new Permissions()
.add(Permission.ReadMessageHistory)
.add(Permission.AddReactions)
.add(Permission.ViewChannel);

console.log(permissions.bits); // 66624n

for (const flag of perms) {
const name = Permission.NAMES.get(flag);
console.log(name); // AddReactions, ViewChannel, ReadMessageHistory
}
```
39 changes: 12 additions & 27 deletions packages/discokit-bitfields/src/application-flags.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,15 @@
import { BitField, makeFlags } from "./bitfield";

const FLAGS = {
GATEWAY_PRESENCE: 1 << 12,
GATEWAY_PRESENCE_LIMITED: 1 << 13,
GATEWAY_GUILD_MEMBERS: 1 << 14,
GATEWAY_GUILD_MEMBERS_LIMITED: 1 << 15,
VERIFICATION_PENDING_GUILD_LIMIT: 1 << 16,
EMBEDDED: 1 << 17,
GATEWAY_MESSAGE_CONTENT: 1 << 18,
GATEWAY_MESSAGE_CONTENT_LIMITED: 1 << 19,
APPLICATION_COMMAND_BADGE: 1 << 23,
} as const;

/**
* An application flag
* https://discord.com/developers/docs/resources/application#application-object-application-flags
*/
export const ApplicationFlag = makeFlags(FLAGS);

/**
* Represents a set of gateway intents
* @see https://discord.com/developers/docs/resources/application#application-object-application-flags
*/
export class ApplicationFlags extends BitField<number> {
protected flags = Object.values(FLAGS);

public constructor(bits: number | BitField<number> = 0) {
super(bits);
}
}
export const ApplicationFlag = {
GatewayPresence: 1 << 12,
GatewayPresenceLimited: 1 << 13,
GatewayGuildMembers: 1 << 14,
GatewayGuildMembersLimited: 1 << 15,
VerificationPendingGuildLimit: 1 << 16,
Embedded: 1 << 17,
GatewayMessageContent: 1 << 18,
GatewayMessageContentLimited: 1 << 19,
ApplicationCommandBadge: 1 << 23,
} as const;
67 changes: 0 additions & 67 deletions packages/discokit-bitfields/src/bitfield.ts

This file was deleted.

25 changes: 5 additions & 20 deletions packages/discokit-bitfields/src/gateway-intents.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { BitField, makeFlags } from "./bitfield";

const FLAGS = {
/**
* A gateway intent
* @see https://discord.com/developers/docs/topics/gateway#gateway-intents
*/
export const GatewayIntent = {
Guilds: 1 << 0,
GuildMembers: 1 << 1,
GuildModeration: 1 << 2,
Expand All @@ -21,20 +23,3 @@ const FLAGS = {
AutoModerationConfiguration: 1 << 20,
AutoModerationExecution: 1 << 21,
} as const;

/**
* A gateway intent
* https://discord.com/developers/docs/topics/gateway#gateway-intents
*/
export const GatewayIntent = makeFlags(FLAGS);

/**
* Represents a set of gateway intents
*/
export class GatewayIntents extends BitField<number> {
protected flags = Object.values(FLAGS);

public constructor(bits: number | BitField<number> = 0) {
super(bits);
}
}
121 changes: 120 additions & 1 deletion packages/discokit-bitfields/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,124 @@
export * from "./application-flags";
export { BitField } from "./bitfield";
export * from "./gateway-intents";
export * from "./permissions";
export * from "./user-flags";

/**
* A bitfield, such as a set of permissions.
*/
export type BitField = number | bigint;

/**
* Adds bitfields together.
*
* @param bitfields - The bitfields to add together
* @returns The result of the addition
*
* @throws `TypeError` if no arguments are passed
*
* @example
* ```ts
* add(Permission.SendMessages, Permission.Speak, Permission.Connect);
* ```
*/
export function add<T extends BitField>(...bitfields: T[]): T {
return bitfields.reduce((a, b) => (a | b) as T);
}

/**
* Subtracts the bitfields from left to right.
*
* @param bitfields - The bitfields to subtract
* @returns The result of the subtraction
*
* @throws `TypeError` if no arguments are passed
*
* @example
* ```ts
* const permissions = add(Permission.SendMessages, Permission.Connect);
* subtract(permissions, Permission.Connect);
* ```
*/
export function subtract<T extends BitField>(...bitfields: T[]): T {
return bitfields.reduce((a, b) => (a & ~b) as T);
}

/**
* Checks whether or not the given `bitfield` has all of the
* bits of the bitfield `other`.
*
* @param bitfield - The bitfield to check in
* @param other - The bitfield to check for
* @returns `true` if `bitfield` contains all of the bits in `other`
*
* @example
* ```ts
* const permissions = add(Permission.SendMessages, Permission.Connect);
* has(permissions, Permission.Connect); // true
* ```
*/
export function has<T extends BitField>(bitfield: T, other: T): boolean {
return (bitfield & other) === other;
}

/**
* Iterates over the value (e.g. `1 << 0`) of the flags of the bitfield.
*
* @param flags - The bitfield flags object (such as {@link Permission} or {@link GatewayIntent})
*
* @example
* ```ts
* const permissions = add(Permission.SendMessages, Permission.Connect);
* for (const flag of bitfieldValues(permissions)) {
* console.log(flag);
* }
* ```
*/
export function* bitfieldValues<T extends BitField>(
flags: Record<string, T>,
bitfield: T
): Generator<T, void, unknown> {
for (const flag of Object.values(flags)) {
if (has(bitfield, flag)) {
yield flag;
}
}
}

/**
* Iterates over the keys (e.g. `"ManageGuild"`) of the flags of the bitfield.
*
* @param flags - The bitfield flags object (such as {@link Permission} or {@link GatewayIntent})
*
* @example
* ```ts
* const permissions = add(Permission.SendMessages, Permission.Connect);
* for (const flag of bitfieldKeys(permissions)) {
* console.log(flag);
* }
* ```
*/
export function* bitfieldKeys<T extends BitField, TFlags extends string>(
flags: Record<TFlags, T>,
bitfield: T
): Generator<TFlags, void, unknown> {
for (const key of Object.keys(flags) as TFlags[]) {
if (has(bitfield, flags[key])) {
yield key;
}
}
}

/**
* Turns this bitfield into JSON, for numbers, it will remain untouched,
* for `bigint`s, it will be turned into a string.
*
* @param bitfield - The bitfield
* @returns A number if the bitfield is a number, a string if the bitfield is a bigint
*/
export function bitfieldToJSON<T extends BitField>(
bitfield: T
): T extends number ? number : string {
const result = typeof bitfield === "number" ? bitfield : bitfield.toString();
return result as T extends number ? number : string;
}
25 changes: 5 additions & 20 deletions packages/discokit-bitfields/src/permissions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { BitField, makeBigIntFlags } from "./bitfield";

const FLAGS = {
/**
* A permission
* https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags
*/
export const Permission = {
CreateInstantInvite: 1n << 0n,
KickMembers: 1n << 1n,
BanMembers: 1n << 2n,
Expand Down Expand Up @@ -43,20 +45,3 @@ const FLAGS = {
UseEmbeddedActivities: 1n << 39n,
ModerateMembers: 1n << 40n,
} as const;

/**
* A permission
* https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags
*/
export const Permission = makeBigIntFlags(FLAGS);

/**
* Represents a set of permissions
*/
export class Permissions extends BitField<bigint> {
protected flags = Object.values(FLAGS);

public constructor(bits: bigint | BitField<bigint> = 0n) {
super(bits);
}
}
25 changes: 5 additions & 20 deletions packages/discokit-bitfields/src/user-flags.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { BitField, makeFlags } from "./bitfield";

const FLAGS = {
/**
* A flag on a user's account
* @see https://discord.com/developers/docs/resources/user#user-object-user-flags
*/
export const UserFlags = {
Staff: 1 << 0,
Partner: 1 << 1,
Hypesquad: 1 << 2,
Expand All @@ -17,20 +19,3 @@ const FLAGS = {
BotHTTPInteractions: 1 << 19,
ActiveDeveloper: 1 << 22,
} as const;

/**
* A flag on a user's account
* https://discord.com/developers/docs/resources/user#user-object-user-flags
*/
export const UserFlag = makeFlags(FLAGS);

/**
* Represents a set of user flags
*/
export class UserFlags extends BitField<number> {
protected flags = Object.values(FLAGS);

public constructor(bits: number | BitField<number> = 0) {
super(bits);
}
}
Loading

0 comments on commit 7c04976

Please sign in to comment.