Skip to content

Commit

Permalink
implement option layout item (#530)
Browse files Browse the repository at this point in the history
  • Loading branch information
nonergodic authored May 23, 2024
1 parent cdd08a9 commit a39e20d
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 3 deletions.
43 changes: 43 additions & 0 deletions core/base/__tests__/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
deserializeLayout,
layoutDiscriminator,
serializeLayout,
optionItem,
} from "./../src/index.js";

// prettier-ignore
Expand Down Expand Up @@ -226,6 +227,48 @@ describe("Layout tests", function () {
});
});

describe("OptionItem tests", () => {
it("basic test", () => {
const layout = optionItem({binary: "uint", size: 1});

const testCases = [[32, [1, 32]], [undefined, [0]]] as const;
for (const [data, expected] of testCases) {
const encoded = serializeLayout(layout, data);
expect(encoded).toEqual(new Uint8Array(expected));

const decoded = deserializeLayout(layout, encoded);
expect(decoded).toEqual(data);
}
})

it("advanced test", () => {
const layout = { binary: "array", layout: [
{ name: "firstOption", ...optionItem({binary: "uint", size: 1}) },
{ name: "someUint", binary: "uint", size: 1},
{ name: "secondOption", ...optionItem({binary: "bytes", size: 4}) },
]} as const satisfies Layout;

const data = [
{ firstOption: undefined, someUint: 1, secondOption: undefined},
{ firstOption: 10, someUint: 2, secondOption: undefined },
{ firstOption: undefined, someUint: 3, secondOption: new Uint8Array([1,2,3,4]) },
{ firstOption: 20, someUint: 4, secondOption: new Uint8Array([5,6,7,8]) },
] as const;
const expected = new Uint8Array([
...[0, 1, 0 ],
...[1, 10, 2, 0 ],
...[0, 3, 1, 1, 2, 3, 4],
...[1, 20, 4, 1, 5, 6, 7, 8],
]);

const encoded = serializeLayout(layout, data);
expect(encoded).toEqual(new Uint8Array(expected));

const decoded = deserializeLayout(layout, encoded);
expect(decoded).toEqual(data);
})
})

it("should serialize and deserialize correctly", function () {
const encoded = serializeLayout(testLayout, completeValues);
const decoded = deserializeLayout(testLayout, encoded);
Expand Down
39 changes: 38 additions & 1 deletion core/base/src/utils/layout/items.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,45 @@
import type { NumSizeToPrimitive} from './layout.js';
import type { NumSizeToPrimitive, LayoutToType, CustomConversion } from './layout.js';
import { numberMaxSize } from './layout.js';
import type { CustomizableBytes, CustomizableBytesReturn } from './utils.js';
import { customizableBytes } from './utils.js';

//TODO implement enum item

const baseOptionItem = <const T extends CustomizableBytes>(someType: T) => ({
binary: "switch",
idSize: 1,
idTag: "isSome",
layouts: [
[[0, false], []],
[[1, true ], [customizableBytes({ name: "value"}, someType)]],
]
} as const);

type BaseOptionItem<T extends CustomizableBytes> =
LayoutToType<ReturnType<typeof baseOptionItem<T>>>;

type BaseOptionValue<T extends CustomizableBytes> =
LayoutToType<CustomizableBytesReturn<{}, T>> | undefined;

export function optionItem<const T extends CustomizableBytes>(optVal: T) {
return {
binary: "bytes",
layout: baseOptionItem(optVal),
custom: {
to: (obj: BaseOptionItem<T>): BaseOptionValue<T> =>
obj.isSome === true
//TODO I'm really not sure why we need to manually narrow the type here
? (obj as Exclude<typeof obj, {isSome: false}>)["value"]
: undefined,
from: (value: BaseOptionValue<T>): BaseOptionItem<T> =>
value === undefined
? { isSome: false }
//TODO and this is even more sketch
: ({ isSome: true, value } as unknown as Exclude<BaseOptionItem<T>, { isSome: false }>),
} satisfies CustomConversion<BaseOptionItem<T>, BaseOptionValue<T>>
} as const
};

export type Bitset<B extends readonly (string | undefined)[]> =
{[K in B[number] as K extends "" | undefined ? never : K]: boolean};

Expand Down
10 changes: 9 additions & 1 deletion core/base/src/utils/layout/size.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ function calcItemSize(item: LayoutItem, data: any): number | null {
const lengthSize = ("lengthSize" in item) ? item.lengthSize | 0 : 0;

if ("layout" in item) {
const layoutSize = internalCalcLayoutSize(item.layout, data);
const { custom } = item;
const layoutSize = internalCalcLayoutSize(
item.layout,
custom === undefined
? data
: typeof custom.from === "function"
? custom.from(data)
: custom.from
);
if (layoutSize === null)
return ("size" in item ) ? item.size ?? null : null;

Expand Down
3 changes: 2 additions & 1 deletion core/base/src/utils/layout/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ type CombineObjects<T, U> = {
};

export type BytesBase =
{ readonly name: string } & Omit<BytesLayoutItem, "binary" | "custom" | "layout">;
( {} | { readonly name: string } ) & Omit<BytesLayoutItem, "binary" | "custom" | "layout">;


export type CustomizableBytesReturn<B extends BytesBase, P extends CustomizableBytes> =
CombineObjects<
Expand Down

0 comments on commit a39e20d

Please sign in to comment.