From 75f5f913cecfa2d09287a24d0c346040110667cd Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Wed, 10 Apr 2024 15:03:30 +0100 Subject: [PATCH] Implement ConstantValueNode type manifests (#200) --- .changeset/dry-queens-drop.md | 5 + src/renderers/js-experimental/TypeManifest.ts | 48 ++-- .../fragments/discriminatorCondition.ts | 15 +- .../fragments/instructionFunction.ts | 5 +- .../fragments/instructionInputDefault.ts | 13 +- .../fragments/instructionInputResolved.ts | 2 +- .../js-experimental/fragments/pdaFunction.ts | 15 +- .../fragments/programAccounts.ts | 10 +- .../fragments/programInstructions.ts | 10 +- .../js-experimental/getRenderMapVisitor.ts | 14 +- .../js-experimental/getTypeManifestVisitor.ts | 216 ++++++++++++-- .../js-experimental/renderValueNodeVisitor.ts | 123 -------- src/renderers/js/getRenderMapVisitor.ts | 22 +- src/renderers/js/getTypeManifestVisitor.ts | 265 +++++++++++++++++- src/renderers/js/renderInstructionDefaults.ts | 30 +- src/renderers/js/renderValueNodeVisitor.ts | 171 ----------- src/renderers/rust/renderValueNodeVisitor.ts | 32 ++- 17 files changed, 563 insertions(+), 433 deletions(-) create mode 100644 .changeset/dry-queens-drop.md delete mode 100644 src/renderers/js-experimental/renderValueNodeVisitor.ts delete mode 100644 src/renderers/js/renderValueNodeVisitor.ts diff --git a/.changeset/dry-queens-drop.md b/.changeset/dry-queens-drop.md new file mode 100644 index 000000000..df3c46e42 --- /dev/null +++ b/.changeset/dry-queens-drop.md @@ -0,0 +1,5 @@ +--- +"@metaplex-foundation/kinobi": minor +--- + +Implement ConstantValueNode type manifests diff --git a/src/renderers/js-experimental/TypeManifest.ts b/src/renderers/js-experimental/TypeManifest.ts index fa64c580b..430997d8d 100644 --- a/src/renderers/js-experimental/TypeManifest.ts +++ b/src/renderers/js-experimental/TypeManifest.ts @@ -1,4 +1,4 @@ -import { Fragment, mergeFragments } from './fragments'; +import { Fragment, fragment, mergeFragments } from './fragments'; export type TypeManifest = { isEnum: boolean; @@ -6,30 +6,40 @@ export type TypeManifest = { looseType: Fragment; encoder: Fragment; decoder: Fragment; + value: Fragment; }; +export function typeManifest(): TypeManifest { + return { + isEnum: false, + strictType: fragment(''), + looseType: fragment(''), + encoder: fragment(''), + decoder: fragment(''), + value: fragment(''), + }; +} + export function mergeManifests( manifests: TypeManifest[], - mergeTypes: (renders: string[]) => string, - mergeCodecs: (renders: string[]) => string + options: { + mergeTypes?: (renders: string[]) => string; + mergeCodecs?: (renders: string[]) => string; + mergeValues?: (renders: string[]) => string; + } = {} ): TypeManifest { + const { mergeTypes, mergeCodecs, mergeValues } = options; + const merge = ( + fragmentFn: (m: TypeManifest) => Fragment, + mergeFn?: (r: string[]) => string + ) => + mergeFn ? mergeFragments(manifests.map(fragmentFn), mergeFn) : fragment(''); return { isEnum: false, - strictType: mergeFragments( - manifests.map((m) => m.strictType), - mergeTypes - ), - looseType: mergeFragments( - manifests.map((m) => m.looseType), - mergeTypes - ), - encoder: mergeFragments( - manifests.map((m) => m.encoder), - mergeCodecs - ), - decoder: mergeFragments( - manifests.map((m) => m.decoder), - mergeCodecs - ), + strictType: merge((m) => m.strictType, mergeTypes), + looseType: merge((m) => m.looseType, mergeTypes), + encoder: merge((m) => m.encoder, mergeCodecs), + decoder: merge((m) => m.decoder, mergeCodecs), + value: merge((m) => m.value, mergeValues), }; } diff --git a/src/renderers/js-experimental/fragments/discriminatorCondition.ts b/src/renderers/js-experimental/fragments/discriminatorCondition.ts index be4aa1d36..fb7bbc176 100644 --- a/src/renderers/js-experimental/fragments/discriminatorCondition.ts +++ b/src/renderers/js-experimental/fragments/discriminatorCondition.ts @@ -30,10 +30,7 @@ import { Fragment, fragment, mergeFragments } from './common'; * ``` */ export function getDiscriminatorConditionFragment( - scope: Pick< - GlobalFragmentScope, - 'nameApi' | 'typeManifestVisitor' | 'valueNodeVisitor' - > & { + scope: Pick & { programNode: ProgramNode; discriminators: DiscriminatorNode[]; struct: StructTypeNode; @@ -77,10 +74,10 @@ function getByteConditionFragment( function getFieldConditionFragment( discriminator: FieldDiscriminatorNode, - scope: Pick< - GlobalFragmentScope, - 'typeManifestVisitor' | 'valueNodeVisitor' - > & { dataName: string; struct: StructTypeNode } + scope: Pick & { + dataName: string; + struct: StructTypeNode; + } ): Fragment { const field = scope.struct.fields.find((f) => f.name === discriminator.name); if (!field || !field.defaultValue) { @@ -111,7 +108,7 @@ function getFieldConditionFragment( return mergeFragments( [ visit(field.type, scope.typeManifestVisitor).encoder, - visit(field.defaultValue, scope.valueNodeVisitor), + visit(field.defaultValue, scope.typeManifestVisitor).value, ], ([encoderFunction, value]) => `${encoderFunction}.encode(${value})` ) diff --git a/src/renderers/js-experimental/fragments/instructionFunction.ts b/src/renderers/js-experimental/fragments/instructionFunction.ts index fa28cd790..d2baa0357 100644 --- a/src/renderers/js-experimental/fragments/instructionFunction.ts +++ b/src/renderers/js-experimental/fragments/instructionFunction.ts @@ -19,7 +19,10 @@ import { getInstructionRemainingAccountsFragment } from './instructionRemainingA export function getInstructionFunctionFragment( scope: Pick< GlobalFragmentScope, - 'nameApi' | 'asyncResolvers' | 'valueNodeVisitor' | 'customInstructionData' + | 'nameApi' + | 'asyncResolvers' + | 'typeManifestVisitor' + | 'customInstructionData' > & { instructionNode: InstructionNode; programNode: ProgramNode; diff --git a/src/renderers/js-experimental/fragments/instructionInputDefault.ts b/src/renderers/js-experimental/fragments/instructionInputDefault.ts index fd4796e3e..e059d5d47 100644 --- a/src/renderers/js-experimental/fragments/instructionInputDefault.ts +++ b/src/renderers/js-experimental/fragments/instructionInputDefault.ts @@ -8,7 +8,7 @@ import { Fragment, fragment, mergeFragments } from './common'; export function getInstructionInputDefaultFragment( scope: Pick< GlobalFragmentScope, - 'nameApi' | 'asyncResolvers' | 'valueNodeVisitor' + 'nameApi' | 'asyncResolvers' | 'typeManifestVisitor' > & { input: ResolvedInstructionInput; optionalAccountStrategy: 'programId' | 'omitted'; @@ -21,7 +21,7 @@ export function getInstructionInputDefaultFragment( asyncResolvers, useAsync, nameApi, - valueNodeVisitor, + typeManifestVisitor, } = scope; if (!input.defaultValue) { return fragment(''); @@ -96,7 +96,7 @@ export function getInstructionInputDefaultFragment( `${seed.name}: expectSome(args.${camelCase(seed.value.name)})` ).addImports('shared', 'expectSome'); } - return visit(seed.value, valueNodeVisitor).mapRender( + return visit(seed.value, typeManifestVisitor).value.mapRender( (r) => `${seed.name}: ${r}` ); }); @@ -209,7 +209,10 @@ export function getInstructionInputDefaultFragment( ? `accounts.${camelCase(defaultValue.condition.name)}.value` : `args.${camelCase(defaultValue.condition.name)}`; if (defaultValue.value) { - const comparedValue = visit(defaultValue.value, valueNodeVisitor); + const comparedValue = visit( + defaultValue.value, + typeManifestVisitor + ).value; conditionalFragment .mergeImportsWith(comparedValue) .mergeFeaturesWith(comparedValue); @@ -235,7 +238,7 @@ export function getInstructionInputDefaultFragment( ); default: - const valueManifest = visit(defaultValue, valueNodeVisitor); + const valueManifest = visit(defaultValue, typeManifestVisitor).value; return defaultFragment(valueManifest.render).mergeImportsWith( valueManifest ); diff --git a/src/renderers/js-experimental/fragments/instructionInputResolved.ts b/src/renderers/js-experimental/fragments/instructionInputResolved.ts index 228dfe7ca..5df8b590d 100644 --- a/src/renderers/js-experimental/fragments/instructionInputResolved.ts +++ b/src/renderers/js-experimental/fragments/instructionInputResolved.ts @@ -8,7 +8,7 @@ import { getInstructionInputDefaultFragment } from './instructionInputDefault'; export function getInstructionInputResolvedFragment( scope: Pick< GlobalFragmentScope, - 'nameApi' | 'asyncResolvers' | 'valueNodeVisitor' + 'nameApi' | 'asyncResolvers' | 'typeManifestVisitor' > & { instructionNode: InstructionNode; resolvedInputs: ResolvedInstructionInput[]; diff --git a/src/renderers/js-experimental/fragments/pdaFunction.ts b/src/renderers/js-experimental/fragments/pdaFunction.ts index 7399275b2..f929c985b 100644 --- a/src/renderers/js-experimental/fragments/pdaFunction.ts +++ b/src/renderers/js-experimental/fragments/pdaFunction.ts @@ -5,21 +5,12 @@ import type { GlobalFragmentScope } from '../getRenderMapVisitor'; import { Fragment, fragmentFromTemplate } from './common'; export function getPdaFunctionFragment( - scope: Pick< - GlobalFragmentScope, - 'nameApi' | 'typeManifestVisitor' | 'valueNodeVisitor' - > & { + scope: Pick & { pdaNode: PdaNode; programNode: ProgramNode; } ): Fragment { - const { - pdaNode, - programNode, - typeManifestVisitor, - valueNodeVisitor, - nameApi, - } = scope; + const { pdaNode, programNode, typeManifestVisitor, nameApi } = scope; // Seeds. const imports = new ImportMap(); @@ -28,7 +19,7 @@ export function getPdaFunctionFragment( const seedManifest = visit(seed.type, typeManifestVisitor); imports.mergeWith(seedManifest.encoder); const seedValue = seed.value; - const valueManifest = visit(seedValue, valueNodeVisitor); + const valueManifest = visit(seedValue, typeManifestVisitor).value; (seedValue as any).render = valueManifest.render; imports.mergeWith(valueManifest.imports); return { ...seed, typeManifest: seedManifest }; diff --git a/src/renderers/js-experimental/fragments/programAccounts.ts b/src/renderers/js-experimental/fragments/programAccounts.ts index ea990f9b7..f83fa817d 100644 --- a/src/renderers/js-experimental/fragments/programAccounts.ts +++ b/src/renderers/js-experimental/fragments/programAccounts.ts @@ -4,10 +4,7 @@ import { Fragment, fragment, mergeFragments } from './common'; import { getDiscriminatorConditionFragment } from './discriminatorCondition'; export function getProgramAccountsFragment( - scope: Pick< - GlobalFragmentScope, - 'nameApi' | 'typeManifestVisitor' | 'valueNodeVisitor' - > & { + scope: Pick & { programNode: ProgramNode; } ): Fragment { @@ -39,10 +36,7 @@ function getProgramAccountsEnumFragment( } function getProgramAccountsIdentifierFunctionFragment( - scope: Pick< - GlobalFragmentScope, - 'nameApi' | 'typeManifestVisitor' | 'valueNodeVisitor' - > & { + scope: Pick & { programNode: ProgramNode; } ): Fragment { diff --git a/src/renderers/js-experimental/fragments/programInstructions.ts b/src/renderers/js-experimental/fragments/programInstructions.ts index 5826be75b..22a32da5b 100644 --- a/src/renderers/js-experimental/fragments/programInstructions.ts +++ b/src/renderers/js-experimental/fragments/programInstructions.ts @@ -11,10 +11,7 @@ import { getDiscriminatorConditionFragment } from './discriminatorCondition'; export function getProgramInstructionsFragment( scope: Pick< GlobalFragmentScope, - | 'nameApi' - | 'typeManifestVisitor' - | 'valueNodeVisitor' - | 'renderParentInstructions' + 'nameApi' | 'typeManifestVisitor' | 'renderParentInstructions' > & { programNode: ProgramNode; } @@ -56,10 +53,7 @@ function getProgramInstructionsEnumFragment( } function getProgramInstructionsIdentifierFunctionFragment( - scope: Pick< - GlobalFragmentScope, - 'nameApi' | 'typeManifestVisitor' | 'valueNodeVisitor' - > & { + scope: Pick & { programNode: ProgramNode; allInstructions: InstructionNode[]; } diff --git a/src/renderers/js-experimental/getRenderMapVisitor.ts b/src/renderers/js-experimental/getRenderMapVisitor.ts index 4c5b112c7..408fe149f 100644 --- a/src/renderers/js-experimental/getRenderMapVisitor.ts +++ b/src/renderers/js-experimental/getRenderMapVisitor.ts @@ -63,10 +63,6 @@ import { NameApi, NameTransformers, } from './nameTransformers'; -import { - renderValueNodeVisitor, - ValueNodeVisitor, -} from './renderValueNodeVisitor'; const DEFAULT_PRETTIER_OPTIONS: PrettierOptions = { semi: true, @@ -96,7 +92,6 @@ export type GlobalFragmentScope = { nameApi: NameApi; linkables: LinkableDictionary; typeManifestVisitor: TypeManifestVisitor; - valueNodeVisitor: ValueNodeVisitor; asyncResolvers: MainCaseString[]; nonScalarEnums: MainCaseString[]; renderParentInstructions: boolean; @@ -132,18 +127,14 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { 'InstructionData' ); - const valueNodeVisitor = renderValueNodeVisitor({ - nameApi, - linkables, - nonScalarEnums, - }); const getTypeManifestVisitor = (parentName?: { strict: string; loose: string; }) => baseGetTypeManifestVisitor({ nameApi, - valueNodeVisitor, + linkables, + nonScalarEnums, customAccountData, customInstructionData, parentName, @@ -155,7 +146,6 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { nameApi, linkables, typeManifestVisitor, - valueNodeVisitor, asyncResolvers, nonScalarEnums, renderParentInstructions, diff --git a/src/renderers/js-experimental/getTypeManifestVisitor.ts b/src/renderers/js-experimental/getTypeManifestVisitor.ts index fefc245df..9b46bbdf9 100644 --- a/src/renderers/js-experimental/getTypeManifestVisitor.ts +++ b/src/renderers/js-experimental/getTypeManifestVisitor.ts @@ -2,7 +2,9 @@ import { CountNode, NumberTypeNode, REGISTERED_TYPE_NODE_KINDS, + REGISTERED_VALUE_NODE_KINDS, TypeNode, + getBytesFromBytesValueNode, isNode, isScalarEnum, resolveNestedTypeNode, @@ -10,27 +12,35 @@ import { structTypeNode, structTypeNodeFromInstructionArgumentNodes, } from '../../nodes'; -import { camelCase, jsDocblock, mainCase, pipe } from '../../shared'; +import { + LinkableDictionary, + MainCaseString, + camelCase, + jsDocblock, + mainCase, + pipe, +} from '../../shared'; import { Visitor, extendVisitor, staticVisitor, visit } from '../../visitors'; import { ImportMap } from './ImportMap'; -import { TypeManifest, mergeManifests } from './TypeManifest'; +import { TypeManifest, mergeManifests, typeManifest } from './TypeManifest'; import { ParsedCustomDataOptions } from './customDataHelpers'; -import { Fragment, fragment } from './fragments'; +import { Fragment, fragment, mergeFragments } from './fragments'; import { NameApi } from './nameTransformers'; -import { ValueNodeVisitor } from './renderValueNodeVisitor'; export type TypeManifestVisitor = ReturnType; export function getTypeManifestVisitor(input: { nameApi: NameApi; - valueNodeVisitor: ValueNodeVisitor; + linkables: LinkableDictionary; + nonScalarEnums: MainCaseString[]; customAccountData: ParsedCustomDataOptions; customInstructionData: ParsedCustomDataOptions; parentName?: { strict: string; loose: string }; }) { const { nameApi, - valueNodeVisitor, + linkables, + nonScalarEnums, customAccountData, customInstructionData, } = input; @@ -46,9 +56,11 @@ export function getTypeManifestVisitor(input: { looseType: fragment(''), encoder: fragment(''), decoder: fragment(''), + value: fragment(''), }) as TypeManifest, [ ...REGISTERED_TYPE_NODE_KINDS, + ...REGISTERED_VALUE_NODE_KINDS, 'definedTypeLinkNode', 'definedTypeNode', 'accountNode', @@ -137,6 +149,7 @@ export function getTypeManifestVisitor(input: { importFrom, decoderFunction ), + value: fragment(''), }; }, @@ -206,13 +219,16 @@ export function getTypeManifestVisitor(input: { 'getEnumDecoder' ) ), + value: fragment(''), }; } const mergedManifest = mergeManifests( enumType.variants.map((variant) => visit(variant, self)), - (renders) => renders.join(' | '), - (renders) => renders.join(', ') + { + mergeTypes: (renders) => renders.join(' | '), + mergeCodecs: (renders) => renders.join(', '), + } ); mergedManifest.encoder .mapRender( @@ -255,6 +271,7 @@ export function getTypeManifestVisitor(input: { 'solanaCodecsDataStructures', 'getUnitDecoder' ), + value: fragment(''), }; }, @@ -317,11 +334,10 @@ export function getTypeManifestVisitor(input: { visitMapType(mapType, { self }) { const key = visit(mapType.key, self); const value = visit(mapType.value, self); - const mergedManifest = mergeManifests( - [key, value], - ([k, v]) => `Map<${k}, ${v}>`, - ([k, v]) => `${k}, ${v}` - ); + const mergedManifest = mergeManifests([key, value], { + mergeTypes: ([k, v]) => `Map<${k}, ${v}>`, + mergeCodecs: ([k, v]) => `${k}, ${v}`, + }); const sizeManifest = getArrayLikeSizeOption(mapType.count, self); const encoderOptions = sizeManifest.encoder.render ? `, { ${sizeManifest.encoder.render} }` @@ -407,16 +423,16 @@ export function getTypeManifestVisitor(input: { }, visitStructType(structType, { self }) { - // const currentParentName = parentName; - parentName = null; const optionalFields = structType.fields.filter( (f) => !!f.defaultValue ); const mergedManifest = mergeManifests( structType.fields.map((field) => visit(field, self)), - (renders) => `{ ${renders.join('')} }`, - (renders) => `([${renders.join(', ')}])` + { + mergeTypes: (renders) => `{ ${renders.join('')} }`, + mergeCodecs: (renders) => `([${renders.join(', ')}])`, + } ); mergedManifest.encoder @@ -438,8 +454,8 @@ export function getTypeManifestVisitor(input: { >; const { render: renderedValue, imports } = visit( defaultValue, - valueNodeVisitor - ); + self + ).value; mergedManifest.encoder.mergeImportsWith(imports); return f.defaultValueStrategy === 'omitted' ? `${key}: ${renderedValue}` @@ -492,11 +508,10 @@ export function getTypeManifestVisitor(input: { visitTupleType(tupleType, { self }) { const items = tupleType.items.map((item) => visit(item, self)); - const mergedManifest = mergeManifests( - items, - (types) => `readonly [${types.join(', ')}]`, - (codecs) => `[${codecs.join(', ')}]` - ); + const mergedManifest = mergeManifests(items, { + mergeTypes: (types) => `readonly [${types.join(', ')}]`, + mergeCodecs: (codecs) => `[${codecs.join(', ')}]`, + }); mergedManifest.encoder .mapRender((render) => `getTupleEncoder(${render})`) .addImports('solanaCodecsDataStructures', 'getTupleEncoder'); @@ -539,6 +554,7 @@ export function getTypeManifestVisitor(input: { `getBooleanDecoder(${sizeDecoder})`, decoderImports ), + value: fragment(''), }; }, @@ -583,6 +599,7 @@ export function getTypeManifestVisitor(input: { `getBytesDecoder(${decoderOptionsAsString})`, decoderImports ), + value: fragment(''), }; }, @@ -618,6 +635,7 @@ export function getTypeManifestVisitor(input: { `${decoderFunction}(${endianness})`, decoderImports ), + value: fragment(''), }; }, @@ -647,6 +665,7 @@ export function getTypeManifestVisitor(input: { 'solanaAddresses', 'getAddressDecoder' ), + value: fragment(''), }; }, @@ -711,6 +730,7 @@ export function getTypeManifestVisitor(input: { `getStringDecoder(${decoderOptionsAsString})`, decoderImports ), + value: fragment(''), }; }, @@ -727,6 +747,154 @@ export function getTypeManifestVisitor(input: { parentSize = null; return manifest; }, + + visitArrayValue(node, { self }) { + return mergeManifests( + node.items.map((v) => visit(v, self)), + { mergeValues: (renders) => `[${renders.join(', ')}]` } + ); + }, + + visitBooleanValue(node) { + const manifest = typeManifest(); + manifest.value.setRender(JSON.stringify(node.boolean)); + return manifest; + }, + + visitBytesValue(node) { + const manifest = typeManifest(); + const bytes = getBytesFromBytesValueNode(node); + manifest.value.setRender( + `new Uint8Array([${Array.from(bytes).join(', ')}])` + ); + return manifest; + }, + + visitConstantValue(node, { self }) { + const manifest = typeManifest(); + manifest.value = mergeFragments( + [visit(node.type, self).encoder, visit(node.value, self).value], + ([encoderFunction, value]) => `${encoderFunction}.encode(${value})` + ); + return manifest; + }, + + visitEnumValue(node, { self }) { + const manifest = typeManifest(); + const enumName = nameApi.dataType(node.enum.name); + const enumFunction = nameApi.discriminatedUnionFunction( + node.enum.name + ); + const importFrom = node.enum.importFrom ?? 'generatedTypes'; + + const enumNode = linkables.get(node.enum)?.type; + const isScalar = + enumNode && isNode(enumNode, 'enumTypeNode') + ? isScalarEnum(enumNode) + : !nonScalarEnums.includes(node.enum.name); + + if (!node.value && isScalar) { + const variantName = nameApi.enumVariant(node.variant); + manifest.value + .setRender(`${enumName}.${variantName}`) + .addImports(importFrom, enumName); + return manifest; + } + + const variantName = nameApi.discriminatedUnionVariant(node.variant); + if (!node.value) { + manifest.value + .setRender(`${enumFunction}('${variantName}')`) + .addImports(importFrom, enumFunction); + return manifest; + } + + manifest.value = visit(node.value, self) + .value.mapRender((r) => `${enumFunction}('${variantName}', ${r})`) + .addImports(importFrom, enumFunction); + return manifest; + }, + + visitMapValue(node, { self }) { + const entryFragments = node.entries.map((entry) => + visit(entry, self) + ); + return mergeManifests(entryFragments, { + mergeValues: (renders) => `new Map([${renders.join(', ')}])`, + }); + }, + + visitMapEntryValue(node, { self }) { + return mergeManifests( + [visit(node.key, self), visit(node.value, self)], + { mergeValues: (renders) => `[${renders.join(', ')}]` } + ); + }, + + visitNoneValue() { + const manifest = typeManifest(); + manifest.value + .setRender('none()') + .addImports('solanaOptions', 'none'); + return manifest; + }, + + visitNumberValue(node) { + const manifest = typeManifest(); + manifest.value.setRender(JSON.stringify(node.number)); + return manifest; + }, + + visitPublicKeyValue(node) { + const manifest = typeManifest(); + manifest.value + .setRender(`address("${node.publicKey}")`) + .addImports('solanaAddresses', 'address'); + return manifest; + }, + + visitSetValue(node, { self }) { + return mergeManifests( + node.items.map((v) => visit(v, self)), + { mergeValues: (renders) => `new Set([${renders.join(', ')}])` } + ); + }, + + visitSomeValue(node, { self }) { + const manifest = typeManifest(); + manifest.value = visit(node.value, self) + .value.mapRender((r) => `some(${r})`) + .addImports('solanaOptions', 'some'); + return manifest; + }, + + visitStringValue(node) { + const manifest = typeManifest(); + manifest.value.setRender(JSON.stringify(node.string)); + return manifest; + }, + + visitStructValue(node, { self }) { + return mergeManifests( + node.fields.map((field) => visit(field, self)), + { mergeValues: (renders) => `{ ${renders.join(', ')} }` } + ); + }, + + visitStructFieldValue(node, { self }) { + const manifest = typeManifest(); + manifest.value = visit(node.value, self).value.mapRender( + (r) => `${node.name}: ${r}` + ); + return manifest; + }, + + visitTupleValue(node, { self }) { + return mergeManifests( + node.items.map((v) => visit(v, self)), + { mergeValues: (renders) => `[${renders.join(', ')}]` } + ); + }, }) ); } diff --git a/src/renderers/js-experimental/renderValueNodeVisitor.ts b/src/renderers/js-experimental/renderValueNodeVisitor.ts deleted file mode 100644 index c05527087..000000000 --- a/src/renderers/js-experimental/renderValueNodeVisitor.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { - RegisteredValueNode, - getBytesFromBytesValueNode, - isNode, - isScalarEnum, -} from '../../nodes'; -import { LinkableDictionary, MainCaseString } from '../../shared'; -import { Visitor, visit } from '../../visitors'; -import { Fragment, fragment, mergeFragments } from './fragments'; -import { NameApi } from './nameTransformers'; - -export type ValueNodeVisitor = ReturnType; - -export function renderValueNodeVisitor(input: { - nameApi: NameApi; - linkables: LinkableDictionary; - nonScalarEnums: MainCaseString[]; -}): Visitor { - const { nameApi, linkables, nonScalarEnums } = input; - return { - visitArrayValue(node) { - return mergeFragments( - node.items.map((v) => visit(v, this)), - (renders) => `[${renders.join(', ')}]` - ); - }, - visitBooleanValue(node) { - return fragment(JSON.stringify(node.boolean)); - }, - visitBytesValue(node) { - const bytes = getBytesFromBytesValueNode(node); - return fragment(`new Uint8Array([${Array.from(bytes).join(', ')}])`); - }, - visitConstantValue() { - throw new Error('Not implemented'); - }, - visitEnumValue(node) { - const enumName = nameApi.dataType(node.enum.name); - const enumFunction = nameApi.discriminatedUnionFunction(node.enum.name); - const importFrom = node.enum.importFrom ?? 'generatedTypes'; - - const enumNode = linkables.get(node.enum)?.type; - const isScalar = - enumNode && isNode(enumNode, 'enumTypeNode') - ? isScalarEnum(enumNode) - : !nonScalarEnums.includes(node.enum.name); - - if (!node.value && isScalar) { - const variantName = nameApi.enumVariant(node.variant); - return fragment(`${enumName}.${variantName}`).addImports( - importFrom, - enumName - ); - } - - const variantName = nameApi.discriminatedUnionVariant(node.variant); - if (!node.value) { - return fragment(`${enumFunction}('${variantName}')`).addImports( - importFrom, - enumFunction - ); - } - - return visit(node.value, this) - .mapRender((r) => `${enumFunction}('${variantName}', ${r})`) - .addImports(importFrom, enumFunction); - }, - visitMapValue(node) { - const entryFragments = node.entries.map((entry) => visit(entry, this)); - return mergeFragments( - entryFragments, - (renders) => `new Map([${renders.join(', ')}])` - ); - }, - visitMapEntryValue(node) { - return mergeFragments( - [visit(node.key, this), visit(node.value, this)], - (renders) => `[${renders.join(', ')}]` - ); - }, - visitNoneValue() { - return fragment('none()').addImports('solanaOptions', 'none'); - }, - visitNumberValue(node) { - return fragment(JSON.stringify(node.number)); - }, - visitPublicKeyValue(node) { - return fragment(`address("${node.publicKey}")`).addImports( - 'solanaAddresses', - 'address' - ); - }, - visitSetValue(node) { - return mergeFragments( - node.items.map((v) => visit(v, this)), - (renders) => `new Set([${renders.join(', ')}])` - ); - }, - visitSomeValue(node) { - return visit(node.value, this) - .mapRender((r) => `some(${r})`) - .addImports('solanaOptions', 'some'); - }, - visitStringValue(node) { - return fragment(JSON.stringify(node.string)); - }, - visitStructValue(node) { - return mergeFragments( - node.fields.map((field) => visit(field, this)), - (renders) => `{ ${renders.join(', ')} }` - ); - }, - visitStructFieldValue(node) { - return visit(node.value, this).mapRender((r) => `${node.name}: ${r}`); - }, - visitTupleValue(node) { - return mergeFragments( - node.items.map((v) => visit(v, this)), - (renders) => `[${renders.join(', ')}]` - ); - }, - }; -} diff --git a/src/renderers/js/getRenderMapVisitor.ts b/src/renderers/js/getRenderMapVisitor.ts index 6b49e2390..27cdaedf6 100644 --- a/src/renderers/js/getRenderMapVisitor.ts +++ b/src/renderers/js/getRenderMapVisitor.ts @@ -50,7 +50,6 @@ import { getTypeManifestVisitor as baseGetTypeManifestVisitor } from './getTypeM import { JavaScriptContextMap } from './JavaScriptContextMap'; import { JavaScriptImportMap } from './JavaScriptImportMap'; import { renderInstructionDefaults } from './renderInstructionDefaults'; -import { renderValueNodeVisitor } from './renderValueNodeVisitor'; const DEFAULT_PRETTIER_OPTIONS: PrettierOptions = { semi: true, @@ -112,16 +111,13 @@ export function getRenderMapVisitor( 'InstructionData' ); - const valueNodeVisitor = renderValueNodeVisitor({ - linkables, - nonScalarEnums, - }); const getTypeManifestVisitor = (parentName?: { strict: string; loose: string; }) => baseGetTypeManifestVisitor({ - valueNodeVisitor, + linkables, + nonScalarEnums, customAccountData, customInstructionData, parentName, @@ -328,13 +324,13 @@ export function getRenderMapVisitor( node.data ).fields.find((f) => f.name === discriminator.name); const discriminatorValue = discriminatorField?.defaultValue - ? visit(discriminatorField.defaultValue, valueNodeVisitor) + ? visit(discriminatorField.defaultValue, typeManifestVisitor) : undefined; if (discriminatorValue) { - imports.mergeWith(discriminatorValue.imports); + imports.mergeWith(discriminatorValue.valueImports); resolvedDiscriminator = { ...discriminator, - value: discriminatorValue.render, + value: discriminatorValue.value, }; } } else if (isNode(discriminator, 'sizeDiscriminatorNode')) { @@ -380,9 +376,9 @@ export function getRenderMapVisitor( const seedManifest = visit(seed.type, typeManifestVisitor); imports.mergeWith(seedManifest.serializerImports); const seedValue = seed.value; - const valueManifest = visit(seedValue, valueNodeVisitor); - (seedValue as any).render = valueManifest.render; - imports.mergeWith(valueManifest.imports); + const valueManifest = visit(seedValue, typeManifestVisitor); + (seedValue as any).render = valueManifest.value; + imports.mergeWith(valueManifest.valueImports); return { ...seed, typeManifest: seedManifest }; } if (isNode(seed, 'variablePdaSeedNode')) { @@ -512,7 +508,7 @@ export function getRenderMapVisitor( ).map((input: ResolvedInstructionInput) => { const renderedInput = renderInstructionDefaults( input, - valueNodeVisitor, + typeManifestVisitor, node.optionalAccountStrategy, argObject ); diff --git a/src/renderers/js/getTypeManifestVisitor.ts b/src/renderers/js/getTypeManifestVisitor.ts index 8831bb79c..289a78b39 100644 --- a/src/renderers/js/getTypeManifestVisitor.ts +++ b/src/renderers/js/getTypeManifestVisitor.ts @@ -2,7 +2,9 @@ import { ArrayTypeNode, NumberTypeNode, REGISTERED_TYPE_NODE_KINDS, + REGISTERED_VALUE_NODE_KINDS, TypeNode, + getBytesFromBytesValueNode, isInteger, isNode, isScalarEnum, @@ -12,11 +14,17 @@ import { structTypeNode, structTypeNodeFromInstructionArgumentNodes, } from '../../nodes'; -import { camelCase, jsDocblock, pascalCase, pipe } from '../../shared'; +import { + LinkableDictionary, + MainCaseString, + camelCase, + jsDocblock, + pascalCase, + pipe, +} from '../../shared'; import { Visitor, extendVisitor, staticVisitor, visit } from '../../visitors'; import { JavaScriptImportMap } from './JavaScriptImportMap'; import { ParsedCustomDataOptions } from './customDataHelpers'; -import { renderValueNodeVisitor } from './renderValueNodeVisitor'; export type JavaScriptTypeManifest = { isEnum: boolean; @@ -26,15 +34,37 @@ export type JavaScriptTypeManifest = { looseImports: JavaScriptImportMap; serializer: string; serializerImports: JavaScriptImportMap; + value: string; + valueImports: JavaScriptImportMap; }; +function typeManifest(): JavaScriptTypeManifest { + return { + isEnum: false, + strictType: '', + strictImports: new JavaScriptImportMap(), + looseType: '', + looseImports: new JavaScriptImportMap(), + serializer: '', + serializerImports: new JavaScriptImportMap(), + value: '', + valueImports: new JavaScriptImportMap(), + }; +} + export function getTypeManifestVisitor(input: { - valueNodeVisitor: ReturnType; + linkables: LinkableDictionary; + nonScalarEnums: MainCaseString[]; customAccountData: ParsedCustomDataOptions; customInstructionData: ParsedCustomDataOptions; parentName?: { strict: string; loose: string }; }) { - const { valueNodeVisitor, customAccountData, customInstructionData } = input; + const { + linkables, + nonScalarEnums, + customAccountData, + customInstructionData, + } = input; let parentName = input.parentName ?? null; let parentSize: number | NumberTypeNode | null = null; @@ -49,9 +79,12 @@ export function getTypeManifestVisitor(input: { looseImports: new JavaScriptImportMap(), serializer: '', serializerImports: new JavaScriptImportMap(), + value: '', + valueImports: new JavaScriptImportMap(), }) as JavaScriptTypeManifest, [ ...REGISTERED_TYPE_NODE_KINDS, + ...REGISTERED_VALUE_NODE_KINDS, 'definedTypeLinkNode', 'definedTypeNode', 'accountNode', @@ -134,6 +167,8 @@ export function getTypeManifestVisitor(input: { importFrom, serializerName ), + value: '', + valueImports: new JavaScriptImportMap(), }; }, @@ -182,6 +217,8 @@ export function getTypeManifestVisitor(input: { 'umiSerializers', 'scalarEnum' ), + value: '', + valueImports: new JavaScriptImportMap(), }; } @@ -229,6 +266,8 @@ export function getTypeManifestVisitor(input: { 'umiSerializers', ['GetDataEnumKindContent', 'GetDataEnumKind', 'dataEnum'] ), + value: '', + valueImports: new JavaScriptImportMap(), }; }, @@ -246,6 +285,8 @@ export function getTypeManifestVisitor(input: { 'umiSerializers', 'unit' ), + value: '', + valueImports: new JavaScriptImportMap(), }; }, @@ -276,6 +317,8 @@ export function getTypeManifestVisitor(input: { strictType: `{ ${kindAttribute},${type.strictType.slice(1, -1)}}`, looseType: `{ ${kindAttribute},${type.looseType.slice(1, -1)}}`, serializer: `['${name}', ${type.serializer}]`, + value: '', + valueImports: new JavaScriptImportMap(), }; }, @@ -295,6 +338,8 @@ export function getTypeManifestVisitor(input: { strictType: `Map<${key.strictType}, ${value.strictType}>`, looseType: `Map<${key.looseType}, ${value.looseType}>`, serializer: `map(${key.serializer}, ${value.serializer}${options})`, + value: '', + valueImports: new JavaScriptImportMap(), }; }, @@ -347,6 +392,8 @@ export function getTypeManifestVisitor(input: { strictType: `Set<${childManifest.strictType}>`, looseType: `Set<${childManifest.looseType}>`, serializer: `set(${childManifest.serializer + options})`, + value: '', + valueImports: new JavaScriptImportMap(), }; }, @@ -377,6 +424,8 @@ export function getTypeManifestVisitor(input: { serializer: `struct<${serializerTypeParams}>` + `([${fieldSerializers}]${structDescription})`, + value: '', + valueImports: new JavaScriptImportMap(), }; const optionalFields = structType.fields.filter( @@ -392,11 +441,11 @@ export function getTypeManifestVisitor(input: { const defaultValue = f.defaultValue as NonNullable< typeof f.defaultValue >; - const { render: renderedValue, imports } = visit( + const { value: renderedValue, valueImports } = visit( defaultValue, - valueNodeVisitor + self ); - baseManifest.serializerImports.mergeWith(imports); + baseManifest.serializerImports.mergeWith(valueImports); if (f.defaultValueStrategy === 'omitted') { return `${key}: ${renderedValue}`; } @@ -456,6 +505,7 @@ export function getTypeManifestVisitor(input: { strictType: `[${items.map((item) => item.strictType).join(', ')}]`, looseType: `[${items.map((item) => item.looseType).join(', ')}]`, serializer: `tuple([${itemSerializers}])`, + value: '', }; }, @@ -484,6 +534,8 @@ export function getTypeManifestVisitor(input: { looseImports, strictImports, serializerImports, + value: '', + valueImports: new JavaScriptImportMap(), }; }, @@ -518,6 +570,8 @@ export function getTypeManifestVisitor(input: { looseImports, serializer: `bytes(${optionsAsString})`, serializerImports, + value: '', + valueImports: new JavaScriptImportMap(), }; }, @@ -542,6 +596,8 @@ export function getTypeManifestVisitor(input: { looseImports: new JavaScriptImportMap(), serializer: `${numberType.format}(${endianness})`, serializerImports, + value: '', + valueImports: new JavaScriptImportMap(), }; }, @@ -625,6 +681,8 @@ export function getTypeManifestVisitor(input: { serializerImports: new JavaScriptImportMap() .add('umiSerializers', 'publicKey') .addAlias('umiSerializers', 'publicKey', 'publicKeySerializer'), + value: '', + valueImports: new JavaScriptImportMap(), }; }, @@ -671,6 +729,8 @@ export function getTypeManifestVisitor(input: { looseImports, serializer: `string(${optionsAsString})`, serializerImports, + value: '', + valueImports: new JavaScriptImportMap(), }; }, @@ -687,6 +747,188 @@ export function getTypeManifestVisitor(input: { parentSize = null; return manifest; }, + + visitArrayValue(node, { self }) { + const list = node.items.map((value) => visit(value, self)); + return { + ...typeManifest(), + value: `[${list.map((c) => c.value).join(', ')}]`, + valueImports: new JavaScriptImportMap().mergeWith( + ...list.map((c) => c.valueImports) + ), + }; + }, + + visitBooleanValue(node) { + return { + ...typeManifest(), + value: JSON.stringify(node.boolean), + }; + }, + + visitBytesValue(node) { + const bytes = getBytesFromBytesValueNode(node); + return { + ...typeManifest(), + value: `new Uint8Array([${Array.from(bytes).join(', ')}])`, + }; + }, + + visitConstantValue(node, { self }) { + const imports = new JavaScriptImportMap(); + const type = visit(node.type, self); + imports.mergeWith(type.serializerImports); + const value = visit(node.value, self); + imports.mergeWith(value.valueImports); + return { + ...typeManifest(), + value: `${type.serializer}.serialize(${value.value})`, + valueImports: imports, + }; + }, + + visitEnumValue(node, { self }) { + const imports = new JavaScriptImportMap(); + const enumName = pascalCase(node.enum.name); + const variantName = pascalCase(node.variant); + const importFrom = node.enum.importFrom ?? 'generatedTypes'; + + const enumNode = linkables.get(node.enum)?.type; + const isScalar = + enumNode && isNode(enumNode, 'enumTypeNode') + ? isScalarEnum(enumNode) + : !nonScalarEnums.includes(node.enum.name); + + if (!node.value && isScalar) { + return { + ...typeManifest(), + value: `${enumName}.${variantName}`, + valueImports: imports.add(importFrom, enumName), + }; + } + + const enumFn = camelCase(node.enum.name); + imports.add(importFrom, enumFn); + + if (!node.value) { + return { + ...typeManifest(), + value: `${enumFn}('${variantName}')`, + valueImports: imports, + }; + } + + const enumValue = visit(node.value, self); + const fields = enumValue.value; + imports.mergeWith(enumValue.valueImports); + + return { + ...typeManifest(), + value: `${enumFn}('${variantName}', ${fields})`, + valueImports: imports, + }; + }, + + visitMapValue(node, { self }) { + const map = node.entries.map((entry) => visit(entry, self)); + return { + ...typeManifest(), + value: `new Map([${map.map((c) => c.value).join(', ')}])`, + valueImports: new JavaScriptImportMap().mergeWith( + ...map.map((c) => c.valueImports) + ), + }; + }, + + visitMapEntryValue(node, { self }) { + const mapKey = visit(node.key, self); + const mapValue = visit(node.value, self); + return { + ...typeManifest(), + imports: mapKey.valueImports.mergeWith(mapValue.valueImports), + render: `[${mapKey.value}, ${mapValue.value}]`, + }; + }, + + visitNoneValue() { + return { + ...typeManifest(), + value: 'none()', + valueImports: new JavaScriptImportMap().add('umi', 'none'), + }; + }, + + visitNumberValue(node) { + return { + ...typeManifest(), + value: JSON.stringify(node.number), + }; + }, + + visitPublicKeyValue(node) { + return { + ...typeManifest(), + value: `publicKey("${node.publicKey}")`, + valueImports: new JavaScriptImportMap().add('umi', 'publicKey'), + }; + }, + + visitSetValue(node, { self }) { + const set = node.items.map((value) => visit(value, self)); + return { + ...typeManifest(), + value: `new Set([${set.map((c) => c.value).join(', ')}])`, + valueImports: new JavaScriptImportMap().mergeWith( + ...set.map((c) => c.valueImports) + ), + }; + }, + + visitSomeValue(node, { self }) { + const child = visit(node.value, self); + return { + ...typeManifest(), + value: `some(${child.value})`, + valueImports: child.valueImports.add('umi', 'some'), + }; + }, + + visitStringValue(node) { + return { + ...typeManifest(), + value: JSON.stringify(node.string), + }; + }, + + visitStructValue(node, { self }) { + const struct = node.fields.map((field) => visit(field, self)); + return { + ...typeManifest(), + value: `{ ${struct.map((c) => c.value).join(', ')} }`, + valueImports: new JavaScriptImportMap().mergeWith( + ...struct.map((c) => c.valueImports) + ), + }; + }, + + visitStructFieldValue(node, { self }) { + const structValue = visit(node.value, self); + return { + ...structValue, + value: `${node.name}: ${structValue.value}`, + }; + }, + + visitTupleValue(node, { self }) { + const list = node.items.map((value) => visit(value, self)); + return { + ...typeManifest(), + value: `[${list.map((c) => c.value).join(', ')}]`, + valueImports: new JavaScriptImportMap().mergeWith( + ...list.map((c) => c.valueImports) + ), + }; + }, }) ); } @@ -695,7 +937,11 @@ function mergeManifests( manifests: JavaScriptTypeManifest[] ): Pick< JavaScriptTypeManifest, - 'strictImports' | 'looseImports' | 'serializerImports' | 'isEnum' + | 'strictImports' + | 'looseImports' + | 'serializerImports' + | 'valueImports' + | 'isEnum' > { return { strictImports: new JavaScriptImportMap().mergeWith( @@ -707,6 +953,9 @@ function mergeManifests( serializerImports: new JavaScriptImportMap().mergeWith( ...manifests.map((td) => td.serializerImports) ), + valueImports: new JavaScriptImportMap().mergeWith( + ...manifests.map((td) => td.valueImports) + ), isEnum: false, }; } diff --git a/src/renderers/js/renderInstructionDefaults.ts b/src/renderers/js/renderInstructionDefaults.ts index e5ba8ea8b..f5ce2da88 100644 --- a/src/renderers/js/renderInstructionDefaults.ts +++ b/src/renderers/js/renderInstructionDefaults.ts @@ -3,11 +3,11 @@ import { camelCase, pascalCase } from '../../shared'; import { ResolvedInstructionInput, visit } from '../../visitors'; import { JavaScriptContextMap } from './JavaScriptContextMap'; import { JavaScriptImportMap } from './JavaScriptImportMap'; -import { renderValueNodeVisitor } from './renderValueNodeVisitor'; +import { getTypeManifestVisitor } from './getTypeManifestVisitor'; export function renderInstructionDefaults( input: ResolvedInstructionInput, - valueNodeVisitor: ReturnType, + typeManifestVisitor: ReturnType, optionalAccountStrategy: 'programId' | 'omitted', argObject: string ): { @@ -98,9 +98,9 @@ export function renderInstructionDefaults( seed.value.name )})`; } - const valueManifest = visit(seed.value, valueNodeVisitor); - imports.mergeWith(valueManifest.imports); - return `${seed.name}: ${valueManifest.render}`; + const valueManifest = visit(seed.value, typeManifestVisitor); + imports.mergeWith(valueManifest.valueImports); + return `${seed.name}: ${valueManifest.value}`; }); if (pdaSeeds.length > 0) { pdaArgs.push(`{ ${pdaSeeds.join(', ')} }`); @@ -164,14 +164,14 @@ export function renderInstructionDefaults( case 'conditionalValueNode': const ifTrueRenderer = renderNestedInstructionDefault( input, - valueNodeVisitor, + typeManifestVisitor, optionalAccountStrategy, defaultValue.ifTrue, argObject ); const ifFalseRenderer = renderNestedInstructionDefault( input, - valueNodeVisitor, + typeManifestVisitor, optionalAccountStrategy, defaultValue.ifFalse, argObject @@ -211,10 +211,10 @@ export function renderInstructionDefaults( ? `resolvedAccounts.${camelCase(defaultValue.condition.name)}.value` : `${argObject}.${camelCase(defaultValue.condition.name)}`; if (defaultValue.value) { - const comparedValue = visit(defaultValue.value, valueNodeVisitor); - imports.mergeWith(comparedValue.imports); + const comparedValue = visit(defaultValue.value, typeManifestVisitor); + imports.mergeWith(comparedValue.valueImports); const operator = negatedCondition ? '!==' : '==='; - condition = `${comparedInputName} ${operator} ${comparedValue.render}`; + condition = `${comparedInputName} ${operator} ${comparedValue.value}`; } else { condition = negatedCondition ? `!${comparedInputName}` @@ -238,15 +238,15 @@ export function renderInstructionDefaults( }\n}`, }; default: - const valueManifest = visit(defaultValue, valueNodeVisitor); - imports.mergeWith(valueManifest.imports); - return render(valueManifest.render); + const valueManifest = visit(defaultValue, typeManifestVisitor); + imports.mergeWith(valueManifest.valueImports); + return render(valueManifest.value); } } function renderNestedInstructionDefault( input: ResolvedInstructionInput, - valueNodeVisitor: ReturnType, + typeManifestVisitor: ReturnType, optionalAccountStrategy: 'programId' | 'omitted', defaultValue: InstructionInputValueNode | undefined, argObject: string @@ -260,7 +260,7 @@ function renderNestedInstructionDefault( if (!defaultValue) return undefined; return renderInstructionDefaults( { ...input, defaultValue }, - valueNodeVisitor, + typeManifestVisitor, optionalAccountStrategy, argObject ); diff --git a/src/renderers/js/renderValueNodeVisitor.ts b/src/renderers/js/renderValueNodeVisitor.ts deleted file mode 100644 index 5ba887908..000000000 --- a/src/renderers/js/renderValueNodeVisitor.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { - RegisteredValueNode, - getBytesFromBytesValueNode, - isNode, - isScalarEnum, -} from '../../nodes'; -import { - LinkableDictionary, - MainCaseString, - camelCase, - pascalCase, -} from '../../shared'; -import { Visitor, visit } from '../../visitors'; -import { JavaScriptImportMap } from './JavaScriptImportMap'; - -export function renderValueNodeVisitor(input: { - linkables: LinkableDictionary; - nonScalarEnums: MainCaseString[]; -}): Visitor< - { - imports: JavaScriptImportMap; - render: string; - }, - RegisteredValueNode['kind'] -> { - const { linkables, nonScalarEnums } = input; - return { - visitArrayValue(node) { - const list = node.items.map((v) => visit(v, this)); - return { - imports: new JavaScriptImportMap().mergeWith( - ...list.map((c) => c.imports) - ), - render: `[${list.map((c) => c.render).join(', ')}]`, - }; - }, - visitBooleanValue(node) { - return { - imports: new JavaScriptImportMap(), - render: JSON.stringify(node.boolean), - }; - }, - visitBytesValue(node) { - const bytes = getBytesFromBytesValueNode(node); - return { - imports: new JavaScriptImportMap(), - render: `new Uint8Array([${Array.from(bytes).join(', ')}])`, - }; - }, - visitConstantValue() { - throw new Error('Not implemented'); - }, - visitEnumValue(node) { - const imports = new JavaScriptImportMap(); - const enumName = pascalCase(node.enum.name); - const variantName = pascalCase(node.variant); - const importFrom = node.enum.importFrom ?? 'generatedTypes'; - - const enumNode = linkables.get(node.enum)?.type; - const isScalar = - enumNode && isNode(enumNode, 'enumTypeNode') - ? isScalarEnum(enumNode) - : !nonScalarEnums.includes(node.enum.name); - - if (!node.value && isScalar) { - return { - imports: imports.add(importFrom, enumName), - render: `${enumName}.${variantName}`, - }; - } - - const enumFn = camelCase(node.enum.name); - imports.add(importFrom, enumFn); - - if (!node.value) { - return { imports, render: `${enumFn}('${variantName}')` }; - } - - const enumValue = visit(node.value, this); - const fields = enumValue.render; - imports.mergeWith(enumValue.imports); - - return { - imports, - render: `${enumFn}('${variantName}', ${fields})`, - }; - }, - visitMapValue(node) { - const map = node.entries.map((entry) => visit(entry, this)); - return { - imports: new JavaScriptImportMap().mergeWith( - ...map.map((c) => c.imports) - ), - render: `new Map([${map.map((c) => c.render).join(', ')}])`, - }; - }, - visitMapEntryValue(node) { - const mapKey = visit(node.key, this); - const mapValue = visit(node.value, this); - return { - imports: mapKey.imports.mergeWith(mapValue.imports), - render: `[${mapKey.render}, ${mapValue.render}]`, - }; - }, - visitNoneValue() { - return { - imports: new JavaScriptImportMap().add('umi', 'none'), - render: 'none()', - }; - }, - visitNumberValue(node) { - return { - imports: new JavaScriptImportMap(), - render: JSON.stringify(node.number), - }; - }, - visitPublicKeyValue(node) { - return { - imports: new JavaScriptImportMap().add('umi', 'publicKey'), - render: `publicKey("${node.publicKey}")`, - }; - }, - visitSetValue(node) { - const set = node.items.map((v) => visit(v, this)); - return { - imports: new JavaScriptImportMap().mergeWith( - ...set.map((c) => c.imports) - ), - render: `new Set([${set.map((c) => c.render).join(', ')}])`, - }; - }, - visitSomeValue(node) { - const child = visit(node.value, this); - return { - imports: child.imports.add('umi', 'some'), - render: `some(${child.render})`, - }; - }, - visitStringValue(node) { - return { - imports: new JavaScriptImportMap(), - render: JSON.stringify(node.string), - }; - }, - visitStructValue(node) { - const struct = node.fields.map((field) => visit(field, this)); - return { - imports: new JavaScriptImportMap().mergeWith( - ...struct.map((c) => c.imports) - ), - render: `{ ${struct.map((c) => c.render).join(', ')} }`, - }; - }, - visitStructFieldValue(node) { - const structValue = visit(node.value, this); - return { - imports: structValue.imports, - render: `${node.name}: ${structValue.render}`, - }; - }, - visitTupleValue(node) { - const list = node.items.map((v) => visit(v, this)); - return { - imports: new JavaScriptImportMap().mergeWith( - ...list.map((c) => c.imports) - ), - render: `[${list.map((c) => c.render).join(', ')}]`, - }; - }, - }; -} diff --git a/src/renderers/rust/renderValueNodeVisitor.ts b/src/renderers/rust/renderValueNodeVisitor.ts index a685376c5..0dffe76c0 100644 --- a/src/renderers/rust/renderValueNodeVisitor.ts +++ b/src/renderers/rust/renderValueNodeVisitor.ts @@ -2,7 +2,9 @@ import { RegisteredValueNode, ValueNode, arrayValueNode, + bytesValueNode, getBytesFromBytesValueNode, + isNode, numberValueNode, } from '../../nodes'; import { pascalCase } from '../../shared'; @@ -42,11 +44,33 @@ export function renderValueNodeVisitor(useStr: boolean = false): Visitor< }, visitBytesValue(node) { const bytes = getBytesFromBytesValueNode(node); - const numbers = Array.from(bytes).map((b) => numberValueNode(b)); + const numbers = Array.from(bytes).map(numberValueNode); return visit(arrayValueNode(numbers), this); }, - visitConstantValue() { - throw new Error('Not implemented'); + visitConstantValue(node) { + if (isNode(node.value, 'bytesValueNode')) { + return visit(node.value, this); + } + if ( + isNode(node.type, 'stringTypeNode') && + isNode(node.value, 'stringValueNode') + ) { + return visit( + bytesValueNode(node.type.encoding, node.value.string), + this + ); + } + if ( + isNode(node.type, 'numberTypeNode') && + isNode(node.value, 'numberValueNode') + ) { + const numberManifest = visit(node.value, this); + const { format, endian } = node.type; + const byteFunction = endian === 'le' ? 'to_le_bytes' : 'to_be_bytes'; + numberManifest.render = `${numberManifest.render}${format}.${byteFunction}()`; + return numberManifest; + } + throw new Error('Unsupported constant value type.'); }, visitEnumValue(node) { const imports = new RustImportMap(); @@ -89,7 +113,7 @@ export function renderValueNodeVisitor(useStr: boolean = false): Visitor< visitNumberValue(node) { return { imports: new RustImportMap(), - render: JSON.stringify(node.number), + render: node.number.toString(), }; }, visitPublicKeyValue(node) {