From 122bb390fe52dc60199f7af5e233d30f26d3f0da Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Fri, 29 Dec 2023 17:45:32 +0000 Subject: [PATCH] Create Size nodes (#122) --- .changeset/rare-dodos-hope.md | 5 ++ src/nodes/AccountNode.ts | 6 +-- src/nodes/Node.ts | 2 + src/nodes/index.ts | 1 + src/nodes/sizeNodes/FixedSizeNode.ts | 22 ++++++++ src/nodes/sizeNodes/PrefixedSizeNode.ts | 25 +++++++++ src/nodes/sizeNodes/RemainderSizeNode.ts | 23 +++++++++ src/nodes/sizeNodes/SizeNode.ts | 28 ++++++++++ src/nodes/sizeNodes/index.ts | 4 ++ src/nodes/typeNodes/ArrayTypeNode.ts | 26 ++++++---- src/nodes/typeNodes/BytesTypeNode.ts | 16 ++---- src/nodes/typeNodes/MapTypeNode.ts | 26 +++++----- src/nodes/typeNodes/SetTypeNode.ts | 24 ++++----- src/nodes/typeNodes/StringTypeNode.ts | 17 ++----- src/nodes/typeNodes/TypeNode.ts | 5 +- .../js-experimental/getTypeManifestVisitor.ts | 32 +++++++----- src/renderers/js/getTypeManifestVisitor.ts | 19 ++++--- src/renderers/rust/getTypeManifestVisitor.ts | 23 +++++---- src/shared/AccountSeed.ts | 6 +-- src/shared/SizeStrategy.ts | 28 ---------- src/shared/index.ts | 1 - src/visitors/getByteSizeVisitor.ts | 15 +++--- src/visitors/getDebugStringVisitor.ts | 4 +- src/visitors/identityVisitor.ts | 51 +++++++++++++++++-- src/visitors/mergeVisitor.ts | 22 +++++--- .../setAnchorDiscriminatorsVisitor.ts | 6 +-- .../transformU8ArraysToBytesVisitor.ts | 12 +++-- test/testFile.cjs | 2 +- test/visitors/getByteSizeVisitor.test.ts | 3 +- test/visitors/getDebugStringVisitor.test.ts | 12 +++-- 30 files changed, 303 insertions(+), 163 deletions(-) create mode 100644 .changeset/rare-dodos-hope.md create mode 100644 src/nodes/sizeNodes/FixedSizeNode.ts create mode 100644 src/nodes/sizeNodes/PrefixedSizeNode.ts create mode 100644 src/nodes/sizeNodes/RemainderSizeNode.ts create mode 100644 src/nodes/sizeNodes/SizeNode.ts create mode 100644 src/nodes/sizeNodes/index.ts delete mode 100644 src/shared/SizeStrategy.ts diff --git a/.changeset/rare-dodos-hope.md b/.changeset/rare-dodos-hope.md new file mode 100644 index 000000000..6de8a9c9f --- /dev/null +++ b/.changeset/rare-dodos-hope.md @@ -0,0 +1,5 @@ +--- +'@metaplex-foundation/kinobi': minor +--- + +Create Size nodes diff --git a/src/nodes/AccountNode.ts b/src/nodes/AccountNode.ts index 6e2f29657..ba33a7c82 100644 --- a/src/nodes/AccountNode.ts +++ b/src/nodes/AccountNode.ts @@ -6,7 +6,6 @@ import { MainCaseString, PartialExcept, mainCase, - remainderSize, } from '../shared'; import { AccountDataNode, accountDataNode } from './AccountDataNode'; import { bytesTypeNode } from './typeNodes/BytesTypeNode'; @@ -15,6 +14,7 @@ import { stringTypeNode } from './typeNodes/StringTypeNode'; import { assertStructTypeNode } from './typeNodes/StructTypeNode'; import { TypeNode, createTypeNodeFromIdl } from './typeNodes/TypeNode'; import { vScalar } from './ValueNode'; +import { remainderSizeNode } from './sizeNodes'; export type AccountNode = { readonly kind: 'accountNode'; @@ -63,9 +63,9 @@ export function accountNodeFromIdl(idl: Partial): AccountNode { const value = vScalar(seed.value); let type: TypeNode; if (seed.type === 'string') { - type = stringTypeNode({ size: remainderSize() }); + type = stringTypeNode({ size: remainderSizeNode() }); } else if (seed.type === 'bytes') { - type = bytesTypeNode(remainderSize()); + type = bytesTypeNode(remainderSizeNode()); } else { type = createTypeNodeFromIdl(seed.type); } diff --git a/src/nodes/Node.ts b/src/nodes/Node.ts index 5d528791f..b5189aa51 100644 --- a/src/nodes/Node.ts +++ b/src/nodes/Node.ts @@ -8,6 +8,7 @@ import type { InstructionExtraArgsNode } from './InstructionExtraArgsNode'; import type { InstructionNode } from './InstructionNode'; import type { ProgramNode } from './ProgramNode'; import type { RootNode } from './RootNode'; +import { REGISTERED_SIZE_NODES } from './sizeNodes'; import { REGISTERED_TYPE_NODES } from './typeNodes'; const REGISTERED_NODES = { @@ -24,6 +25,7 @@ const REGISTERED_NODES = { // Groups. ...REGISTERED_TYPE_NODES, + ...REGISTERED_SIZE_NODES, }; export const REGISTERED_NODES_KEYS = Object.keys( diff --git a/src/nodes/index.ts b/src/nodes/index.ts index 2863c1bc5..c205c899d 100644 --- a/src/nodes/index.ts +++ b/src/nodes/index.ts @@ -11,4 +11,5 @@ export * from './ProgramNode'; export * from './RootNode'; export * from './ValueNode'; +export * from './sizeNodes'; export * from './typeNodes'; diff --git a/src/nodes/sizeNodes/FixedSizeNode.ts b/src/nodes/sizeNodes/FixedSizeNode.ts new file mode 100644 index 000000000..cf2ad0368 --- /dev/null +++ b/src/nodes/sizeNodes/FixedSizeNode.ts @@ -0,0 +1,22 @@ +import { Node } from '../Node'; + +export type FixedSizeNode = { + readonly kind: 'fixedSizeNode'; + readonly size: number; +}; + +export function fixedSizeNode(size: number): FixedSizeNode { + return { kind: 'fixedSizeNode', size }; +} + +export function isFixedSizeNode(node: Node | null): node is FixedSizeNode { + return !!node && node.kind === 'fixedSizeNode'; +} + +export function assertFixedSizeNode( + node: Node | null +): asserts node is FixedSizeNode { + if (!isFixedSizeNode(node)) { + throw new Error(`Expected fixedSizeNode, got ${node?.kind ?? 'null'}.`); + } +} diff --git a/src/nodes/sizeNodes/PrefixedSizeNode.ts b/src/nodes/sizeNodes/PrefixedSizeNode.ts new file mode 100644 index 000000000..451e240f8 --- /dev/null +++ b/src/nodes/sizeNodes/PrefixedSizeNode.ts @@ -0,0 +1,25 @@ +import { Node } from '../Node'; +import { NumberTypeNode } from '../typeNodes'; + +export type PrefixedSizeNode = { + readonly kind: 'prefixedSizeNode'; + readonly prefix: NumberTypeNode; +}; + +export function prefixedSizeNode(prefix: NumberTypeNode): PrefixedSizeNode { + return { kind: 'prefixedSizeNode', prefix }; +} + +export function isPrefixedSizeNode( + node: Node | null +): node is PrefixedSizeNode { + return !!node && node.kind === 'prefixedSizeNode'; +} + +export function assertPrefixedSizeNode( + node: Node | null +): asserts node is PrefixedSizeNode { + if (!isPrefixedSizeNode(node)) { + throw new Error(`Expected prefixedSizeNode, got ${node?.kind ?? 'null'}.`); + } +} diff --git a/src/nodes/sizeNodes/RemainderSizeNode.ts b/src/nodes/sizeNodes/RemainderSizeNode.ts new file mode 100644 index 000000000..07db7e274 --- /dev/null +++ b/src/nodes/sizeNodes/RemainderSizeNode.ts @@ -0,0 +1,23 @@ +import { Node } from '../Node'; + +export type RemainderSizeNode = { + readonly kind: 'remainderSizeNode'; +}; + +export function remainderSizeNode(): RemainderSizeNode { + return { kind: 'remainderSizeNode' }; +} + +export function isRemainderSizeNode( + node: Node | null +): node is RemainderSizeNode { + return !!node && node.kind === 'remainderSizeNode'; +} + +export function assertRemainderSizeNode( + node: Node | null +): asserts node is RemainderSizeNode { + if (!isRemainderSizeNode(node)) { + throw new Error(`Expected remainderSizeNode, got ${node?.kind ?? 'null'}.`); + } +} diff --git a/src/nodes/sizeNodes/SizeNode.ts b/src/nodes/sizeNodes/SizeNode.ts new file mode 100644 index 000000000..e4caf51c8 --- /dev/null +++ b/src/nodes/sizeNodes/SizeNode.ts @@ -0,0 +1,28 @@ +import { Node } from '../Node'; +import type { FixedSizeNode } from './FixedSizeNode'; +import type { PrefixedSizeNode } from './PrefixedSizeNode'; +import type { RemainderSizeNode } from './RemainderSizeNode'; + +export const REGISTERED_SIZE_NODES = { + fixedSizeNode: {} as FixedSizeNode, + remainderSizeNode: {} as RemainderSizeNode, + prefixedSizeNode: {} as PrefixedSizeNode, +}; + +export const REGISTERED_SIZE_NODE_KEYS = Object.keys( + REGISTERED_SIZE_NODES +) as (keyof typeof REGISTERED_SIZE_NODES)[]; + +export type RegisteredSizeNodes = typeof REGISTERED_SIZE_NODES; + +export type SizeNode = RegisteredSizeNodes[keyof RegisteredSizeNodes]; + +export function isSizeNode(node: Node | null): node is SizeNode { + return !!node && (REGISTERED_SIZE_NODE_KEYS as string[]).includes(node.kind); +} + +export function assertSizeNode(node: Node | null): asserts node is SizeNode { + if (!isSizeNode(node)) { + throw new Error(`Expected typeNode, got ${node?.kind ?? 'null'}.`); + } +} diff --git a/src/nodes/sizeNodes/index.ts b/src/nodes/sizeNodes/index.ts new file mode 100644 index 000000000..f5f688624 --- /dev/null +++ b/src/nodes/sizeNodes/index.ts @@ -0,0 +1,4 @@ +export * from './FixedSizeNode'; +export * from './PrefixedSizeNode'; +export * from './RemainderSizeNode'; +export * from './SizeNode'; diff --git a/src/nodes/typeNodes/ArrayTypeNode.ts b/src/nodes/typeNodes/ArrayTypeNode.ts index 8f4bc8985..7a03c532c 100644 --- a/src/nodes/typeNodes/ArrayTypeNode.ts +++ b/src/nodes/typeNodes/ArrayTypeNode.ts @@ -1,18 +1,18 @@ import type { IdlTypeArray, IdlTypeVec } from '../../idl'; -import { - SizeStrategy, - fixedSize, - prefixedSize, - remainderSize, -} from '../../shared/SizeStrategy'; import type { Node } from '../Node'; +import { + SizeNode, + fixedSizeNode, + prefixedSizeNode, + remainderSizeNode, +} from '../sizeNodes'; import { numberTypeNode } from './NumberTypeNode'; import { TypeNode, createTypeNodeFromIdl } from './TypeNode'; export type ArrayTypeNode = { readonly kind: 'arrayTypeNode'; readonly child: TypeNode; - readonly size: SizeStrategy; + readonly size: SizeNode; }; export function arrayTypeNode( @@ -21,7 +21,11 @@ export function arrayTypeNode( readonly size?: ArrayTypeNode['size']; } = {} ): ArrayTypeNode { - return { kind: 'arrayTypeNode', child, size: options.size ?? prefixedSize() }; + return { + kind: 'arrayTypeNode', + child, + size: options.size ?? prefixedSizeNode(numberTypeNode('u32')), + }; } export function arrayTypeNodeFromIdl( @@ -29,16 +33,16 @@ export function arrayTypeNodeFromIdl( ): ArrayTypeNode { if ('array' in idl) { const child = createTypeNodeFromIdl(idl.array[0]); - return arrayTypeNode(child, { size: fixedSize(idl.array[1]) }); + return arrayTypeNode(child, { size: fixedSizeNode(idl.array[1]) }); } const child = createTypeNodeFromIdl(idl.vec); if (!idl.size) return arrayTypeNode(child); if (idl.size === 'remainder') { - return arrayTypeNode(child, { size: remainderSize() }); + return arrayTypeNode(child, { size: remainderSizeNode() }); } return arrayTypeNode(child, { - size: prefixedSize(numberTypeNode(idl.size)), + size: prefixedSizeNode(numberTypeNode(idl.size)), }); } diff --git a/src/nodes/typeNodes/BytesTypeNode.ts b/src/nodes/typeNodes/BytesTypeNode.ts index 7ce116b08..cd84f4d8c 100644 --- a/src/nodes/typeNodes/BytesTypeNode.ts +++ b/src/nodes/typeNodes/BytesTypeNode.ts @@ -1,21 +1,13 @@ -import { - SizeStrategy, - displaySizeStrategy, - remainderSize, -} from '../../shared/SizeStrategy'; import type { Node } from '../Node'; +import { SizeNode, remainderSizeNode } from '../sizeNodes'; export type BytesTypeNode = { readonly kind: 'bytesTypeNode'; - readonly size: SizeStrategy; + readonly size: SizeNode; }; -export function bytesTypeNode(size?: SizeStrategy): BytesTypeNode { - return { kind: 'bytesTypeNode', size: size ?? remainderSize() }; -} - -export function displayBytesTypeNode(node: BytesTypeNode): string { - return `bytes(${displaySizeStrategy(node.size)})`; +export function bytesTypeNode(size?: SizeNode): BytesTypeNode { + return { kind: 'bytesTypeNode', size: size ?? remainderSizeNode() }; } export function isBytesTypeNode(node: Node | null): node is BytesTypeNode { diff --git a/src/nodes/typeNodes/MapTypeNode.ts b/src/nodes/typeNodes/MapTypeNode.ts index 3c148b414..11fcdd441 100644 --- a/src/nodes/typeNodes/MapTypeNode.ts +++ b/src/nodes/typeNodes/MapTypeNode.ts @@ -1,11 +1,11 @@ import type { IdlTypeMap } from '../../idl'; -import { - SizeStrategy, - fixedSize, - prefixedSize, - remainderSize, -} from '../../shared'; import type { Node } from '../Node'; +import { + SizeNode, + fixedSizeNode, + prefixedSizeNode, + remainderSizeNode, +} from '../sizeNodes'; import { numberTypeNode } from './NumberTypeNode'; import { TypeNode, createTypeNodeFromIdl } from './TypeNode'; @@ -13,7 +13,7 @@ export type MapTypeNode = { readonly kind: 'mapTypeNode'; readonly key: TypeNode; readonly value: TypeNode; - readonly size: SizeStrategy; + readonly size: SizeNode; readonly idlMap: 'hashMap' | 'bTreeMap'; }; @@ -21,7 +21,7 @@ export function mapTypeNode( key: TypeNode, value: TypeNode, options: { - readonly size?: SizeStrategy; + readonly size?: MapTypeNode['size']; readonly idlMap?: MapTypeNode['idlMap']; } = {} ): MapTypeNode { @@ -29,20 +29,20 @@ export function mapTypeNode( kind: 'mapTypeNode', key, value, - size: options.size ?? prefixedSize(), + size: options.size ?? prefixedSizeNode(numberTypeNode('u32')), idlMap: options.idlMap ?? 'hashMap', }; } export function mapTypeNodeFromIdl(idl: IdlTypeMap): MapTypeNode { const [key, value] = 'hashMap' in idl ? idl.hashMap : idl.bTreeMap; - let size: SizeStrategy | undefined; + let size: SizeNode | undefined; if (idl.size === 'remainder') { - size = remainderSize(); + size = remainderSizeNode(); } else if (typeof idl.size === 'number') { - size = fixedSize(idl.size); + size = fixedSizeNode(idl.size); } else if (idl.size) { - size = prefixedSize(numberTypeNode(idl.size)); + size = prefixedSizeNode(numberTypeNode(idl.size)); } return mapTypeNode(createTypeNodeFromIdl(key), createTypeNodeFromIdl(value), { size, diff --git a/src/nodes/typeNodes/SetTypeNode.ts b/src/nodes/typeNodes/SetTypeNode.ts index b5b7a84ad..dc3132088 100644 --- a/src/nodes/typeNodes/SetTypeNode.ts +++ b/src/nodes/typeNodes/SetTypeNode.ts @@ -1,32 +1,32 @@ import type { IdlTypeSet } from '../../idl'; -import { - SizeStrategy, - fixedSize, - prefixedSize, - remainderSize, -} from '../../shared'; import type { Node } from '../Node'; +import { + SizeNode, + fixedSizeNode, + prefixedSizeNode, + remainderSizeNode, +} from '../sizeNodes'; import { numberTypeNode } from './NumberTypeNode'; import { TypeNode, createTypeNodeFromIdl } from './TypeNode'; export type SetTypeNode = { readonly kind: 'setTypeNode'; readonly child: TypeNode; - readonly size: SizeStrategy; + readonly size: SizeNode; readonly idlSet: 'hashSet' | 'bTreeSet'; }; export function setTypeNode( child: TypeNode, options: { - readonly size?: SizeStrategy; + readonly size?: SetTypeNode['size']; readonly idlSet?: SetTypeNode['idlSet']; } = {} ): SetTypeNode { return { kind: 'setTypeNode', child, - size: options.size ?? prefixedSize(), + size: options.size ?? prefixedSizeNode(numberTypeNode('u32')), idlSet: options.idlSet ?? 'hashSet', }; } @@ -35,11 +35,11 @@ export function setTypeNodeFromIdl(idl: IdlTypeSet): SetTypeNode { const child = 'hashSet' in idl ? idl.hashSet : idl.bTreeSet; let size: SetTypeNode['size'] | undefined; if (idl.size === 'remainder') { - size = remainderSize(); + size = remainderSizeNode(); } else if (typeof idl.size === 'number') { - size = fixedSize(idl.size); + size = fixedSizeNode(idl.size); } else if (idl.size) { - size = prefixedSize(numberTypeNode(idl.size)); + size = prefixedSizeNode(numberTypeNode(idl.size)); } return setTypeNode(createTypeNodeFromIdl(child), { size, diff --git a/src/nodes/typeNodes/StringTypeNode.ts b/src/nodes/typeNodes/StringTypeNode.ts index 62a81aeee..a59970a1b 100644 --- a/src/nodes/typeNodes/StringTypeNode.ts +++ b/src/nodes/typeNodes/StringTypeNode.ts @@ -1,35 +1,28 @@ -import { - SizeStrategy, - displaySizeStrategy, - prefixedSize, -} from '../../shared/SizeStrategy'; import type { Node } from '../Node'; +import { SizeNode, prefixedSizeNode } from '../sizeNodes'; +import { numberTypeNode } from './NumberTypeNode'; export type StringEncoding = 'utf8' | 'base16' | 'base58' | 'base64'; export type StringTypeNode = { readonly kind: 'stringTypeNode'; readonly encoding: StringEncoding; - readonly size: SizeStrategy; + readonly size: SizeNode; }; export function stringTypeNode( options: { readonly encoding?: StringEncoding; - readonly size?: SizeStrategy; + readonly size?: SizeNode; } = {} ): StringTypeNode { return { kind: 'stringTypeNode', encoding: options.encoding ?? 'utf8', - size: options.size ?? prefixedSize(), + size: options.size ?? prefixedSizeNode(numberTypeNode('u32')), }; } -export function displayStringTypeNode(node: StringTypeNode): string { - return `string(${node.encoding};${displaySizeStrategy(node.size)})`; -} - export function isStringTypeNode(node: Node | null): node is StringTypeNode { return !!node && node.kind === 'stringTypeNode'; } diff --git a/src/nodes/typeNodes/TypeNode.ts b/src/nodes/typeNodes/TypeNode.ts index 6c098f8a1..244d59893 100644 --- a/src/nodes/typeNodes/TypeNode.ts +++ b/src/nodes/typeNodes/TypeNode.ts @@ -1,5 +1,4 @@ import { IDL_TYPE_LEAVES, IdlType } from '../../idl'; -import { prefixedSize } from '../../shared'; import { ArrayTypeNode, arrayTypeNodeFromIdl } from './ArrayTypeNode'; import { BoolTypeNode, boolTypeNode } from './BoolTypeNode'; import { BytesTypeNode, bytesTypeNode } from './BytesTypeNode'; @@ -21,6 +20,7 @@ import { StructFieldTypeNode } from './StructFieldTypeNode'; import { EnumEmptyVariantTypeNode } from './EnumEmptyVariantTypeNode'; import { EnumStructVariantTypeNode } from './EnumStructVariantTypeNode'; import { EnumTupleVariantTypeNode } from './EnumTupleVariantTypeNode'; +import { prefixedSizeNode } from '../sizeNodes'; export const TYPE_NODES = { amountTypeNode: {} as AmountTypeNode, @@ -73,7 +73,8 @@ export const createTypeNodeFromIdl = (idlType: IdlType): TypeNode => { if (idlType === 'bool') return boolTypeNode(); if (idlType === 'string') return stringTypeNode(); if (idlType === 'publicKey') return publicKeyTypeNode(); - if (idlType === 'bytes') return bytesTypeNode(prefixedSize()); + if (idlType === 'bytes') + return bytesTypeNode(prefixedSizeNode(numberTypeNode('u32'))); return numberTypeNode(idlType); } diff --git a/src/renderers/js-experimental/getTypeManifestVisitor.ts b/src/renderers/js-experimental/getTypeManifestVisitor.ts index d77f7f58c..07e7643c1 100644 --- a/src/renderers/js-experimental/getTypeManifestVisitor.ts +++ b/src/renderers/js-experimental/getTypeManifestVisitor.ts @@ -1,10 +1,14 @@ import { REGISTERED_TYPE_NODE_KEYS, + SizeNode, + isFixedSizeNode, + isPrefixedSizeNode, + isRemainderSizeNode, isScalarEnum, structFieldTypeNode, structTypeNode, } from '../../nodes'; -import { SizeStrategy, camelCase, pascalCase, pipe } from '../../shared'; +import { camelCase, pascalCase, pipe } from '../../shared'; import { Visitor, extendVisitor, staticVisitor, visit } from '../../visitors'; import { ImportMap } from './ImportMap'; import { TypeManifest, mergeManifests } from './TypeManifest'; @@ -551,15 +555,15 @@ export function getTypeManifestVisitor(nameApi: NameApi) { const decoderOptions: string[] = []; // Size option. - if (bytesType.size.kind === 'prefixed') { + if (isPrefixedSizeNode(bytesType.size)) { const prefix = visit(bytesType.size.prefix, self); encoderImports.mergeWith(prefix.encoder); decoderImports.mergeWith(prefix.decoder); encoderOptions.push(`size: ${prefix.encoder.render}`); decoderOptions.push(`size: ${prefix.decoder.render}`); - } else if (bytesType.size.kind === 'fixed') { - encoderOptions.push(`size: ${bytesType.size.value}`); - decoderOptions.push(`size: ${bytesType.size.value}`); + } else if (isFixedSizeNode(bytesType.size)) { + encoderOptions.push(`size: ${bytesType.size.size}`); + decoderOptions.push(`size: ${bytesType.size.size}`); } const encoderOptionsAsString = @@ -673,12 +677,12 @@ export function getTypeManifestVisitor(nameApi: NameApi) { } // Size option. - if (stringType.size.kind === 'remainder') { + if (isRemainderSizeNode(stringType.size)) { encoderOptions.push(`size: 'variable'`); decoderOptions.push(`size: 'variable'`); - } else if (stringType.size.kind === 'fixed') { - encoderOptions.push(`size: ${stringType.size.value}`); - decoderOptions.push(`size: ${stringType.size.value}`); + } else if (isFixedSizeNode(stringType.size)) { + encoderOptions.push(`size: ${stringType.size.size}`); + decoderOptions.push(`size: ${stringType.size.size}`); } else if ( stringType.size.prefix.format !== 'u32' || stringType.size.prefix.endian !== 'le' @@ -721,19 +725,19 @@ function createDocblock(docs: string[]): string { } function getArrayLikeSizeOption( - size: SizeStrategy, + size: SizeNode, visitor: Visitor ): { encoder: Fragment; decoder: Fragment; } { - if (size.kind === 'fixed') { + if (isFixedSizeNode(size)) { return { - encoder: fragment(`size: ${size.value}`), - decoder: fragment(`size: ${size.value}`), + encoder: fragment(`size: ${size.size}`), + decoder: fragment(`size: ${size.size}`), }; } - if (size.kind === 'remainder') { + if (isRemainderSizeNode(size)) { return { encoder: fragment(`size: 'remainder'`), decoder: fragment(`size: 'remainder'`), diff --git a/src/renderers/js/getTypeManifestVisitor.ts b/src/renderers/js/getTypeManifestVisitor.ts index 937cd5555..0020a06ed 100644 --- a/src/renderers/js/getTypeManifestVisitor.ts +++ b/src/renderers/js/getTypeManifestVisitor.ts @@ -1,7 +1,10 @@ import { ArrayTypeNode, REGISTERED_TYPE_NODE_KEYS, + isFixedSizeNode, isInteger, + isPrefixedSizeNode, + isRemainderSizeNode, isScalarEnum, isUnsignedInteger, structFieldTypeNode, @@ -498,14 +501,14 @@ export function getTypeManifestVisitor() { const options: string[] = []; // Size option. - if (bytesType.size.kind === 'prefixed') { + if (isPrefixedSizeNode(bytesType.size)) { const prefix = visit(bytesType.size.prefix, self); strictImports.mergeWith(prefix.strictImports); looseImports.mergeWith(prefix.looseImports); serializerImports.mergeWith(prefix.serializerImports); options.push(`size: ${prefix.serializer}`); - } else if (bytesType.size.kind === 'fixed') { - options.push(`size: ${bytesType.size.value}`); + } else if (isFixedSizeNode(bytesType.size)) { + options.push(`size: ${bytesType.size.size}`); } const optionsAsString = @@ -643,10 +646,10 @@ export function getTypeManifestVisitor() { } // Size option. - if (stringType.size.kind === 'remainder') { + if (isRemainderSizeNode(stringType.size)) { options.push(`size: 'variable'`); - } else if (stringType.size.kind === 'fixed') { - options.push(`size: ${stringType.size.value}`); + } else if (isFixedSizeNode(stringType.size)) { + options.push(`size: ${stringType.size.size}`); } else if ( stringType.size.prefix.format !== 'u32' || stringType.size.prefix.endian !== 'le' @@ -710,8 +713,8 @@ function getArrayLikeSizeOption( >, self: Visitor ): string | null { - if (size.kind === 'fixed') return `size: ${size.value}`; - if (size.kind === 'remainder') return `size: 'remainder'`; + if (isFixedSizeNode(size)) return `size: ${size.size}`; + if (isRemainderSizeNode(size)) return `size: 'remainder'`; const prefixManifest = visit(size.prefix, self); if (prefixManifest.serializer === 'u32()') return null; diff --git a/src/renderers/rust/getTypeManifestVisitor.ts b/src/renderers/rust/getTypeManifestVisitor.ts index f1c4e6d60..7d8ebed22 100644 --- a/src/renderers/rust/getTypeManifestVisitor.ts +++ b/src/renderers/rust/getTypeManifestVisitor.ts @@ -2,6 +2,9 @@ import { REGISTERED_TYPE_NODE_KEYS, arrayTypeNode, isEnumTypeNode, + isFixedSizeNode, + isPrefixedSizeNode, + isRemainderSizeNode, isScalarEnum, numberTypeNode, } from '../../nodes'; @@ -118,15 +121,15 @@ export function getTypeManifestVisitor() { visitArrayType(arrayType, { self }) { const childManifest = visit(arrayType.child, self); - if (arrayType.size.kind === 'fixed') { + if (isFixedSizeNode(arrayType.size)) { return { ...childManifest, - type: `[${childManifest.type}; ${arrayType.size.value}]`, + type: `[${childManifest.type}; ${arrayType.size.size}]`, }; } if ( - arrayType.size.kind === 'prefixed' && + isPrefixedSizeNode(arrayType.size) && arrayType.size.prefix.endian === 'le' ) { switch (arrayType.size.prefix.format) { @@ -152,7 +155,7 @@ export function getTypeManifestVisitor() { } } - if (arrayType.size.kind === 'remainder') { + if (isRemainderSizeNode(arrayType.size)) { childManifest.imports.add('kaigan::types::RemainderVec'); return { ...childManifest, @@ -358,8 +361,8 @@ export function getTypeManifestVisitor() { (structFieldType.child.kind === 'arrayTypeNode' || structFieldType.child.kind === 'bytesTypeNode' || structFieldType.child.kind === 'stringTypeNode') && - structFieldType.child.size.kind === 'fixed' && - structFieldType.child.size.value > 32 + isFixedSizeNode(structFieldType.child.size) && + structFieldType.child.size.size > 32 ) { derive = '#[cfg_attr(feature = "serde", serde(with = "serde_with::As::"))]\n'; @@ -427,7 +430,7 @@ export function getTypeManifestVisitor() { visitStringType(stringType) { if ( - stringType.size.kind === 'prefixed' && + isPrefixedSizeNode(stringType.size) && stringType.size.prefix.format === 'u32' && stringType.size.prefix.endian === 'le' ) { @@ -438,15 +441,15 @@ export function getTypeManifestVisitor() { }; } - if (stringType.size.kind === 'fixed') { + if (isFixedSizeNode(stringType.size)) { return { - type: `[u8; ${stringType.size.value}]`, + type: `[u8; ${stringType.size.size}]`, imports: new RustImportMap(), nestedStructs: [], }; } - if (stringType.size.kind === 'remainder') { + if (isRemainderSizeNode(stringType.size)) { return { type: `&str`, imports: new RustImportMap(), diff --git a/src/shared/AccountSeed.ts b/src/shared/AccountSeed.ts index c369d80f9..1f95aee45 100644 --- a/src/shared/AccountSeed.ts +++ b/src/shared/AccountSeed.ts @@ -2,10 +2,10 @@ import { TypeNode, ValueNode, publicKeyTypeNode, + remainderSizeNode, stringTypeNode, vScalar, } from '../nodes'; -import { remainderSize } from './SizeStrategy'; import { MainCaseString, mainCase } from './utils'; export type AccountSeed = @@ -21,7 +21,7 @@ export const constantSeed = ( ): AccountSeed => ({ kind: 'constant', type, value }); export const stringConstantSeed = (value: string): AccountSeed => - constantSeed(stringTypeNode({ size: remainderSize() }), vScalar(value)); + constantSeed(stringTypeNode({ size: remainderSizeNode() }), vScalar(value)); export const variableSeed = ( name: string, @@ -43,4 +43,4 @@ export const stringSeed = ( name: string, docs: string | string[] = [] ): AccountSeed => - variableSeed(name, stringTypeNode({ size: remainderSize() }), docs); + variableSeed(name, stringTypeNode({ size: remainderSizeNode() }), docs); diff --git a/src/shared/SizeStrategy.ts b/src/shared/SizeStrategy.ts deleted file mode 100644 index 89eca0c1f..000000000 --- a/src/shared/SizeStrategy.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { - NumberTypeNode, - displayNumberTypeNode, - numberTypeNode, -} from '../nodes/typeNodes/NumberTypeNode'; - -export type SizeStrategy = - | { kind: 'fixed'; value: number } - | { kind: 'prefixed'; prefix: NumberTypeNode } - | { kind: 'remainder' }; - -export const fixedSize = (value: number): SizeStrategy => ({ - kind: 'fixed', - value, -}); - -export const prefixedSize = (prefix?: NumberTypeNode): SizeStrategy => ({ - kind: 'prefixed', - prefix: prefix ?? numberTypeNode('u32'), -}); - -export const remainderSize = (): SizeStrategy => ({ kind: 'remainder' }); - -export const displaySizeStrategy = (size: SizeStrategy): string => { - if (size.kind === 'fixed') return `${size.value}`; - if (size.kind === 'prefixed') return `${displayNumberTypeNode(size.prefix)}`; - return 'remainder'; -}; diff --git a/src/shared/index.ts b/src/shared/index.ts index 26ba96772..369d09e18 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -9,7 +9,6 @@ export * from './NodeSelector'; export * from './NodeStack'; export * from './RemainingAccounts'; export * from './RenderMap'; -export * from './SizeStrategy'; export * from './ValidatorBag'; export * from './errors'; export * from './logs'; diff --git a/src/visitors/getByteSizeVisitor.ts b/src/visitors/getByteSizeVisitor.ts index 71a890fe5..1811c4c65 100644 --- a/src/visitors/getByteSizeVisitor.ts +++ b/src/visitors/getByteSizeVisitor.ts @@ -2,10 +2,11 @@ import { DefinedTypeNode, REGISTERED_TYPE_NODE_KEYS, RegisteredTypeNodes, + isFixedSizeNode, isScalarEnum, } from '../nodes'; -import { Visitor, visit } from './visitor'; import { mergeVisitor } from './mergeVisitor'; +import { Visitor, visit } from './visitor'; export type ByteSizeVisitorKeys = | keyof RegisteredTypeNodes @@ -68,8 +69,8 @@ export function getByteSizeVisitor( }, visitArrayType(node) { - if (node.size.kind !== 'fixed') return null; - const fixedSize = node.size.value; + if (!isFixedSizeNode(node.size)) return null; + const fixedSize = node.size.size; const childSize = visit(node.child, this); const arraySize = childSize !== null ? childSize * fixedSize : null; return fixedSize === 0 ? 0 : arraySize; @@ -121,8 +122,8 @@ export function getByteSizeVisitor( }, visitBytesType(node) { - if (node.size.kind !== 'fixed') return null; - return node.size.value; + if (!isFixedSizeNode(node.size)) return null; + return node.size.size; }, visitNumberType(node) { @@ -134,8 +135,8 @@ export function getByteSizeVisitor( }, visitStringType(node) { - if (node.size.kind !== 'fixed') return null; - return node.size.value; + if (!isFixedSizeNode(node.size)) return null; + return node.size.size; }, }; } diff --git a/src/visitors/getDebugStringVisitor.ts b/src/visitors/getDebugStringVisitor.ts index f4d25e399..b60733160 100644 --- a/src/visitors/getDebugStringVisitor.ts +++ b/src/visitors/getDebugStringVisitor.ts @@ -71,7 +71,9 @@ function getNodeDetails(node: Node): string[] { case 'amountTypeNode': return [node.identifier, node.decimals.toString()]; case 'stringTypeNode': - return [node.encoding, node.size.kind]; + return [node.encoding]; + case 'fixedSizeNode': + return [node.size.toString()]; default: return 'name' in node ? [node.name] : []; } diff --git a/src/visitors/identityVisitor.ts b/src/visitors/identityVisitor.ts index 6ca1d42e4..953258c13 100644 --- a/src/visitors/identityVisitor.ts +++ b/src/visitors/identityVisitor.ts @@ -18,11 +18,13 @@ import { assertLinkTypeNode, assertNumberTypeNode, assertProgramNode, + assertSizeNode, assertStructFieldTypeNode, assertStructTypeNode, assertTupleTypeNode, assertTypeNode, boolTypeNode, + bytesTypeNode, dateTimeTypeNode, definedTypeNode, enumStructVariantTypeNode, @@ -33,11 +35,13 @@ import { instructionNode, mapTypeNode, optionTypeNode, + prefixedSizeNode, programNode, removeNullAndAssertNodeFilter, rootNode, setTypeNode, solAmountTypeNode, + stringTypeNode, structFieldTypeNode, structTypeNode, tupleTypeNode, @@ -175,10 +179,13 @@ export function identityVisitor< if (castedNodeKeys.includes('arrayTypeNode')) { visitor.visitArrayType = function visitArrayType(node) { + const size = visit(this)(node.size); + if (size === null) return null; + assertSizeNode(size); const child = visit(this)(node.child); if (child === null) return null; assertTypeNode(child); - return arrayTypeNode(child, { ...node }); + return arrayTypeNode(child, { size }); }; } @@ -217,12 +224,16 @@ export function identityVisitor< if (castedNodeKeys.includes('mapTypeNode')) { visitor.visitMapType = function visitMapType(node) { + const size = visit(this)(node.size); + if (size === null) return null; + assertSizeNode(size); const key = visit(this)(node.key); - const value = visit(this)(node.value); - if (key === null || value === null) return null; + if (key === null) return null; assertTypeNode(key); + const value = visit(this)(node.value); + if (value === null) return null; assertTypeNode(value); - return mapTypeNode(key, value, { ...node }); + return mapTypeNode(key, value, { ...node, size }); }; } @@ -249,10 +260,13 @@ export function identityVisitor< if (castedNodeKeys.includes('setTypeNode')) { visitor.visitSetType = function visitSetType(node) { + const size = visit(this)(node.size); + if (size === null) return null; + assertSizeNode(size); const child = visit(this)(node.child); if (child === null) return null; assertTypeNode(child); - return setTypeNode(child, { ...node }); + return setTypeNode(child, { ...node, size }); }; } @@ -285,6 +299,24 @@ export function identityVisitor< }; } + if (castedNodeKeys.includes('stringTypeNode')) { + visitor.visitStringType = function visitStringType(node) { + const size = visit(this)(node.size); + if (size === null) return null; + assertSizeNode(size); + return stringTypeNode({ ...node, size }); + }; + } + + if (castedNodeKeys.includes('bytesTypeNode')) { + visitor.visitBytesType = function visitBytesType(node) { + const size = visit(this)(node.size); + if (size === null) return null; + assertSizeNode(size); + return bytesTypeNode(size); + }; + } + if (castedNodeKeys.includes('amountTypeNode')) { visitor.visitAmountType = function visitAmountType(node) { const number = visit(this)(node.number); @@ -312,5 +344,14 @@ export function identityVisitor< }; } + if (castedNodeKeys.includes('prefixedSizeNode')) { + visitor.visitPrefixedSize = function visitPrefixedSize(node) { + const prefix = visit(this)(node.prefix); + if (prefix === null) return null; + assertNumberTypeNode(prefix); + return prefixedSizeNode(prefix); + }; + } + return visitor as Visitor; } diff --git a/src/visitors/mergeVisitor.ts b/src/visitors/mergeVisitor.ts index 5ec8717ab..9e0eeb60c 100644 --- a/src/visitors/mergeVisitor.ts +++ b/src/visitors/mergeVisitor.ts @@ -95,7 +95,7 @@ export function mergeVisitor< if (castedNodeKeys.includes('arrayTypeNode')) { visitor.visitArrayType = function visitArrayType(node) { return merge(node, [ - ...(node.size.kind === 'prefixed' ? visit(this)(node.size.prefix) : []), + ...visit(this)(node.size), ...visit(this)(node.child), ]); }; @@ -129,7 +129,7 @@ export function mergeVisitor< if (castedNodeKeys.includes('mapTypeNode')) { visitor.visitMapType = function visitMapType(node) { return merge(node, [ - ...(node.size.kind === 'prefixed' ? visit(this)(node.size.prefix) : []), + ...visit(this)(node.size), ...visit(this)(node.key), ...visit(this)(node.value), ]); @@ -154,7 +154,7 @@ export function mergeVisitor< if (castedNodeKeys.includes('setTypeNode')) { visitor.visitSetType = function visitSetType(node) { return merge(node, [ - ...(node.size.kind === 'prefixed' ? visit(this)(node.size.prefix) : []), + ...visit(this)(node.size), ...visit(this)(node.child), ]); }; @@ -198,9 +198,19 @@ export function mergeVisitor< if (castedNodeKeys.includes('stringTypeNode')) { visitor.visitStringType = function visitStringType(node) { - return node.size.kind === 'prefixed' - ? merge(node, visit(this)(node.size.prefix)) - : leafValue(node); + return merge(node, visit(this)(node.size)); + }; + } + + if (castedNodeKeys.includes('bytesTypeNode')) { + visitor.visitBytesType = function visitBytesType(node) { + return merge(node, visit(this)(node.size)); + }; + } + + if (castedNodeKeys.includes('prefixedSizeNode')) { + visitor.visitPrefixedSize = function visitPrefixedSize(node) { + return merge(node, visit(this)(node.prefix)); }; } diff --git a/src/visitors/setAnchorDiscriminatorsVisitor.ts b/src/visitors/setAnchorDiscriminatorsVisitor.ts index 6782e713d..ae605062b 100644 --- a/src/visitors/setAnchorDiscriminatorsVisitor.ts +++ b/src/visitors/setAnchorDiscriminatorsVisitor.ts @@ -3,6 +3,7 @@ import { accountDataNode, accountNode, arrayTypeNode, + fixedSizeNode, instructionDataArgsNode, instructionNode, numberTypeNode, @@ -11,7 +12,6 @@ import { } from '../nodes'; import { fieldAccountDiscriminator, - fixedSize, getAnchorAccountDiscriminator, getAnchorInstructionDiscriminator, pipe, @@ -44,7 +44,7 @@ export function setAnchorDiscriminatorsVisitor() { const discriminatorField = structFieldTypeNode({ name: 'discriminator', child: arrayTypeNode(numberTypeNode('u8'), { - size: fixedSize(8), + size: fixedSizeNode(8), }), defaultsTo: { strategy: 'omitted', @@ -72,7 +72,7 @@ export function setAnchorDiscriminatorsVisitor() { const discriminatorField = structFieldTypeNode({ name: 'discriminator', child: arrayTypeNode(numberTypeNode('u8'), { - size: fixedSize(8), + size: fixedSizeNode(8), }), defaultsTo: { strategy: 'omitted', diff --git a/src/visitors/transformU8ArraysToBytesVisitor.ts b/src/visitors/transformU8ArraysToBytesVisitor.ts index 5bb51849a..7149ffa09 100644 --- a/src/visitors/transformU8ArraysToBytesVisitor.ts +++ b/src/visitors/transformU8ArraysToBytesVisitor.ts @@ -3,9 +3,11 @@ import { arrayTypeNode, assertTypeNode, bytesTypeNode, + fixedSizeNode, + isFixedSizeNode, isNumberTypeNode, } from '../nodes'; -import { fixedSize, pipe } from '../shared'; +import { pipe } from '../shared'; import { extendVisitor } from './extendVisitor'; import { identityVisitor } from './identityVisitor'; import { visit } from './visitor'; @@ -14,8 +16,8 @@ export function transformU8ArraysToBytesVisitor( sizes: number[] | '*' = [32, 64] ) { const hasRequiredSize = (size: ArrayTypeNode['size']): boolean => { - if (size.kind !== 'fixed') return false; - return sizes === '*' || sizes.includes(size.value); + if (!isFixedSizeNode(size)) return false; + return sizes === '*' || sizes.includes(size.size); }; return pipe(identityVisitor(), (v) => @@ -27,10 +29,10 @@ export function transformU8ArraysToBytesVisitor( if ( isNumberTypeNode(child) && child.format === 'u8' && - node.size.kind === 'fixed' && + isFixedSizeNode(node.size) && hasRequiredSize(node.size) ) { - return bytesTypeNode(fixedSize(node.size.value)); + return bytesTypeNode(fixedSizeNode(node.size.size)); } return arrayTypeNode(child, { ...node }); diff --git a/test/testFile.cjs b/test/testFile.cjs index 896a1d381..c38ff2848 100644 --- a/test/testFile.cjs +++ b/test/testFile.cjs @@ -139,7 +139,7 @@ kinobi.update( }, proof: { type: k.arrayTypeNode(k.publicKeyTypeNode(), { - size: k.remainderSize(), + size: k.remainderSizeNode(), }), defaultsTo: k.valueDefault(k.vList([])), }, diff --git a/test/visitors/getByteSizeVisitor.test.ts b/test/visitors/getByteSizeVisitor.test.ts index 99f32d1c4..039e75844 100644 --- a/test/visitors/getByteSizeVisitor.test.ts +++ b/test/visitors/getByteSizeVisitor.test.ts @@ -7,6 +7,7 @@ import { enumStructVariantTypeNode, enumTupleVariantTypeNode, enumTypeNode, + fixedSizeNode, getByteSizeVisitor, numberTypeNode, publicKeyTypeNode, @@ -52,7 +53,7 @@ test( structFieldTypeNode({ name: 'age', child: numberTypeNode('u32') }), structFieldTypeNode({ name: 'firstname', - child: stringTypeNode({ size: { kind: 'fixed', value: 42 } }), + child: stringTypeNode({ size: fixedSizeNode(42) }), }), ]), 4 + 42 diff --git a/test/visitors/getDebugStringVisitor.test.ts b/test/visitors/getDebugStringVisitor.test.ts index 4407a8ca2..9b59af81f 100644 --- a/test/visitors/getDebugStringVisitor.test.ts +++ b/test/visitors/getDebugStringVisitor.test.ts @@ -5,6 +5,7 @@ import { getDebugStringVisitor, numberTypeNode, optionTypeNode, + prefixedSizeNode, publicKeyTypeNode, stringTypeNode, structFieldTypeNode, @@ -21,7 +22,7 @@ test('it returns a string representing the main information of a node for debugg structFieldTypeNode({ name: 'firstname', child: stringTypeNode({ - size: { kind: 'prefixed', prefix: numberTypeNode('u64') }, + size: prefixedSizeNode(numberTypeNode('u64')), encoding: 'utf8', }), }), @@ -49,7 +50,7 @@ test('it returns a string representing the main information of a node for debugg // Then we expect the following string. t.deepEqual( result, - 'tupleTypeNode(numberTypeNode[u32], structTypeNode(structFieldTypeNode[firstname](stringTypeNode[utf8.prefixed](numberTypeNode[u64])), structFieldTypeNode[age](numberTypeNode[u32]), structFieldTypeNode[wallet](optionTypeNode(numberTypeNode[u16], publicKeyTypeNode)), structFieldTypeNode[industry](enumTypeNode(numberTypeNode[u8], enumEmptyVariantTypeNode[programming], enumEmptyVariantTypeNode[crypto], enumEmptyVariantTypeNode[music]))))' + 'tupleTypeNode(numberTypeNode[u32], structTypeNode(structFieldTypeNode[firstname](stringTypeNode[utf8](prefixedSizeNode(numberTypeNode[u64]))), structFieldTypeNode[age](numberTypeNode[u32]), structFieldTypeNode[wallet](optionTypeNode(numberTypeNode[u16], publicKeyTypeNode)), structFieldTypeNode[industry](enumTypeNode(numberTypeNode[u8], enumEmptyVariantTypeNode[programming], enumEmptyVariantTypeNode[crypto], enumEmptyVariantTypeNode[music]))))' ); }); @@ -61,7 +62,7 @@ test('it can create indented strings', (t) => { structFieldTypeNode({ name: 'firstname', child: stringTypeNode({ - size: { kind: 'prefixed', prefix: numberTypeNode('u64') }, + size: prefixedSizeNode(numberTypeNode('u64')), encoding: 'utf8', }), }), @@ -93,8 +94,9 @@ test('it can create indented strings', (t) => { | numberTypeNode [u32] | structTypeNode | | structFieldTypeNode [firstname] -| | | stringTypeNode [utf8.prefixed] -| | | | numberTypeNode [u64] +| | | stringTypeNode [utf8] +| | | | prefixedSizeNode +| | | | | numberTypeNode [u64] | | structFieldTypeNode [age] | | | numberTypeNode [u32] | | structFieldTypeNode [wallet]