diff --git a/src/lib/__tests__/serialize.spec.ts b/src/lib/key-set/__tests__/serialize.spec.ts similarity index 85% rename from src/lib/__tests__/serialize.spec.ts rename to src/lib/key-set/__tests__/serialize.spec.ts index 42beeb1..5801750 100644 --- a/src/lib/__tests__/serialize.spec.ts +++ b/src/lib/key-set/__tests__/serialize.spec.ts @@ -5,6 +5,7 @@ import { type ComposedKeySetSerialized, type IKeyLabel, InvalidKeySetError, + type KeyLabelSet, type KeyLabelSetAll, type KeyLabelSetAllExceptSome, type KeyLabelSetAllExceptSomeSerialized, @@ -46,7 +47,7 @@ import { serializeKeyLabelSet, serializeKeySet, some, -} from "../.."; +} from "../../.."; const allSerialized: KeySetAllSerialized = { type: KeySetTypes.all }; const noneSerialized: KeySetNoneSerialized = { type: KeySetTypes.none }; @@ -606,6 +607,7 @@ describe("serialize KeySet", () => { }; const composedSerialized: KeySetSerialized[] = [keySetSerialized1, keySetSerialized2]; + const arrayKeySets: KeySet[] = [keySet1, keySet2]; const composedKeySet = composedKeySetFrom([keySet1, keySet2]); describe("isComposedKeySetSerialized()", () => { @@ -618,6 +620,9 @@ describe("serialize KeySet", () => { it("isComposedKeySetSerialized(keySet) -> NOPE", () => { expect(isComposedKeySetSerialized(keySet1)).toBeFalsy(); }); + it("isComposedKeySetSerialized(arrayKeySets) -> NOPE", () => { + expect(isComposedKeySetSerialized(arrayKeySets)).toBeFalsy(); + }); it("isComposedKeySetSerialized(keySetSerialized) -> NOPE", () => { expect(isComposedKeySetSerialized(keySetSerialized1)).toBeFalsy(); }); @@ -633,26 +638,42 @@ describe("serialize KeySet", () => { it("serializeComposedKeySet(composedSerialized) -> return same", () => { expect(serializeComposedKeySet(composedSerialized)).toBe(composedSerialized); }); + it("serializeComposedKeySet(arrayKeySets) -> serialize each", () => { + expect(serializeComposedKeySet(arrayKeySets)).toEqual(composedSerialized); + }); it("serializeComposedKeySet(composedKeySet) -> serialize each", () => { expect(serializeComposedKeySet(composedKeySet)).toEqual(composedSerialized); }); + it("serializeComposedKeySet(invalid) -> throw", () => { + expect(() => serializeComposedKeySet(new Date() as unknown as ComposedKeySetSerialized)).toThrow(); + }); + it("serializeComposedKeySet(invalid element in array) -> throw", () => { + expect(() => + serializeComposedKeySet([keySet1, new Date(), keySet2] as unknown as ComposedKeySetSerialized), + ).toThrow(); + }); it("composedKeySet.serialized() -> serialize each", () => { expect(composedKeySet.serialized()).toEqual(composedSerialized); }); }); describe("parseComposedKeySet()", () => { - it("parseComposedKeySet() with key sets", () => { + it("parseComposedKeySet() with composedKeySetSerialized", () => { expect(parseComposedKeySet(composedSerialized)).toEqual(composedKeySet); }); + it("parseComposedKeySet() with a list of keySets", () => { + expect(parseComposedKeySet(arrayKeySets)).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", () => { + it("parseComposedKeySet() with invalid argument", () => { + expect(() => parseComposedKeySet({ invalid: "stuff" } as unknown as ComposedKeySetSerialized)).toThrow(); + }); + it("parseComposedKeySet() with invalid elements", () => { expect(() => parseComposedKeySet([{ invalid: "stuff" }] as unknown as ComposedKeySetSerialized)).toThrow(); }); }); @@ -685,6 +706,7 @@ describe("serialize KeySet", () => { }; const composedSerialized: KeyLabelSetSerialized[] = [keySetSerialized1, keySetSerialized2]; + const arrayKeySets: KeyLabelSet[] = [keySet1, keySet2]; const composedKeyLabelSet = composedKeySetFrom([keySet1, keySet2]); describe("isComposedKeyLabelSetSerialized()", () => { @@ -697,6 +719,9 @@ describe("serialize KeySet", () => { it("isComposedKeyLabelSetSerialized(keySet) -> NOPE", () => { expect(isComposedKeyLabelSetSerialized(keySet1)).toBeFalsy(); }); + it("isComposedKeyLabelSetSerialized(arrayKeySets) -> NOPE", () => { + expect(isComposedKeyLabelSetSerialized(arrayKeySets)).toBeFalsy(); + }); it("isComposedKeyLabelSetSerialized(keySetSerialized) -> NOPE", () => { expect(isComposedKeyLabelSetSerialized(keySetSerialized1)).toBeFalsy(); }); @@ -706,6 +731,9 @@ describe("serialize KeySet", () => { it("isComposedKeyLabelSetSerialized(composedKeyLabelSet) -> NOPE", () => { expect(isComposedKeyLabelSetSerialized(keySet1)).toBeFalsy(); }); + it("isComposedKeyLabelSetSerialized(invalid) -> NOPE", () => { + expect(isComposedKeyLabelSetSerialized(new Date())).toBeFalsy(); + }); }); describe("serializeComposedKeyLabelSet()", () => { @@ -715,6 +743,37 @@ describe("serialize KeySet", () => { it("serializeComposedKeyLabelSet(composedKeyLabelSet) -> serialize each", () => { expect(serializeComposedKeyLabelSet(composedKeyLabelSet)).toEqual(composedSerialized); }); + it("serializeComposedKeyLabelSet(arrayKeySets) -> serialize each", () => { + expect(serializeComposedKeyLabelSet(arrayKeySets)).toEqual(composedSerialized); + }); + it("serializeComposedKeyLabelSet(invalid) -> throw", () => { + expect(() => serializeComposedKeyLabelSet(new Date() as unknown as KeyLabelSetSerialized[])).toThrow(); + }); + it("serializeComposedKeyLabelSet(invalid) -> throw", () => { + expect(() => + serializeComposedKeyLabelSet([keySet1, new Date(), keySetSerialized2] as unknown as KeyLabelSetSerialized[]), + ).toThrow(); + }); + it("serializeComposedKeyLabelSet(composedKeySet not of KeyLabels) -> throw", () => { + expect(() => + serializeComposedKeyLabelSet( + composedKeySetFrom([allExceptSome([1, 2]), some([1, 2])]) as unknown as KeyLabelSetSerialized[], + ), + ).toThrow(); + }); + it("serializeComposedKeyLabelSet(array not of KeyLabels) -> throw", () => { + expect(() => + serializeComposedKeyLabelSet([allExceptSome([1, 2]), some([1, 2])] as unknown as KeyLabelSetSerialized[]), + ).toThrow(); + }); + it("serializeComposedKeyLabelSet(array not of KeyLabel serialized) -> throw", () => { + expect(() => + serializeComposedKeyLabelSet([ + allExceptSome([1, 2]).serialized(), + some([1, 2]).serialized(), + ] as unknown as KeyLabelSetSerialized[]), + ).toThrow(); + }); it("composedKeyLabelSet.serialized() -> serialize each", () => { expect(composedKeyLabelSet.serialized()).toEqual(composedSerialized); }); @@ -724,6 +783,9 @@ describe("serialize KeySet", () => { it("parseComposedKeyLabelSet() with key sets", () => { expect(parseComposedKeyLabelSet(composedSerialized)).toEqual(composedKeyLabelSet); }); + it("parseComposedKeyLabelSet() with arrayKeySets", () => { + expect(parseComposedKeyLabelSet(arrayKeySets)).toEqual(composedKeyLabelSet); + }); it("parseComposedKeyLabelSet() with empty list", () => { expect(parseComposedKeyLabelSet([])).toEqual(composedKeySetFrom([])); }); @@ -731,11 +793,33 @@ describe("serialize KeySet", () => { expect(parseComposedKeyLabelSet(composedKeyLabelSet)).toBe(composedKeyLabelSet); }); - it("parseComposedKeyLabelSet() with empty list", () => { + it("parseComposedKeyLabelSet() with key set not keyLabelSet", () => { + expect(() => + parseComposedKeyLabelSet([allExceptSome([1, 2])] as unknown as ComposedKeyLabelSetSerialized), + ).toThrow(); + }); + it("parseComposedKeyLabelSet() with composedKeySet not keyLabelSet", () => { + expect(() => + parseComposedKeyLabelSet( + composedKeySetFrom([allExceptSome([1, 2])]) as unknown as ComposedKeyLabelSetSerialized, + ), + ).toThrow(); + }); + it("parseComposedKeyLabelSet() with key set not keyLabelSet serialized", () => { + expect(() => + parseComposedKeyLabelSet([allExceptSome([1, 2]).serialized()] as unknown as ComposedKeyLabelSetSerialized), + ).toThrow(); + }); + it("parseComposedKeyLabelSet() with invalid element", () => { expect(() => parseComposedKeyLabelSet([{ invalid: "stuff" }] as unknown as ComposedKeyLabelSetSerialized), ).toThrow(); }); + it("parseComposedKeyLabelSet() with invalid", () => { + expect(() => + parseComposedKeyLabelSet({ invalid: "stuff" } as unknown as ComposedKeyLabelSetSerialized), + ).toThrow(); + }); }); }); }); diff --git a/src/lib/key-set/serialize.ts b/src/lib/key-set/serialize.ts index 9289932..a8dad48 100644 --- a/src/lib/key-set/serialize.ts +++ b/src/lib/key-set/serialize.ts @@ -132,17 +132,69 @@ export function isComposedKeyLabelSetSerialized(x: unknown): x is ComposedKeyLab } export function serializeComposedKeySet( - x: ComposedKeySet | ComposedKeySetSerialized, + x: ComposedKeySet | ComposedKeySetSerialized | Array>, ): ComposedKeySetSerialized { - if (isComposedKeySetSerialized(x)) return x; - return x.serialized(); + if (isComposedKeySet(x)) return x.serialized(); + if (Array.isArray(x)) { + let allSerialized = true; + const newList: KeySetSerialized[] = []; + for (const ks of x) { + if (isKeySet(ks)) { + allSerialized = false; + newList.push(ks.serialized()); + } else if (isKeySetSerialized(ks)) { + newList.push(ks); + } else { + // ERROR: invalid element + throw new InvalidKeySetError( + `ComposedKeySet or array of key sets expected. Invalid element found ${JSON.stringify(ks)}. Full argument given ${JSON.stringify(x)}`, + ); + } + } + + // if all elements are already serialized, return the original array, reusing the same instance in memory + return allSerialized ? (x as ComposedKeySetSerialized) : newList; + } + + // not recognised: error + throw new InvalidKeySetError(`ComposedKeySet expected, given ${JSON.stringify(x)}`); } export function serializeComposedKeyLabelSet( - x: ComposedKeyLabelSet | ComposedKeyLabelSetSerialized, + x: ComposedKeyLabelSet | ComposedKeyLabelSetSerialized | Array>, ): ComposedKeyLabelSetSerialized { - if (isComposedKeyLabelSetSerialized(x)) return x; - return x.serialized(); + if (isComposedKeySet(x)) { + if (isComposedKeyLabelSet(x)) { + return x.serialized(); + } + throw new InvalidKeySetError( + `ComposedKeyLabelSet expected, a ComposedKeySet with no KeyLabels given ${JSON.stringify(x)}`, + ); + } + + if (Array.isArray(x)) { + let allSerialized = true; + const newList: KeyLabelSetSerialized[] = []; + for (const ks of x) { + if (isKeyLabelSet(ks)) { + allSerialized = false; + newList.push(ks.serialized()); + } else if (isKeyLabelSetSerialized(ks)) { + newList.push(ks); + } else { + // ERROR: invalid element + throw new InvalidKeySetError( + `ComposedKeyLabelSet or array of key sets expected. Invalid element found ${JSON.stringify(ks)}. Full argument given ${JSON.stringify(x)}`, + ); + } + } + + // if all elements are already serialized, return the original array, reusing the same instance in memory + return allSerialized ? (x as ComposedKeySetSerialized) : newList; + } + + // not recognised: error + throw new InvalidKeySetError(`ComposedKeySet expected, given ${JSON.stringify(x)}`); } export function serializeKeySet( @@ -177,27 +229,62 @@ export function serializeKeySet(keySet: KeySet | KeySetSeriali export const serializeKeyLabelSet = serializeKeySet; export function parseComposedKeySet( - x: ComposedKeySetSerialized | ComposedKeySet, + x: ComposedKeySetSerialized | ComposedKeySet | Array>, ): ComposedKeySet { if (isComposedKeySet(x)) return x; - if (!isComposedKeySetSerialized(x)) { - throw new InvalidKeySetError(`composedKeySetSerialized expected, given ${JSON.stringify(x)}`); + if (Array.isArray(x)) { + const newList: KeySet[] = []; + for (const ks of x) { + if (isKeySet(ks)) { + newList.push(ks.clone()); + } else if (isKeySetSerialized(ks)) { + newList.push(parseKeySet(ks)); + } else { + // ERROR: invalid element + throw new InvalidKeySetError( + `ComposedKeySet or array of key sets expected. Invalid element found ${JSON.stringify(ks)}. Full argument given ${JSON.stringify(x)}`, + ); + } + } + return composedKeySetFrom(newList); } - return composedKeySetFrom(x.map((y) => parseKeySet(y))); + // not recognised: error + throw new InvalidKeySetError(`ComposedKeySet expected, given ${JSON.stringify(x)}`); } export function parseComposedKeyLabelSet( - x: ComposedKeyLabelSetSerialized | ComposedKeyLabelSet, + x: ComposedKeyLabelSetSerialized | ComposedKeyLabelSet | Array>, ): ComposedKeyLabelSet { - if (isComposedKeyLabelSet(x)) return x; + if (isComposedKeySet(x)) { + if (isComposedKeyLabelSet(x)) { + return x as ComposedKeyLabelSet; + } + throw new InvalidKeySetError( + `ComposedKeyLabelSet expected, a ComposedKeySet with no KeyLabels given ${JSON.stringify(x)}`, + ); + } - if (!isComposedKeySetSerialized(x)) { - throw new InvalidKeySetError(`composedKeySetSerialized expected, given ${JSON.stringify(x)}`); + if (Array.isArray(x)) { + const newList: KeyLabelSet[] = []; + for (const ks of x) { + if (isKeyLabelSet(ks)) { + newList.push(ks.clone()); + } else if (isKeyLabelSetSerialized(ks)) { + newList.push(parseKeyLabelSet(ks)); + } else { + // ERROR: invalid element + throw new InvalidKeySetError( + `ComposedKeyLabelSet or array of key sets expected. Invalid element found ${JSON.stringify(ks)}. Full argument given ${JSON.stringify(x)}`, + ); + } + } + return composedKeySetFrom(newList); } - return composedKeySetFrom(x.map((y) => parseKeyLabelSet(y))); + // not recognised: error + throw new InvalidKeySetError(`ComposedKeyLabelSet expected, given ${JSON.stringify(x)}`); } export function parseKeySet(x: KeySetAllSerialized | KeySetAll): KeySetAll;