finished implementation of Base64BitFlagSets
This commit is contained in:
73
node/src/builders/base64.ts
Normal file
73
node/src/builders/base64.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { Base64BitFlagDefinitionFactory, FlagDefinition } from '~/definitions'
|
||||
import { Base64BitFlagSet } from '~/flagsets'
|
||||
|
||||
import {
|
||||
applyDeclarations,
|
||||
FlagWithOrdinal,
|
||||
ListOfFlagsWithOrdinal,
|
||||
NamedFlagWithOrdinal,
|
||||
} from './declarative'
|
||||
import { FlagSetBuilder } from './generic'
|
||||
import {
|
||||
DefineWithOrdinal,
|
||||
RequireParentsOrDefineWithOrdinal,
|
||||
WithOrdinal,
|
||||
WithOrdinalOrCompose,
|
||||
} from './syntax'
|
||||
import { InvalidOperationError } from '~/errors'
|
||||
|
||||
export class Base64BitFlagSetBuilder
|
||||
implements
|
||||
WithOrdinalOrCompose<string, Base64BitFlagSet>,
|
||||
RequireParentsOrDefineWithOrdinal<string, Base64BitFlagSet>
|
||||
{
|
||||
private readonly _underlying: FlagSetBuilder<number>
|
||||
|
||||
public constructor() {
|
||||
this._underlying = new FlagSetBuilder()
|
||||
}
|
||||
|
||||
public define(): WithOrdinal<string, Base64BitFlagSet>
|
||||
public define(alias: string): WithOrdinalOrCompose<string, Base64BitFlagSet>
|
||||
public define(alias?: string): WithOrdinalOrCompose<string, Base64BitFlagSet> {
|
||||
this._underlying.define(alias)
|
||||
return this
|
||||
}
|
||||
|
||||
public compose(...flags: string[]): DefineWithOrdinal<string, Base64BitFlagSet> {
|
||||
this._underlying.compose(flags)
|
||||
return this
|
||||
}
|
||||
|
||||
public withValue(): never {
|
||||
throw new InvalidOperationError('withValue')
|
||||
}
|
||||
|
||||
public withOrdinal(
|
||||
ordinal: number,
|
||||
): RequireParentsOrDefineWithOrdinal<string, Base64BitFlagSet> {
|
||||
this._underlying.withValue(ordinal)
|
||||
return this
|
||||
}
|
||||
|
||||
public requires(...flags: string[]): DefineWithOrdinal<string, Base64BitFlagSet> {
|
||||
this._underlying.requires(flags)
|
||||
return this
|
||||
}
|
||||
|
||||
public getResult(): Base64BitFlagSet {
|
||||
const graph = this._underlying.finish()
|
||||
const factory = new Base64BitFlagDefinitionFactory()
|
||||
return new Base64BitFlagSet(graph.intoDictionary(factory))
|
||||
}
|
||||
}
|
||||
|
||||
export function createBase64BitFlagSet(declarations: FlagWithOrdinal[]): Base64BitFlagSet
|
||||
export function createBase64BitFlagSet<D extends string>(
|
||||
declarations: Record<D, NamedFlagWithOrdinal>,
|
||||
): Base64BitFlagSet & Record<D, FlagDefinition<number>>
|
||||
export function createBase64BitFlagSet(declarations: ListOfFlagsWithOrdinal): Base64BitFlagSet {
|
||||
const builder = new Base64BitFlagSetBuilder()
|
||||
applyDeclarations(declarations, builder)
|
||||
return builder.getResult()
|
||||
}
|
||||
@@ -1,15 +1,4 @@
|
||||
import type { DefineWithOrdinal, DefineWithValue, DefineWithValueOrOrdinal } from './syntax'
|
||||
import { FlagsGraph, refByAlias } from '~/definitions'
|
||||
|
||||
// Generic helper functions
|
||||
|
||||
/**
|
||||
* Copies the record keys into the 'as' property of the values and return an
|
||||
* array containing those values.
|
||||
*/
|
||||
function toDeclarationArray<D>(record: Record<string, D>): (D & { as: string })[] {
|
||||
return Object.keys(record).map((key) => ({ ...record[key], as: key }))
|
||||
}
|
||||
import type { DefineWithValueOrOrdinal } from './syntax'
|
||||
|
||||
// Declarations for builders that supports only definitions by value
|
||||
|
||||
@@ -23,17 +12,13 @@ export type ListOfFlagsWithValue<F> = FlagWithValue<F>[] | Record<string, NamedF
|
||||
|
||||
// Declarations for builders that supports only definitions by ordinal
|
||||
|
||||
export type FlagWithOrdinal<F> =
|
||||
export type FlagWithOrdinal =
|
||||
| { ordinal: number; as?: string; requires?: string[] }
|
||||
| { compose: string[]; as: string }
|
||||
|
||||
export type NamedFlagWithOrdinal<F> =
|
||||
| { ordinal: number; requires?: string[] }
|
||||
| { compose: string[] }
|
||||
export type NamedFlagWithOrdinal = { ordinal: number; requires?: string[] } | { compose: string[] }
|
||||
|
||||
export type ListOfFlagsWithOrdinal<F> =
|
||||
| FlagWithOrdinal<F>[]
|
||||
| Record<string, NamedFlagWithOrdinal<F>>
|
||||
export type ListOfFlagsWithOrdinal = FlagWithOrdinal[] | Record<string, NamedFlagWithOrdinal>
|
||||
|
||||
// Declarations for builders that supports definitions by value and ordinal
|
||||
|
||||
@@ -51,27 +36,43 @@ export type ListOfFlagsWithValueOrOrdinal<F> =
|
||||
| FlagWithValueOrOrdinal<F>[]
|
||||
| Record<string, NamedFlagWithValueOrOrdinal<F>>
|
||||
|
||||
// Helper function
|
||||
|
||||
export interface AnyBuilder<F> {
|
||||
define(alias: string | undefined): unknown
|
||||
compose(...flags: string[]): unknown
|
||||
withOrdinal(ordinal: number): unknown
|
||||
withValue(value: F): unknown
|
||||
requires(...flags: string[]): unknown
|
||||
}
|
||||
|
||||
export function applyDeclarations<F>(
|
||||
declarations: ListOfFlagsWithValueOrOrdinal<F>,
|
||||
builder: DefineWithValueOrOrdinal<F, unknown, unknown>,
|
||||
builder: AnyBuilder<F>,
|
||||
) {
|
||||
const declarationsArray = Array.isArray(declarations)
|
||||
? declarations
|
||||
: toDeclarationArray(declarations)
|
||||
let declarationsArray: FlagWithValueOrOrdinal<F>[]
|
||||
if (Array.isArray(declarations)) {
|
||||
declarationsArray = declarations
|
||||
} else {
|
||||
declarationsArray = Object.keys(declarations).map((key): FlagWithValueOrOrdinal<F> => {
|
||||
return { ...declarations[key], as: key }
|
||||
})
|
||||
}
|
||||
|
||||
for (const declaration of declarationsArray) {
|
||||
builder.define(declaration.as)
|
||||
if ('compose' in declaration) {
|
||||
builder.define(declaration.as).compose(...declaration.compose)
|
||||
} else if ('ordinal' in declaration) {
|
||||
builder
|
||||
.define(declaration.as!) // see note above
|
||||
.withOrdinal(declaration.ordinal)
|
||||
.requires(...(declaration.requires ?? []))
|
||||
builder.compose(...declaration.compose)
|
||||
} else {
|
||||
builder
|
||||
.define(declaration.as!) // see note above
|
||||
.withValue(declaration.value)
|
||||
.requires(...(declaration.requires ?? []))
|
||||
if ('ordinal' in declaration) {
|
||||
builder.withOrdinal(declaration.ordinal)
|
||||
} else {
|
||||
builder.withValue(declaration.value)
|
||||
}
|
||||
|
||||
if (declaration.requires !== undefined) {
|
||||
builder.requires(...declaration.requires)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export { ArrayFlagSetBuilder, createArrayFlagSet } from './array'
|
||||
export { Base64BitFlagSetBuilder, createBase64BitFlagSet } from './base64'
|
||||
export { BigBitFlagSetBuilder, createBigBitFlagSet } from './bigint'
|
||||
export { CollectionFlagSetBuilder, createCollectionFlagSet } from './collection'
|
||||
export { BitFlagSetBuilder, createBitFlagSet } from './number'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { FlagsDictionary, FlagSet } from '~'
|
||||
import type { FlagSet } from '~'
|
||||
|
||||
export interface Root<F, S, R = FlagSet<F, S>> {
|
||||
/**
|
||||
@@ -48,42 +48,42 @@ export interface RequireParentsOrDefineWithValue<F, S, R> extends DefineWithValu
|
||||
|
||||
// Syntax for builders that supports only definitions by ordinal
|
||||
|
||||
export interface DefineWithOrdinal<F, S, R = FlagSet<F, S>> extends Root<F, S, R> {
|
||||
export interface DefineWithOrdinal<S, R = FlagSet<number, S>> extends Root<number, S, R> {
|
||||
/**
|
||||
* Define an anonymous flag.
|
||||
*/
|
||||
define(): WithOrdinal<F, S, R>
|
||||
define(): WithOrdinal<S, R>
|
||||
|
||||
/**
|
||||
* Define a named flag.
|
||||
* @param alias The name of the flag.
|
||||
*/
|
||||
define(alias: string): WithOrdinalOrCompose<F, S, R>
|
||||
define(alias: string): WithOrdinalOrCompose<S, R>
|
||||
}
|
||||
|
||||
export interface WithOrdinal<F, S, R> {
|
||||
export interface WithOrdinal<S, R> {
|
||||
/**
|
||||
* Set the value of this flag.
|
||||
* @param ordinal The number of the flag (starting at 1). A unique value
|
||||
* will be assigned based on this number.
|
||||
*/
|
||||
withOrdinal(ordinal: number): RequireParentsOrDefineWithOrdinal<F, S, R>
|
||||
withOrdinal(ordinal: number): RequireParentsOrDefineWithOrdinal<S, R>
|
||||
}
|
||||
|
||||
export interface WithOrdinalOrCompose<F, S, R> extends WithOrdinal<F, S, R> {
|
||||
export interface WithOrdinalOrCompose<S, R> extends WithOrdinal<S, R> {
|
||||
/**
|
||||
* Define this flag as a composed flag.
|
||||
* @param flags The name of the flags in the group.
|
||||
*/
|
||||
compose(...flags: string[]): DefineWithOrdinal<F, S, R>
|
||||
compose(...flags: string[]): DefineWithOrdinal<S, R>
|
||||
}
|
||||
|
||||
export interface RequireParentsOrDefineWithOrdinal<F, S, R> extends DefineWithOrdinal<F, S, R> {
|
||||
export interface RequireParentsOrDefineWithOrdinal<S, R> extends DefineWithOrdinal<S, R> {
|
||||
/**
|
||||
* Set the parents of this flag.
|
||||
* @param flags The names of the parent flags.
|
||||
*/
|
||||
requires(...flags: string[]): DefineWithOrdinal<F, S, R>
|
||||
requires(...flags: string[]): DefineWithOrdinal<S, R>
|
||||
}
|
||||
|
||||
// Syntax for builders that supports definitions by value and ordinal
|
||||
@@ -124,11 +124,8 @@ export interface WithValueOrOrdinalOrCompose<F, S, R> extends WithValueOrOrdinal
|
||||
compose(...flags: string[]): DefineWithValueOrOrdinal<F, S, R>
|
||||
}
|
||||
|
||||
export interface RequireParentsOrDefineWithValueOrOrdinal<F, S, R> extends DefineWithValueOrOrdinal<
|
||||
F,
|
||||
S,
|
||||
R
|
||||
> {
|
||||
export interface RequireParentsOrDefineWithValueOrOrdinal<F, S, R>
|
||||
extends DefineWithValueOrOrdinal<F, S, R> {
|
||||
/**
|
||||
* Set the parents of this flag.
|
||||
* @param flags The names of the parent flags.
|
||||
|
||||
118
node/src/definitions/base64.ts
Normal file
118
node/src/definitions/base64.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { decodeB64Byte, encodeB64Byte, normaliseB64String, ZERO_STRING } from '~/base64'
|
||||
|
||||
import { FlagDefinition, FlagDefinitionFactory, PartialFlagDefinition } from '.'
|
||||
|
||||
interface PrecomputedValues {
|
||||
base: string
|
||||
additive: string
|
||||
subtractive: string
|
||||
}
|
||||
|
||||
function union(first: string, second: string): string {
|
||||
const [shorter, longer] = first.length < second.length ? [first, second] : [second, first]
|
||||
let result = ''
|
||||
for (let i = 0; i < longer.length; i++) {
|
||||
if (i < shorter.length) {
|
||||
const value = decodeB64Byte(shorter[i]) | decodeB64Byte(longer[i])
|
||||
result += encodeB64Byte(value)
|
||||
} else {
|
||||
result += longer[i]
|
||||
}
|
||||
}
|
||||
return normaliseB64String(result)
|
||||
}
|
||||
|
||||
export class Base64BitFlagDefinition implements FlagDefinition<string> {
|
||||
private readonly _baseValue: string
|
||||
private readonly _additiveValue: string
|
||||
private readonly _subtractiveValue: string
|
||||
private readonly _alias: string | undefined
|
||||
|
||||
public constructor(precomputedValues: PrecomputedValues, alias: string | undefined) {
|
||||
this._baseValue = precomputedValues.base
|
||||
this._additiveValue = precomputedValues.additive
|
||||
this._subtractiveValue = precomputedValues.subtractive
|
||||
this._alias = alias
|
||||
}
|
||||
|
||||
public get alias(): string | undefined {
|
||||
return this._alias
|
||||
}
|
||||
|
||||
public get values(): string {
|
||||
return this._baseValue
|
||||
}
|
||||
|
||||
public isIn(set: string): boolean {
|
||||
let result = set.length >= this._additiveValue.length
|
||||
for (let i = 0; i < this._additiveValue.length && result; i++) {
|
||||
const value = decodeB64Byte(this._additiveValue[i])
|
||||
result = (decodeB64Byte(set[i]) & value) === value
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public addTo(set: string): string {
|
||||
return union(set, this._additiveValue)
|
||||
}
|
||||
|
||||
public removeFrom(set: string): string {
|
||||
return set
|
||||
}
|
||||
}
|
||||
|
||||
export class Base64BitFlagDefinitionFactory implements FlagDefinitionFactory<number, string> {
|
||||
public makeDefinitions(
|
||||
sortedPartialDefinitions: PartialFlagDefinition<number>[],
|
||||
results: Map<PartialFlagDefinition<number>, FlagDefinition<string>>,
|
||||
): void {
|
||||
const precomputedValues = new Map<PartialFlagDefinition<number>, PrecomputedValues>()
|
||||
|
||||
for (let i = 0; i < sortedPartialDefinitions.length; i++) {
|
||||
const pfd = sortedPartialDefinitions[i]
|
||||
|
||||
let stringValue = ZERO_STRING
|
||||
if (pfd.value !== undefined) {
|
||||
if (pfd.value < 1) {
|
||||
throw new RangeError('Indices should be greater than or equal to 1.')
|
||||
}
|
||||
const indexFromZero = pfd.value - 1
|
||||
const leadingBytes = ZERO_STRING.repeat(indexFromZero / 6)
|
||||
const bigEnd = encodeB64Byte(1 << indexFromZero % 6)
|
||||
stringValue = leadingBytes + bigEnd
|
||||
}
|
||||
|
||||
const values: PrecomputedValues = {
|
||||
base: stringValue,
|
||||
additive: stringValue,
|
||||
subtractive: stringValue,
|
||||
}
|
||||
|
||||
for (const parentPfd of pfd.parents) {
|
||||
const parentValues = precomputedValues.get(parentPfd)
|
||||
if (parentValues !== undefined) {
|
||||
values.additive = union(values.additive, parentValues.additive)
|
||||
if (pfd.value === undefined) {
|
||||
values.base = union(values.base, parentValues.base)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
precomputedValues.set(pfd, values)
|
||||
}
|
||||
|
||||
for (let i = sortedPartialDefinitions.length - 1; i >= 0; i--) {
|
||||
const pfd = sortedPartialDefinitions[i]
|
||||
const values = precomputedValues.get(pfd)!
|
||||
|
||||
for (const childPfd of pfd.children) {
|
||||
const childValues = precomputedValues.get(childPfd)
|
||||
if (childValues !== undefined) {
|
||||
values.subtractive = union(values.subtractive, childValues.subtractive)
|
||||
}
|
||||
}
|
||||
|
||||
results.set(pfd, new Base64BitFlagDefinition(values, pfd.alias))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FlagDefinition, FlagsDictionary, printFlagValue } from '~/definitions'
|
||||
import { FlagDefinition, FlagsDictionary } from '~/definitions'
|
||||
import { InternalError } from '~/errors'
|
||||
|
||||
export interface PartialFlagInit<F> {
|
||||
@@ -14,7 +14,7 @@ export function refByAlias(refs: string[]): PartialFlagInit<never>[] {
|
||||
export interface FlagDefinitionFactory<F, S> {
|
||||
makeDefinitions(
|
||||
sortedPartialDefinitions: PartialFlagDefinition<F>[],
|
||||
results: Map<PartialFlagDefinition<F>, FlagDefinition<F, S>>,
|
||||
results: Map<PartialFlagDefinition<F>, FlagDefinition<S>>,
|
||||
): void
|
||||
}
|
||||
|
||||
@@ -130,23 +130,27 @@ export class FlagsGraph<F> {
|
||||
public intoDictionary<S>(factory: FlagDefinitionFactory<F, S>): FlagsDictionary<F, S> {
|
||||
const sortedPartialDefinitions = this.sortedDefinitions()
|
||||
|
||||
const definitions = new Map<PartialFlagDefinition<F>, FlagDefinition<F, S>>()
|
||||
const definitions = new Map<PartialFlagDefinition<F>, FlagDefinition<S>>()
|
||||
factory.makeDefinitions(sortedPartialDefinitions, definitions)
|
||||
|
||||
const aliasToDefinition = new Map<string, FlagDefinition<F, S>>()
|
||||
const aliasToDefinition = new Map<string, FlagDefinition<S>>()
|
||||
for (const [alias, pfd] of this._aliasToDefinition.entries()) {
|
||||
const definition = definitions.get(pfd)
|
||||
if (definition === undefined) {
|
||||
throw new InternalError(`factory didn't provide any definition for ${pfd}`)
|
||||
throw new InternalError(
|
||||
`factory didn't provide any definition for ${pfd.toString()}`,
|
||||
)
|
||||
}
|
||||
aliasToDefinition.set(alias, definition)
|
||||
}
|
||||
|
||||
const valueToDefinition = new Map<F, FlagDefinition<F, S>>()
|
||||
const valueToDefinition = new Map<F, FlagDefinition<S>>()
|
||||
for (const [value, pfd] of this._valueToDefinition.entries()) {
|
||||
const definition = definitions.get(pfd)
|
||||
if (definition === undefined) {
|
||||
throw new InternalError(`factory didn't provide any definition for ${pfd}`)
|
||||
throw new InternalError(
|
||||
`factory didn't provide any definition for ${pfd.toString()}`,
|
||||
)
|
||||
}
|
||||
valueToDefinition.set(value, definition)
|
||||
}
|
||||
@@ -156,22 +160,22 @@ export class FlagsGraph<F> {
|
||||
}
|
||||
|
||||
class BiMapFlagsDictionary<F, S> implements FlagsDictionary<F, S> {
|
||||
private readonly _aliasToDefinition: Map<string, FlagDefinition<F, S>>
|
||||
private readonly _valueToDefinition: Map<F, FlagDefinition<F, S>>
|
||||
private readonly _aliasToDefinition: Map<string, FlagDefinition<S>>
|
||||
private readonly _valueToDefinition: Map<F, FlagDefinition<S>>
|
||||
|
||||
public constructor(
|
||||
aliasToDefinition: Map<string, FlagDefinition<F, S>>,
|
||||
valueToDefinition: Map<F, FlagDefinition<F, S>>,
|
||||
aliasToDefinition: Map<string, FlagDefinition<S>>,
|
||||
valueToDefinition: Map<F, FlagDefinition<S>>,
|
||||
) {
|
||||
this._aliasToDefinition = aliasToDefinition
|
||||
this._valueToDefinition = valueToDefinition
|
||||
}
|
||||
|
||||
public findByAlias(alias: string): FlagDefinition<F, S> | undefined {
|
||||
public findByAlias(alias: string): FlagDefinition<S> | undefined {
|
||||
return this._aliasToDefinition.get(alias)
|
||||
}
|
||||
|
||||
public findByValue(value: F): FlagDefinition<F, S> | undefined {
|
||||
public findByValue(value: F): FlagDefinition<S> | undefined {
|
||||
return this._valueToDefinition.get(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export { Base64BitFlagDefinition, Base64BitFlagDefinitionFactory } from './base64'
|
||||
export { BigBitFlagDefinition, BigBitFlagDefinitionFactory } from './bigint'
|
||||
export { FlagDefinition, FlagsDictionary, printFlagValue, valueToString } from './dictionary'
|
||||
export {
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
import { decodeB64Byte, encodeB64Byte, normaliseB64String, ZERO_STRING } from '~/base64'
|
||||
import { FlagDefinition, FlagsDictionary } from '~/definitions'
|
||||
import { Base64BitflagIterator, EnumerateFlags, useIterator } from '~/enumeration'
|
||||
|
||||
import type { FlagSet } from '.'
|
||||
import { decodeB64Byte, encodeB64Byte, normaliseB64String, ZERO_STRING } from '../base64'
|
||||
import { Base64BitflagIterator, EnumerateFlags, useIterator } from '../enumeration'
|
||||
|
||||
function toBase64(value: number): string {
|
||||
if (value < 1) {
|
||||
throw new RangeError('Indices should be greater than or equal to 1.')
|
||||
}
|
||||
const indexFromZero = value - 1
|
||||
const leadingBytes = ZERO_STRING.repeat(indexFromZero / 6)
|
||||
const bigEnd = encodeB64Byte(1 << indexFromZero % 6)
|
||||
return leadingBytes + bigEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides flags that are stored in strings using a little-endian base 64
|
||||
@@ -11,22 +23,31 @@ import { Base64BitflagIterator, EnumerateFlags, useIterator } from '../enumerati
|
||||
* instead if you need the data to be easily understandable by other systems.
|
||||
*/
|
||||
export class Base64BitFlagSet implements FlagSet<number, string> {
|
||||
/*protected wrapValue(value: number): string {
|
||||
if (value < 1) {
|
||||
throw new RangeError(
|
||||
'Indices should be greater than or equal to 1.'
|
||||
)
|
||||
private readonly _dictionary: FlagsDictionary<number, string>
|
||||
|
||||
public constructor(dictionary: FlagsDictionary<number, string>) {
|
||||
this._dictionary = dictionary
|
||||
}
|
||||
const indexFromZero = value - 1
|
||||
const leadingBytes = ZERO_STRING.repeat(indexFromZero / 6)
|
||||
const bigEnd = encodeByte(1 << indexFromZero % 6)
|
||||
return leadingBytes + bigEnd
|
||||
}*/
|
||||
|
||||
public none(): string {
|
||||
return ''
|
||||
}
|
||||
|
||||
public of(...values: number[]): string {
|
||||
return normaliseB64String(
|
||||
values.reduce((set, value) => this.union(set, toBase64(value)), ZERO_STRING),
|
||||
)
|
||||
}
|
||||
|
||||
public named(...aliases: string[]): string {
|
||||
return normaliseB64String(
|
||||
aliases.reduce(
|
||||
(set, alias) => this.union(set, this.getFlag(alias)?.values ?? ZERO_STRING),
|
||||
ZERO_STRING,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
public union(first: string, second: string): string {
|
||||
let result = ''
|
||||
|
||||
@@ -107,19 +128,41 @@ export class Base64BitFlagSet implements FlagSet<number, string> {
|
||||
return result
|
||||
}
|
||||
|
||||
public hasAny(flags: string, required: string): boolean {
|
||||
return this.minimum(this.intersection(flags, required)) !== ''
|
||||
}
|
||||
|
||||
public hasAll(flags: string, required: string): boolean {
|
||||
return this.isSuperset(flags, this.maximum(required))
|
||||
}
|
||||
|
||||
public enumerate(flags: string): EnumerateFlags<number> {
|
||||
return useIterator(flags, Base64BitflagIterator)
|
||||
}
|
||||
|
||||
maximum(flags: string): string {
|
||||
throw new Error('not implemented')
|
||||
public maximum(flags: string): string {
|
||||
let result = ZERO_STRING
|
||||
for (const value of this.enumerate(flags)) {
|
||||
const definition = this._dictionary.findByValue(value)
|
||||
if (definition !== undefined) {
|
||||
result = definition.addTo(result)
|
||||
}
|
||||
}
|
||||
return normaliseB64String(result)
|
||||
}
|
||||
|
||||
minimum(flags: string): string {
|
||||
throw new Error('not implemented')
|
||||
public minimum(flags: string): string {
|
||||
let result = ZERO_STRING
|
||||
for (const value of this.enumerate(flags)) {
|
||||
const definition = this._dictionary.findByValue(value)
|
||||
if (definition !== undefined && definition.isIn(flags)) {
|
||||
result = definition.addTo(result)
|
||||
}
|
||||
}
|
||||
return normaliseB64String(result)
|
||||
}
|
||||
|
||||
public getFlag(alias: string): FlagDefinition<number, number> | undefined {
|
||||
return this._dictionary.lookUp(alias)
|
||||
public getFlag(alias: string): FlagDefinition<string> | undefined {
|
||||
return this._dictionary.findByAlias(alias)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { EnumerateFlags } from '../enumeration'
|
||||
import { FlagDefinition } from '../definitions'
|
||||
import { FlagDefinition } from '~/definitions'
|
||||
import { EnumerateFlags } from '~/enumeration'
|
||||
|
||||
/**
|
||||
* Represents a group of flags of type `F` and the relationships between
|
||||
@@ -128,11 +128,11 @@ export interface FlagSet<F, S> {
|
||||
* @returns The corresponding definition, or `undefined` if there is no flag
|
||||
* with this alias.
|
||||
*/
|
||||
getFlag(alias: string): FlagDefinition<F, S> | undefined
|
||||
getFlag(alias: string): FlagDefinition<S> | undefined
|
||||
}
|
||||
|
||||
export { ArrayFlagSet } from './array'
|
||||
export { Base64BitFlagSet } from './base64'
|
||||
export { BigBitFlagSet } from './bigint'
|
||||
export { CollectionFlagSet } from './collection'
|
||||
export { BitFlagSet, BitFlags } from './number'
|
||||
export { BitFlags, BitFlagSet } from './number'
|
||||
|
||||
@@ -3,6 +3,7 @@ export {
|
||||
createBigBitFlagSet,
|
||||
createBitFlagSet,
|
||||
createCollectionFlagSet,
|
||||
createBase64BitFlagSet,
|
||||
} from './builders'
|
||||
export { FlagDefinition } from './definitions'
|
||||
export { InvalidBitFlagValueError } from './errors'
|
||||
|
||||
@@ -1,15 +1,40 @@
|
||||
import { Base64BitFlagSet } from '~'
|
||||
import { describe, expect, test } from 'vitest'
|
||||
|
||||
import { Base64BitFlagSet, createBase64BitFlagSet } from '~'
|
||||
|
||||
describe(Base64BitFlagSet, () => {
|
||||
test('none', () => {
|
||||
const flags = new Base64BitFlagSet()
|
||||
const flags = createBase64BitFlagSet([])
|
||||
|
||||
expect(flags.none()).toEqual('')
|
||||
})
|
||||
|
||||
test('of', () => {
|
||||
const flags = createBase64BitFlagSet([])
|
||||
|
||||
expect(flags.of()).toEqual('')
|
||||
expect(flags.of(1)).toEqual('B')
|
||||
expect(flags.of(2, 3)).toEqual('G')
|
||||
})
|
||||
|
||||
test('named', () => {
|
||||
const flags = createBase64BitFlagSet([
|
||||
{ ordinal: 1, as: 'A' },
|
||||
{ ordinal: 2, as: 'B' },
|
||||
{ ordinal: 3, as: 'C' },
|
||||
{ ordinal: 4, as: 'D' },
|
||||
{ compose: ['A', 'B'], as: 'AB' },
|
||||
{ compose: ['A', 'C'], as: 'AC' },
|
||||
])
|
||||
|
||||
expect(flags.named()).toEqual('')
|
||||
expect(flags.named('A')).toEqual('B')
|
||||
expect(flags.named('AB', 'D')).toEqual('L')
|
||||
expect(flags.named('AB', 'AC', 'B')).toEqual('H')
|
||||
})
|
||||
|
||||
test('union', () => {
|
||||
const flags = new Base64BitFlagSet()
|
||||
const flags = createBase64BitFlagSet([])
|
||||
|
||||
expect(flags.union('', '')).toEqual('')
|
||||
expect(flags.union('A', 'A')).toEqual('')
|
||||
@@ -20,7 +45,7 @@ describe(Base64BitFlagSet, () => {
|
||||
})
|
||||
|
||||
test('difference', () => {
|
||||
const flags = new Base64BitFlagSet()
|
||||
const flags = createBase64BitFlagSet([])
|
||||
|
||||
expect(flags.difference('', '')).toEqual('')
|
||||
expect(flags.difference('A', 'A')).toEqual('')
|
||||
@@ -31,7 +56,7 @@ describe(Base64BitFlagSet, () => {
|
||||
})
|
||||
|
||||
test('intersection', () => {
|
||||
const flags = new Base64BitFlagSet()
|
||||
const flags = createBase64BitFlagSet([])
|
||||
|
||||
expect(flags.intersection('', '')).toEqual('')
|
||||
expect(flags.intersection('A', 'A')).toEqual('')
|
||||
@@ -43,7 +68,7 @@ describe(Base64BitFlagSet, () => {
|
||||
})
|
||||
|
||||
test('isSuperset', () => {
|
||||
const flags = new Base64BitFlagSet()
|
||||
const flags = createBase64BitFlagSet([])
|
||||
|
||||
expect(flags.isSuperset('A', 'A')).toBe(true)
|
||||
expect(flags.isSuperset('D', 'A')).toBe(true)
|
||||
@@ -53,8 +78,40 @@ describe(Base64BitFlagSet, () => {
|
||||
expect(flags.isSuperset('I', 'E')).toBe(false)
|
||||
})
|
||||
|
||||
test('hasAny', () => {
|
||||
const flags = createBase64BitFlagSet([
|
||||
{ ordinal: 1, as: 'A' },
|
||||
{ ordinal: 2, as: 'B', requires: ['A'] },
|
||||
{ ordinal: 3, as: 'C', requires: ['A'] },
|
||||
{ ordinal: 4, as: 'D', requires: ['B', 'C'] },
|
||||
])
|
||||
|
||||
expect(flags.hasAny('P', 'A')).toBe(false)
|
||||
expect(flags.hasAny('A', 'A')).toBe(false)
|
||||
expect(flags.hasAny('H', 'B')).toBe(true)
|
||||
expect(flags.hasAny('B', 'H')).toBe(true)
|
||||
expect(flags.hasAny('F', 'M')).toBe(false)
|
||||
expect(flags.hasAny('C', 'C')).toBe(false)
|
||||
})
|
||||
|
||||
test('hasAll', () => {
|
||||
const flags = createBase64BitFlagSet([
|
||||
{ ordinal: 1, as: 'A' },
|
||||
{ ordinal: 2, as: 'B', requires: ['A'] },
|
||||
{ ordinal: 3, as: 'C', requires: ['A'] },
|
||||
{ ordinal: 4, as: 'D', requires: ['B', 'C'] },
|
||||
])
|
||||
|
||||
expect(flags.hasAll('P', 'A')).toBe(true)
|
||||
expect(flags.hasAll('A', 'A')).toBe(true)
|
||||
expect(flags.hasAll('H', 'C')).toBe(true)
|
||||
expect(flags.hasAll('G', 'C')).toBe(false)
|
||||
expect(flags.hasAll('B', 'H')).toBe(false)
|
||||
expect(flags.hasAll('F', 'M')).toBe(false)
|
||||
})
|
||||
|
||||
test('enumerate', () => {
|
||||
const flags = new Base64BitFlagSet()
|
||||
const flags = createBase64BitFlagSet([])
|
||||
|
||||
expect([...flags.enumerate('A')]).toEqual([])
|
||||
expect([...flags.enumerate('B')]).toEqual([1])
|
||||
@@ -64,4 +121,36 @@ describe(Base64BitFlagSet, () => {
|
||||
expect([...flags.enumerate('kB')]).toEqual([3, 6, 7])
|
||||
expect([...flags.enumerate('AAB')]).toEqual([13])
|
||||
})
|
||||
|
||||
test('maximum', () => {
|
||||
const flags = createBase64BitFlagSet([
|
||||
{ ordinal: 1, as: 'A' },
|
||||
{ ordinal: 2, as: 'B', requires: ['A'] },
|
||||
{ ordinal: 3, as: 'C', requires: ['A'] },
|
||||
{ ordinal: 4, as: 'D', requires: ['B', 'C'] },
|
||||
])
|
||||
|
||||
expect(flags.maximum('A')).toEqual('')
|
||||
expect(flags.maximum('B')).toEqual('B')
|
||||
expect(flags.maximum('C')).toEqual('D')
|
||||
expect(flags.maximum('D')).toEqual('D')
|
||||
expect(flags.maximum('E')).toEqual('F')
|
||||
expect(flags.maximum('I')).toEqual('P')
|
||||
})
|
||||
|
||||
test('minimum', () => {
|
||||
const flags = createBase64BitFlagSet([
|
||||
{ ordinal: 1, as: 'A' },
|
||||
{ ordinal: 2, as: 'B', requires: ['A'] },
|
||||
{ ordinal: 3, as: 'C', requires: ['A'] },
|
||||
{ ordinal: 4, as: 'D', requires: ['B', 'C'] },
|
||||
])
|
||||
|
||||
expect(flags.minimum('A')).toEqual('')
|
||||
expect(flags.minimum('B')).toEqual('B')
|
||||
expect(flags.minimum('C')).toEqual('')
|
||||
expect(flags.minimum('D')).toEqual('D')
|
||||
expect(flags.minimum('E')).toEqual('')
|
||||
expect(flags.minimum('N')).toEqual('F')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user