Skip to content

Commit

Permalink
feat: Composed KeyLabelSet parse and serialization
Browse files Browse the repository at this point in the history
- types KeyLabelSetAll, KeyLabelSetNone, KeyLabelSetSome, KeyLabelSetAllExceptSome
- types ComposedKeySetSerialized and ComposedKeyLabelSetSerialized, as arrays of KeySetSerialized and KeyLabelSetSerialized respectively
- constant KEY_SET_TYPES with the values of KeySetTypes
- isKeySetType() function
- isKeyLabelSet() function
- isKeyLabelSetSerialized() function
- isComposedKeySetSerialized() function
- serializeComposedKeySet and parseComposedKeySet functions
- serializeKeyLabelSet and parseKeyLabelSet functions
- composedKeySet.serialized() method
  • Loading branch information
eturino committed Jan 8, 2025
1 parent 8f12360 commit 8091558
Show file tree
Hide file tree
Showing 5 changed files with 341 additions and 8 deletions.
178 changes: 178 additions & 0 deletions src/lib/__tests__/serialize.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import { describe, expect, it } from "vitest";

import {
type ComposedKeySetSerialized,
type IKeyLabel,
InvalidKeySetError,
type KeyLabelSetAll,
type KeyLabelSetAllExceptSome,
type KeyLabelSetAllExceptSomeSerialized,
type KeyLabelSetAllSerialized,
type KeyLabelSetNone,
type KeyLabelSetNoneSerialized,
type KeyLabelSetSome,
type KeyLabelSetSomeSerialized,
type KeySet,
KeySetAll,
KeySetAllExceptSome,
Expand All @@ -15,13 +25,20 @@ import {
KeySetTypes,
all,
allExceptSome,
composedKeySetFrom,
isComposedKeySetSerialized,
isKeyLabelSetSerialized,
isKeySetAllExceptSomeSerialized,
isKeySetAllSerialized,
isKeySetNoneSerialized,
isKeySetSerialized,
isKeySetSomeSerialized,
none,
parseComposedKeySet,
parseKeyLabelSet,
parseKeySet,
serializeComposedKeySet,
serializeKeyLabelSet,
serializeKeySet,
some,
} from "../..";
Expand Down Expand Up @@ -474,4 +491,165 @@ describe("serialize KeySet", () => {
expect(JSON.stringify(keySet)).toEqual(JSON.stringify(expected));
});
});

describe("KeyLabelSet", () => {
const allKS: KeyLabelSetAll<string> = all<IKeyLabel<string>>();
const allSerialized: KeyLabelSetAllSerialized<string> = { type: KeySetTypes.all };

const noneKS: KeyLabelSetNone<string> = none<IKeyLabel<string>>();
const noneSerialized: KeyLabelSetNoneSerialized<string> = { type: KeySetTypes.none };

const aesKS: KeyLabelSetAllExceptSome = allExceptSome([
{ key: "a", label: "A" },
{ key: "b", label: "B" },
]);
const aesSerialized: KeyLabelSetAllExceptSomeSerialized = {
type: KeySetTypes.allExceptSome,
elements: [
{ key: "a", label: "A" },
{ key: "b", label: "B" },
],
};
const someKS: KeyLabelSetSome = some([
{ key: "a", label: "A" },
{ key: "b", label: "B" },
]);
const someSerialized: KeyLabelSetSomeSerialized = {
type: KeySetTypes.some,
elements: [
{ key: "a", label: "A" },
{ key: "b", label: "B" },
],
};

const aesKeySetSerialized = {
type: KeySetTypes.allExceptSome,
elements: [1, 2],
};

const someKeySetSerialized = {
type: KeySetTypes.some,
elements: [1, 2],
};

describe("isKeyLabelSetSerialized()", () => {
it("isKeyLabelSetSerialized(keyLabelSetSerialized) -> true", () => {
expect(isKeyLabelSetSerialized(aesSerialized)).toBeTruthy();
expect(isKeyLabelSetSerialized(someSerialized)).toBeTruthy();
expect(isKeyLabelSetSerialized(allSerialized)).toBeTruthy();
expect(isKeyLabelSetSerialized(noneSerialized)).toBeTruthy();
});
it("isKeyLabelSetSerialized(normalKeySetSerialized some or AES) -> false", () => {
expect(isKeyLabelSetSerialized(aesKeySetSerialized)).toBeFalsy();
expect(isKeyLabelSetSerialized(someKeySetSerialized)).toBeFalsy();
});
it("isKeyLabelSetSerialized(keySet) -> false", () => {
expect(isKeyLabelSetSerialized(allKS)).toBeFalsy();
expect(isKeyLabelSetSerialized(noneKS)).toBeFalsy();
expect(isKeyLabelSetSerialized(someKS)).toBeFalsy();
expect(isKeyLabelSetSerialized(aesKS)).toBeFalsy();
});
});

describe("serializeKeyLabelSet()", () => {
it("serializeKeyLabelSet(keyLabelSetSerialized) -> return same", () => {
expect(serializeKeyLabelSet(aesSerialized)).toBe(aesSerialized);
expect(serializeKeyLabelSet(someSerialized)).toBe(someSerialized);
expect(serializeKeyLabelSet(allSerialized)).toBe(allSerialized);
expect(serializeKeyLabelSet(noneSerialized)).toBe(noneSerialized);
});
it("serializeKeyLabelSet(ks) -> serialize", () => {
expect(serializeKeyLabelSet(aesKS)).toEqual(aesSerialized);
expect(serializeKeyLabelSet(someKS)).toEqual(someSerialized);
expect(serializeKeyLabelSet(allKS)).toEqual(allSerialized);
expect(serializeKeyLabelSet(noneKS)).toEqual(noneSerialized);
});
it("serializeKeyLabelSet(invalid) -> throws", () => {
expect(() => serializeKeyLabelSet("whatever" as unknown as KeySet)).toThrow();
});
});

describe("parseKeyLabelSet()", () => {
it("parseKeyLabelSet(keyLabelSetSerialized) -> return same", () => {
expect(parseKeyLabelSet(aesSerialized)).toEqual(aesKS);
expect(parseKeyLabelSet(someSerialized)).toEqual(someKS);
expect(parseKeyLabelSet(allSerialized)).toEqual(allKS);
expect(parseKeyLabelSet(noneSerialized)).toEqual(noneKS);
});
it("parseKeyLabelSet(ks) -> parse", () => {
expect(parseKeyLabelSet(aesKS)).toBe(aesKS);
expect(parseKeyLabelSet(someKS)).toBe(someKS);
expect(parseKeyLabelSet(allKS)).toBe(allKS);
expect(parseKeyLabelSet(noneKS)).toBe(noneKS);
});
it("parseKeyLabelSet(invalid) -> throws", () => {
expect(() => parseKeyLabelSet("whatever" as unknown as KeySet)).toThrow();
});
});
});

describe("composedKeySet", () => {
const keySet1 = allExceptSome([1, 2]);
const keySetSerialized1 = {
type: KeySetTypes.allExceptSome,
elements: [1, 2],
};
const keySet2 = some([1, 2, 3, 4]);
const keySetSerialized2 = {
type: KeySetTypes.some,
elements: [1, 2, 3, 4],
};

const composedSerialized: KeySetSerialized[] = [keySetSerialized1, keySetSerialized2];
const composedKeySet = composedKeySetFrom([keySet1, keySet2]);

describe("isComposedKeySetSerialized()", () => {
it("isComposedKeySetSerialized(composedSerialized) -> OK", () => {
expect(isComposedKeySetSerialized(composedSerialized)).toBeTruthy();
});
it("isComposedKeySetSerialized([]) -> OK", () => {
expect(isComposedKeySetSerialized([])).toBeTruthy();
});
it("isComposedKeySetSerialized(keySet) -> NOPE", () => {
expect(isComposedKeySetSerialized(keySet1)).toBeFalsy();
});
it("isComposedKeySetSerialized(keySetSerialized) -> NOPE", () => {
expect(isComposedKeySetSerialized(keySetSerialized1)).toBeFalsy();
});
it("isComposedKeySetSerialized(composedKeySet) -> NOPE", () => {
expect(isComposedKeySetSerialized(composedKeySet)).toBeFalsy();
});
it("isComposedKeySetSerialized(composedKeySet) -> NOPE", () => {
expect(isComposedKeySetSerialized(keySet1)).toBeFalsy();
});
});

describe("serializeComposedKeySet()", () => {
it("serializeComposedKeySet(composedSerialized) -> return same", () => {
expect(serializeComposedKeySet(composedSerialized)).toBe(composedSerialized);
});
it("serializeComposedKeySet(composedKeySet) -> serialize each", () => {
expect(serializeComposedKeySet(composedKeySet)).toEqual(composedSerialized);
});
it("composedKeySet.serialized() -> serialize each", () => {
expect(composedKeySet.serialized()).toEqual(composedSerialized);
});
});

describe("parseComposedKeySet()", () => {
it("parseComposedKeySet() with key sets", () => {
expect(parseComposedKeySet(composedSerialized)).toEqual(composedKeySet);
});
it("parseComposedKeySet() with empty list", () => {
expect(parseComposedKeySet([])).toEqual(composedKeySetFrom([]));
});
it("parseComposedKeySet() with an already composedKeySet", () => {
expect(parseComposedKeySet(composedKeySet)).toBe(composedKeySet);
});

it("parseComposedKeySet() with empty list", () => {
expect(() => parseComposedKeySet([{ invalid: "stuff" }] as unknown as ComposedKeySetSerialized)).toThrow();
});
});
});
});
26 changes: 26 additions & 0 deletions src/lib/key-set.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import {
ComposedKeyLabelSetSerialized,
ComposedKeySetSerialized,
Key,
KeyLabelSet,
KeyLabelSetAll,
KeyLabelSetAllExceptSome,
KeyLabelSetAllExceptSomeSerialized,
KeyLabelSetAllSerialized,
KeyLabelSetNone,
KeyLabelSetNoneSerialized,
KeyLabelSetSerialized,
KeyLabelSetSome,
KeyLabelSetSomeSerialized,
KeySet,
KeySetAllExceptSomeSerialized,
Expand All @@ -19,6 +25,7 @@ import {
isKeySetAllExceptSome,
isKeySetNone,
isKeySetSome,
isKeySetType,
isValidKey,
} from "./key-set/-base";
import { KeySetAll, all, allKeySet } from "./key-set/all";
Expand Down Expand Up @@ -46,12 +53,18 @@ import { InvalidEmptySetError } from "./key-set/invalid-empty-set-error";
import { InvalidKeySetError } from "./key-set/invalid-key-set-error";
import { KeySetNone, none, noneKeySet } from "./key-set/none";
import {
isComposedKeySetSerialized,
isKeyLabelSetSerialized,
isKeySetAllExceptSomeSerialized,
isKeySetAllSerialized,
isKeySetNoneSerialized,
isKeySetSerialized,
isKeySetSomeSerialized,
parseComposedKeySet,
parseKeyLabelSet,
parseKeySet,
serializeComposedKeySet,
serializeKeyLabelSet,
serializeKeySet,
} from "./key-set/serialize";
import { KeySetSome, some, someForced, someKeySet, someKeySetForced } from "./key-set/some";
Expand All @@ -77,10 +90,16 @@ export {
composedKeySetFrom,
// types and classes
ComposedKeySet,
ComposedKeySetSerialized,
ComposedKeyLabelSet,
ComposedKeyLabelSetSerialized,
Key,
KeySet,
KeyLabelSet,
KeyLabelSetAll,
KeyLabelSetNone,
KeyLabelSetSome,
KeyLabelSetAllExceptSome,
KeySetAll,
KeySetAllExceptSome,
KeySetNone,
Expand All @@ -104,6 +123,10 @@ export {
// serialize functions
serializeKeySet,
parseKeySet,
parseComposedKeySet,
serializeComposedKeySet,
parseKeyLabelSet,
serializeKeyLabelSet,
// set utils
setByKeys,
sortKeys,
Expand All @@ -124,6 +147,9 @@ export {
isKeySetNoneSerialized,
isKeySetSomeSerialized,
isKeySetAllExceptSomeSerialized,
isKeySetType,
isComposedKeySetSerialized,
isKeyLabelSetSerialized,
// utils
isValidKey,
isKeyLabel,
Expand Down
31 changes: 27 additions & 4 deletions src/lib/key-set/-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ export function isValidKey(x: unknown): x is Key {

export type KeySet<T extends Key = Key> = KeySetAll<T> | KeySetNone<T> | KeySetSome<T> | KeySetAllExceptSome<T>;

export type KeyLabelSetAll<T extends string | number = string | number> = KeySetAll<IKeyLabel<T>>;
export type KeyLabelSetNone<T extends string | number = string | number> = KeySetNone<IKeyLabel<T>>;
export type KeyLabelSetSome<T extends string | number = string | number> = KeySetSome<IKeyLabel<T>>;
export type KeyLabelSetAllExceptSome<T extends string | number = string | number> = KeySetAllExceptSome<IKeyLabel<T>>;

export type KeyLabelSet<T extends string | number = string | number> =
| KeySetAll<IKeyLabel<T>>
| KeySetNone<IKeyLabel<T>>
| KeySetSome<IKeyLabel<T>>
| KeySetAllExceptSome<IKeyLabel<T>>;
| KeyLabelSetAll<T>
| KeyLabelSetNone<T>
| KeyLabelSetSome<T>
| KeyLabelSetAllExceptSome<T>;

/**
* one type for each of the 4 sets
Expand All @@ -32,6 +37,12 @@ export enum KeySetTypes {

export type KeySetTypesEnumValues = "ALL" | "ALL_EXCEPT_SOME" | "NONE" | "SOME";

export const KEY_SET_TYPES = Object.values(KeySetTypes).sort();

export function isKeySetType(x: unknown): x is KeySetTypes {
return KEY_SET_TYPES.includes(x as KeySetTypes);
}

export type KeySetAllSerialized<T extends Key = Key> =
| { type: KeySetTypes.all }
| { type: KeySetTypes.all; elements: EmptyArray<T> };
Expand Down Expand Up @@ -82,6 +93,9 @@ export type KeyLabelSetSerialized<T extends string | number = string | number> =
| KeyLabelSetSomeSerialized<T>
| KeyLabelSetAllExceptSomeSerialized<T>;

export type ComposedKeySetSerialized<T extends Key = Key> = Array<KeySetSerialized<T>>;
export type ComposedKeyLabelSetSerialized = Array<KeyLabelSetSerialized>;

export interface IKeySetClass<T extends Key> {
/**
* returns the KeySetType that defines this class
Expand Down Expand Up @@ -199,3 +213,12 @@ export function isKeySet(x: unknown): x is KeySet;
export function isKeySet(x: unknown): x is KeySet {
return isKeySetAll(x) || isKeySetNone(x) || isKeySetSome(x) || isKeySetAllExceptSome(x);
}

export function isKeyLabelSet<T extends string | number>(
x: KeySet<T> | KeySetSerialized<T> | KeyLabelSet<T> | KeyLabelSetSerialized<T>,
): x is KeyLabelSet<T>;
export function isKeyLabelSet(x: unknown): x is KeyLabelSet;
export function isKeyLabelSet(x: unknown): x is KeyLabelSet {
if (!isKeySet(x)) return false;
return x.elementsList.every(isKeyLabel);
}
10 changes: 9 additions & 1 deletion src/lib/key-set/composed.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { uniqWith } from "es-toolkit";
import { sortBy } from "es-toolkit/compat";
import type { IKeyLabel } from "../util/object-utils";
import type { Key, KeySet } from "./-base";
import type { ComposedKeySetSerialized, Key, KeySet } from "./-base";
import { INSPECT } from "./-is-node-env";
import { KeySetAll, all } from "./all";
import { KeySetAllExceptSome } from "./all-except-some";
Expand Down Expand Up @@ -219,6 +219,14 @@ export class ComposedKeySet<T extends Key = Key> {
compactIntersect(): ComposedKeySet<T> {
return compactWith(this.list, (list) => list.reduce((acc, x) => acc.intersect(x), new KeySetAll<T>()));
}

/**
* returns an array with the serialized version of each of the key sets of this composed key set
* @returns the serialized version of the composed key set
*/
serialized(): ComposedKeySetSerialized<T> {
return this.list.map((x) => x.serialized());
}
}

export function isComposedKeySet<T extends Key>(x: ComposedKeySet<T>): x is ComposedKeySet<T>;
Expand Down
Loading

0 comments on commit 8091558

Please sign in to comment.