codama-renderers-dart 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/package.json +61 -0
- package/readme.md +297 -0
- package/src/fragments/accountPage.ts +201 -0
- package/src/fragments/discriminatorConstants.ts +74 -0
- package/src/fragments/errorPage.ts +82 -0
- package/src/fragments/index.ts +8 -0
- package/src/fragments/indexPage.ts +44 -0
- package/src/fragments/instructionPage.ts +306 -0
- package/src/fragments/pdaPage.ts +128 -0
- package/src/fragments/programPage.ts +63 -0
- package/src/fragments/typePage.ts +554 -0
- package/src/index.ts +13 -0
- package/src/types/global.d.ts +2 -0
- package/src/utils/codecs.ts +19 -0
- package/src/utils/formatCode.ts +35 -0
- package/src/utils/fragment.ts +83 -0
- package/src/utils/importMap.ts +129 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/nameTransformers.ts +112 -0
- package/src/utils/options.ts +46 -0
- package/src/utils/typeManifest.ts +31 -0
- package/src/visitors/getRenderMapVisitor.ts +272 -0
- package/src/visitors/getTypeManifestVisitor.ts +794 -0
- package/src/visitors/index.ts +3 -0
- package/src/visitors/renderVisitor.ts +154 -0
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AccountNode,
|
|
3
|
+
type AmountTypeNode,
|
|
4
|
+
type ArrayTypeNode,
|
|
5
|
+
type BooleanTypeNode,
|
|
6
|
+
type BytesTypeNode,
|
|
7
|
+
type CamelCaseString,
|
|
8
|
+
type DateTimeTypeNode,
|
|
9
|
+
type DefinedTypeLinkNode,
|
|
10
|
+
type DefinedTypeNode,
|
|
11
|
+
type EnumEmptyVariantTypeNode,
|
|
12
|
+
type EnumStructVariantTypeNode,
|
|
13
|
+
type EnumTupleVariantTypeNode,
|
|
14
|
+
type EnumTypeNode,
|
|
15
|
+
type FixedSizeTypeNode,
|
|
16
|
+
type HiddenPrefixTypeNode,
|
|
17
|
+
type HiddenSuffixTypeNode,
|
|
18
|
+
type InstructionNode,
|
|
19
|
+
type MapTypeNode,
|
|
20
|
+
type NumberTypeNode,
|
|
21
|
+
type OptionTypeNode,
|
|
22
|
+
type PostOffsetTypeNode,
|
|
23
|
+
type PreOffsetTypeNode,
|
|
24
|
+
type PublicKeyTypeNode,
|
|
25
|
+
type RemainderOptionTypeNode,
|
|
26
|
+
type SentinelTypeNode,
|
|
27
|
+
type SetTypeNode,
|
|
28
|
+
type SizePrefixTypeNode,
|
|
29
|
+
type SolAmountTypeNode,
|
|
30
|
+
type StringTypeNode,
|
|
31
|
+
type StructFieldTypeNode,
|
|
32
|
+
type StructTypeNode,
|
|
33
|
+
type TupleTypeNode,
|
|
34
|
+
type ZeroableOptionTypeNode,
|
|
35
|
+
isNode,
|
|
36
|
+
resolveNestedTypeNode,
|
|
37
|
+
} from "@codama/nodes";
|
|
38
|
+
import {
|
|
39
|
+
type LinkableDictionary,
|
|
40
|
+
type NodeStack,
|
|
41
|
+
type Visitor,
|
|
42
|
+
extendVisitor,
|
|
43
|
+
staticVisitor,
|
|
44
|
+
visit,
|
|
45
|
+
} from "@codama/visitors-core";
|
|
46
|
+
import { CODAMA_ERROR__UNEXPECTED_NODE_KIND } from "@codama/errors";
|
|
47
|
+
import { CodamaError } from "@codama/errors";
|
|
48
|
+
|
|
49
|
+
import {
|
|
50
|
+
type Fragment,
|
|
51
|
+
type TypeManifest,
|
|
52
|
+
emptyTypeManifest,
|
|
53
|
+
fragment,
|
|
54
|
+
fragmentFromString,
|
|
55
|
+
use,
|
|
56
|
+
} from "../utils/index.js";
|
|
57
|
+
import { type DartNameApi, snakeCase } from "../utils/nameTransformers.js";
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Type alias for the type manifest visitor.
|
|
61
|
+
*/
|
|
62
|
+
export type TypeManifestVisitor = ReturnType<typeof getTypeManifestVisitor>;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Creates a visitor that maps Codama type/value nodes to TypeManifest objects.
|
|
66
|
+
* Each TypeManifest contains Dart type, encoder, decoder, and value expressions.
|
|
67
|
+
*/
|
|
68
|
+
export function getTypeManifestVisitor(input: {
|
|
69
|
+
nameApi: DartNameApi;
|
|
70
|
+
linkables: LinkableDictionary;
|
|
71
|
+
stack: NodeStack;
|
|
72
|
+
nonScalarEnums?: CamelCaseString[];
|
|
73
|
+
}) {
|
|
74
|
+
const { nameApi, linkables, nonScalarEnums = [] } = input;
|
|
75
|
+
|
|
76
|
+
const baseVisitor = staticVisitor<TypeManifest>(
|
|
77
|
+
(node): TypeManifest => {
|
|
78
|
+
throw new CodamaError(CODAMA_ERROR__UNEXPECTED_NODE_KIND, {
|
|
79
|
+
expectedKinds: ["typeNode"],
|
|
80
|
+
kind: (node as { kind: string }).kind,
|
|
81
|
+
} as never);
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
keys: [
|
|
85
|
+
// Type nodes
|
|
86
|
+
"numberTypeNode",
|
|
87
|
+
"booleanTypeNode",
|
|
88
|
+
"stringTypeNode",
|
|
89
|
+
"publicKeyTypeNode",
|
|
90
|
+
"bytesTypeNode",
|
|
91
|
+
"arrayTypeNode",
|
|
92
|
+
"mapTypeNode",
|
|
93
|
+
"setTypeNode",
|
|
94
|
+
"tupleTypeNode",
|
|
95
|
+
"structTypeNode",
|
|
96
|
+
"structFieldTypeNode",
|
|
97
|
+
"enumTypeNode",
|
|
98
|
+
"enumEmptyVariantTypeNode",
|
|
99
|
+
"enumStructVariantTypeNode",
|
|
100
|
+
"enumTupleVariantTypeNode",
|
|
101
|
+
"optionTypeNode",
|
|
102
|
+
"remainderOptionTypeNode",
|
|
103
|
+
"zeroableOptionTypeNode",
|
|
104
|
+
"fixedSizeTypeNode",
|
|
105
|
+
"sizePrefixTypeNode",
|
|
106
|
+
"hiddenPrefixTypeNode",
|
|
107
|
+
"hiddenSuffixTypeNode",
|
|
108
|
+
"sentinelTypeNode",
|
|
109
|
+
"preOffsetTypeNode",
|
|
110
|
+
"postOffsetTypeNode",
|
|
111
|
+
"solAmountTypeNode",
|
|
112
|
+
"amountTypeNode",
|
|
113
|
+
"dateTimeTypeNode",
|
|
114
|
+
"definedTypeLinkNode",
|
|
115
|
+
// Context nodes (for links)
|
|
116
|
+
"definedTypeNode",
|
|
117
|
+
"accountNode",
|
|
118
|
+
"instructionNode",
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
return extendVisitor(baseVisitor, {
|
|
124
|
+
visitNumberType(node: NumberTypeNode, { self }) {
|
|
125
|
+
const { format, endian } = node;
|
|
126
|
+
const endianSuffix =
|
|
127
|
+
endian === "be"
|
|
128
|
+
? ", NumberCodecConfig(endian: Endian.big)"
|
|
129
|
+
: "";
|
|
130
|
+
const endianDecoderSuffix =
|
|
131
|
+
endian === "be"
|
|
132
|
+
? ", NumberCodecConfig(endian: Endian.big)"
|
|
133
|
+
: "";
|
|
134
|
+
|
|
135
|
+
// Map format to Dart type and codec
|
|
136
|
+
const dartType = getNumberDartType(format);
|
|
137
|
+
const codecName = getNumberCodecName(format);
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
type: dartType === "BigInt"
|
|
141
|
+
? use("BigInt", "dartTypedData")
|
|
142
|
+
: fragmentFromString(dartType),
|
|
143
|
+
encoder: fragment`${use(
|
|
144
|
+
`get${codecName}Encoder`,
|
|
145
|
+
"solanaCodecsNumbers",
|
|
146
|
+
)}(${endianSuffix ? fragmentFromString(endianSuffix.slice(2)) : fragmentFromString("")})`,
|
|
147
|
+
decoder: fragment`${use(
|
|
148
|
+
`get${codecName}Decoder`,
|
|
149
|
+
"solanaCodecsNumbers",
|
|
150
|
+
)}(${endianDecoderSuffix ? fragmentFromString(endianDecoderSuffix.slice(2)) : fragmentFromString("")})`,
|
|
151
|
+
value: emptyTypeManifest().value,
|
|
152
|
+
isEnum: false,
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
visitBooleanType(_node: BooleanTypeNode, { self }) {
|
|
157
|
+
return {
|
|
158
|
+
type: fragmentFromString("bool"),
|
|
159
|
+
encoder: fragment`${use(
|
|
160
|
+
"getBooleanEncoder",
|
|
161
|
+
"solanaCodecsDataStructures",
|
|
162
|
+
)}()`,
|
|
163
|
+
decoder: fragment`${use(
|
|
164
|
+
"getBooleanDecoder",
|
|
165
|
+
"solanaCodecsDataStructures",
|
|
166
|
+
)}()`,
|
|
167
|
+
value: emptyTypeManifest().value,
|
|
168
|
+
isEnum: false,
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
visitStringType(node: StringTypeNode, { self }) {
|
|
173
|
+
const { encoding } = node;
|
|
174
|
+
const codecMap: Record<string, string> = {
|
|
175
|
+
utf8: "Utf8",
|
|
176
|
+
base58: "Base58",
|
|
177
|
+
base64: "Base64",
|
|
178
|
+
base16: "Base16",
|
|
179
|
+
};
|
|
180
|
+
const codecName = codecMap[encoding] ?? "Utf8";
|
|
181
|
+
return {
|
|
182
|
+
type: fragmentFromString("String"),
|
|
183
|
+
encoder: fragment`${use(
|
|
184
|
+
`get${codecName}Encoder`,
|
|
185
|
+
"solanaCodecsStrings",
|
|
186
|
+
)}()`,
|
|
187
|
+
decoder: fragment`${use(
|
|
188
|
+
`get${codecName}Decoder`,
|
|
189
|
+
"solanaCodecsStrings",
|
|
190
|
+
)}()`,
|
|
191
|
+
value: emptyTypeManifest().value,
|
|
192
|
+
isEnum: false,
|
|
193
|
+
};
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
visitPublicKeyType(_node: PublicKeyTypeNode, { self }) {
|
|
197
|
+
return {
|
|
198
|
+
type: fragment`${use("Address", "solanaAddresses")}`,
|
|
199
|
+
encoder: fragment`${use(
|
|
200
|
+
"getAddressEncoder",
|
|
201
|
+
"solanaAddresses",
|
|
202
|
+
)}()`,
|
|
203
|
+
decoder: fragment`${use(
|
|
204
|
+
"getAddressDecoder",
|
|
205
|
+
"solanaAddresses",
|
|
206
|
+
)}()`,
|
|
207
|
+
value: emptyTypeManifest().value,
|
|
208
|
+
isEnum: false,
|
|
209
|
+
};
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
visitBytesType(_node: BytesTypeNode, { self }) {
|
|
213
|
+
return {
|
|
214
|
+
type: fragment`${use("Uint8List", "dartTypedData")}`,
|
|
215
|
+
encoder: fragment`${use(
|
|
216
|
+
"getBytesEncoder",
|
|
217
|
+
"solanaCodecsDataStructures",
|
|
218
|
+
)}()`,
|
|
219
|
+
decoder: fragment`${use(
|
|
220
|
+
"getBytesDecoder",
|
|
221
|
+
"solanaCodecsDataStructures",
|
|
222
|
+
)}()`,
|
|
223
|
+
value: emptyTypeManifest().value,
|
|
224
|
+
isEnum: false,
|
|
225
|
+
};
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
visitArrayType(node: ArrayTypeNode, { self }) {
|
|
229
|
+
const itemManifest = visit(node.item, self);
|
|
230
|
+
const sizeExpr = getArraySizeExpression(node);
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
type: fragment`List<${itemManifest.type}>`,
|
|
234
|
+
encoder: fragment`${use(
|
|
235
|
+
"getArrayEncoder",
|
|
236
|
+
"solanaCodecsDataStructures",
|
|
237
|
+
)}(${itemManifest.encoder}${sizeExpr})`,
|
|
238
|
+
decoder: fragment`${use(
|
|
239
|
+
"getArrayDecoder",
|
|
240
|
+
"solanaCodecsDataStructures",
|
|
241
|
+
)}(${itemManifest.decoder}${sizeExpr})`,
|
|
242
|
+
value: emptyTypeManifest().value,
|
|
243
|
+
isEnum: false,
|
|
244
|
+
};
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
visitMapType(node: MapTypeNode, { self }) {
|
|
248
|
+
const keyManifest = visit(node.key, self);
|
|
249
|
+
const valueManifest = visit(node.value, self);
|
|
250
|
+
const sizeExpr = getMapSizeExpression(node);
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
type: fragment`Map<${keyManifest.type}, ${valueManifest.type}>`,
|
|
254
|
+
encoder: fragment`${use(
|
|
255
|
+
"getMapEncoder",
|
|
256
|
+
"solanaCodecsDataStructures",
|
|
257
|
+
)}(${keyManifest.encoder}, ${valueManifest.encoder}${sizeExpr})`,
|
|
258
|
+
decoder: fragment`${use(
|
|
259
|
+
"getMapDecoder",
|
|
260
|
+
"solanaCodecsDataStructures",
|
|
261
|
+
)}(${keyManifest.decoder}, ${valueManifest.decoder}${sizeExpr})`,
|
|
262
|
+
value: emptyTypeManifest().value,
|
|
263
|
+
isEnum: false,
|
|
264
|
+
};
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
visitSetType(node: SetTypeNode, { self }) {
|
|
268
|
+
const itemManifest = visit(node.item, self);
|
|
269
|
+
const sizeExpr = getSetSizeExpression(node);
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
type: fragment`Set<${itemManifest.type}>`,
|
|
273
|
+
encoder: fragment`${use(
|
|
274
|
+
"getSetEncoder",
|
|
275
|
+
"solanaCodecsDataStructures",
|
|
276
|
+
)}(${itemManifest.encoder}${sizeExpr})`,
|
|
277
|
+
decoder: fragment`${use(
|
|
278
|
+
"getSetDecoder",
|
|
279
|
+
"solanaCodecsDataStructures",
|
|
280
|
+
)}(${itemManifest.decoder}${sizeExpr})`,
|
|
281
|
+
value: emptyTypeManifest().value,
|
|
282
|
+
isEnum: false,
|
|
283
|
+
};
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
visitTupleType(node: TupleTypeNode, { self }) {
|
|
287
|
+
const itemManifests = node.items.map((item) => visit(item, self));
|
|
288
|
+
const typeList = itemManifests
|
|
289
|
+
.map((m) => m.type.content)
|
|
290
|
+
.join(", ");
|
|
291
|
+
const encoderList = itemManifests.map((m) => m.encoder);
|
|
292
|
+
const decoderList = itemManifests.map((m) => m.decoder);
|
|
293
|
+
|
|
294
|
+
// Merge imports from all items
|
|
295
|
+
const typeFragment = fragment`(${fragmentFromString(typeList)})`;
|
|
296
|
+
for (const m of itemManifests) {
|
|
297
|
+
typeFragment.imports.mergeWith(m.type.imports);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const encoderListStr = encoderList.map((e) => e.content).join(", ");
|
|
301
|
+
const encoderFrag = fragment`${use(
|
|
302
|
+
"getTupleEncoder",
|
|
303
|
+
"solanaCodecsDataStructures",
|
|
304
|
+
)}([${fragmentFromString(encoderListStr)}])`;
|
|
305
|
+
for (const e of encoderList) {
|
|
306
|
+
encoderFrag.imports.mergeWith(e.imports);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const decoderListStr = decoderList.map((d) => d.content).join(", ");
|
|
310
|
+
const decoderFrag = fragment`${use(
|
|
311
|
+
"getTupleDecoder",
|
|
312
|
+
"solanaCodecsDataStructures",
|
|
313
|
+
)}([${fragmentFromString(decoderListStr)}])`;
|
|
314
|
+
for (const d of decoderList) {
|
|
315
|
+
decoderFrag.imports.mergeWith(d.imports);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return {
|
|
319
|
+
type: typeFragment,
|
|
320
|
+
encoder: encoderFrag,
|
|
321
|
+
decoder: decoderFrag,
|
|
322
|
+
value: emptyTypeManifest().value,
|
|
323
|
+
isEnum: false,
|
|
324
|
+
};
|
|
325
|
+
},
|
|
326
|
+
|
|
327
|
+
visitStructType(node: StructTypeNode, { self }) {
|
|
328
|
+
const fields = node.fields.map((f) => visit(f, self));
|
|
329
|
+
const fieldNames = node.fields.map((f) => f.name as string);
|
|
330
|
+
|
|
331
|
+
// Type: Map<String, Object?> (for inline structs without a name)
|
|
332
|
+
const typeStr = "Map<String, Object?>";
|
|
333
|
+
|
|
334
|
+
// Build encoder fields list
|
|
335
|
+
const encoderFields = fields
|
|
336
|
+
.map((f, i) => `('${fieldNames[i]}', ${f.encoder.content})`)
|
|
337
|
+
.join(", ");
|
|
338
|
+
const encoderFrag = fragment`${use(
|
|
339
|
+
"getStructEncoder",
|
|
340
|
+
"solanaCodecsDataStructures",
|
|
341
|
+
)}([${fragmentFromString(encoderFields)}])`;
|
|
342
|
+
for (const f of fields) {
|
|
343
|
+
encoderFrag.imports.mergeWith(f.encoder.imports);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const decoderFields = fields
|
|
347
|
+
.map((f, i) => `('${fieldNames[i]}', ${f.decoder.content})`)
|
|
348
|
+
.join(", ");
|
|
349
|
+
const decoderFrag = fragment`${use(
|
|
350
|
+
"getStructDecoder",
|
|
351
|
+
"solanaCodecsDataStructures",
|
|
352
|
+
)}([${fragmentFromString(decoderFields)}])`;
|
|
353
|
+
for (const f of fields) {
|
|
354
|
+
decoderFrag.imports.mergeWith(f.decoder.imports);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
type: fragmentFromString(typeStr),
|
|
359
|
+
encoder: encoderFrag,
|
|
360
|
+
decoder: decoderFrag,
|
|
361
|
+
value: emptyTypeManifest().value,
|
|
362
|
+
isEnum: false,
|
|
363
|
+
};
|
|
364
|
+
},
|
|
365
|
+
|
|
366
|
+
visitStructFieldType(node: StructFieldTypeNode, { self }) {
|
|
367
|
+
return visit(node.type, self);
|
|
368
|
+
},
|
|
369
|
+
|
|
370
|
+
visitEnumType(node: EnumTypeNode, { self }) {
|
|
371
|
+
// Check if this is a scalar enum (all empty variants)
|
|
372
|
+
const isScalar = node.variants.every(
|
|
373
|
+
(v) => v.kind === "enumEmptyVariantTypeNode",
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
if (isScalar) {
|
|
377
|
+
// Scalar enums use a simple int-based encoder
|
|
378
|
+
return {
|
|
379
|
+
type: emptyTypeManifest().type, // Will be replaced by the named type
|
|
380
|
+
encoder: emptyTypeManifest().encoder,
|
|
381
|
+
decoder: emptyTypeManifest().decoder,
|
|
382
|
+
value: emptyTypeManifest().value,
|
|
383
|
+
isEnum: true,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Data enum — uses discriminated union
|
|
388
|
+
return {
|
|
389
|
+
type: emptyTypeManifest().type,
|
|
390
|
+
encoder: emptyTypeManifest().encoder,
|
|
391
|
+
decoder: emptyTypeManifest().decoder,
|
|
392
|
+
value: emptyTypeManifest().value,
|
|
393
|
+
isEnum: false,
|
|
394
|
+
};
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
visitEnumEmptyVariantType(
|
|
398
|
+
_node: EnumEmptyVariantTypeNode,
|
|
399
|
+
{ self },
|
|
400
|
+
) {
|
|
401
|
+
return emptyTypeManifest();
|
|
402
|
+
},
|
|
403
|
+
|
|
404
|
+
visitEnumStructVariantType(
|
|
405
|
+
node: EnumStructVariantTypeNode,
|
|
406
|
+
{ self },
|
|
407
|
+
) {
|
|
408
|
+
return visit(node.struct, self);
|
|
409
|
+
},
|
|
410
|
+
|
|
411
|
+
visitEnumTupleVariantType(
|
|
412
|
+
node: EnumTupleVariantTypeNode,
|
|
413
|
+
{ self },
|
|
414
|
+
) {
|
|
415
|
+
return visit(node.tuple, self);
|
|
416
|
+
},
|
|
417
|
+
|
|
418
|
+
visitOptionType(node: OptionTypeNode, { self }) {
|
|
419
|
+
const itemManifest = visit(node.item, self);
|
|
420
|
+
const resolvedPrefix = resolveNestedTypeNode(node.prefix);
|
|
421
|
+
const prefixExpr = resolvedPrefix.format !== "u8"
|
|
422
|
+
? `, prefix: ${getNumberCodecExpression(resolvedPrefix.format, "Encoder")}`
|
|
423
|
+
: "";
|
|
424
|
+
const prefixDecoderExpr = resolvedPrefix.format !== "u8"
|
|
425
|
+
? `, prefix: ${getNumberCodecExpression(resolvedPrefix.format, "Decoder")}`
|
|
426
|
+
: "";
|
|
427
|
+
|
|
428
|
+
return {
|
|
429
|
+
type: fragment`${itemManifest.type}?`,
|
|
430
|
+
encoder: fragment`${use(
|
|
431
|
+
"getNullableEncoder",
|
|
432
|
+
"solanaCodecsDataStructures",
|
|
433
|
+
)}(${itemManifest.encoder}${fragmentFromString(prefixExpr)})`,
|
|
434
|
+
decoder: fragment`${use(
|
|
435
|
+
"getNullableDecoder",
|
|
436
|
+
"solanaCodecsDataStructures",
|
|
437
|
+
)}(${itemManifest.decoder}${fragmentFromString(prefixDecoderExpr)})`,
|
|
438
|
+
value: emptyTypeManifest().value,
|
|
439
|
+
isEnum: false,
|
|
440
|
+
};
|
|
441
|
+
},
|
|
442
|
+
|
|
443
|
+
visitRemainderOptionType(
|
|
444
|
+
node: RemainderOptionTypeNode,
|
|
445
|
+
{ self },
|
|
446
|
+
) {
|
|
447
|
+
const itemManifest = visit(node.item, self);
|
|
448
|
+
|
|
449
|
+
return {
|
|
450
|
+
type: fragment`${itemManifest.type}?`,
|
|
451
|
+
encoder: fragment`${use(
|
|
452
|
+
"getNullableEncoder",
|
|
453
|
+
"solanaCodecsDataStructures",
|
|
454
|
+
)}(${itemManifest.encoder}, hasPrefix: false)`,
|
|
455
|
+
decoder: fragment`${use(
|
|
456
|
+
"getNullableDecoder",
|
|
457
|
+
"solanaCodecsDataStructures",
|
|
458
|
+
)}(${itemManifest.decoder}, hasPrefix: false)`,
|
|
459
|
+
value: emptyTypeManifest().value,
|
|
460
|
+
isEnum: false,
|
|
461
|
+
};
|
|
462
|
+
},
|
|
463
|
+
|
|
464
|
+
visitZeroableOptionType(
|
|
465
|
+
node: ZeroableOptionTypeNode,
|
|
466
|
+
{ self },
|
|
467
|
+
) {
|
|
468
|
+
const itemManifest = visit(node.item, self);
|
|
469
|
+
|
|
470
|
+
return {
|
|
471
|
+
type: fragment`${itemManifest.type}?`,
|
|
472
|
+
encoder: fragment`${use(
|
|
473
|
+
"getNullableEncoder",
|
|
474
|
+
"solanaCodecsDataStructures",
|
|
475
|
+
)}(${itemManifest.encoder}, hasPrefix: false, noneValue: const ZeroesNoneValue())`,
|
|
476
|
+
decoder: fragment`${use(
|
|
477
|
+
"getNullableDecoder",
|
|
478
|
+
"solanaCodecsDataStructures",
|
|
479
|
+
)}(${itemManifest.decoder}, hasPrefix: false, noneValue: const ZeroesNoneValue())`,
|
|
480
|
+
value: emptyTypeManifest().value,
|
|
481
|
+
isEnum: false,
|
|
482
|
+
};
|
|
483
|
+
},
|
|
484
|
+
|
|
485
|
+
visitFixedSizeType(node: FixedSizeTypeNode, { self }) {
|
|
486
|
+
const innerManifest = visit(node.type, self);
|
|
487
|
+
|
|
488
|
+
return {
|
|
489
|
+
type: innerManifest.type,
|
|
490
|
+
encoder: fragment`${use(
|
|
491
|
+
"fixEncoderSize",
|
|
492
|
+
"solanaCodecsCore",
|
|
493
|
+
)}(${innerManifest.encoder}, ${fragmentFromString(String(node.size))})`,
|
|
494
|
+
decoder: fragment`${use(
|
|
495
|
+
"fixDecoderSize",
|
|
496
|
+
"solanaCodecsCore",
|
|
497
|
+
)}(${innerManifest.decoder}, ${fragmentFromString(String(node.size))})`,
|
|
498
|
+
value: innerManifest.value,
|
|
499
|
+
isEnum: innerManifest.isEnum,
|
|
500
|
+
};
|
|
501
|
+
},
|
|
502
|
+
|
|
503
|
+
visitSizePrefixType(node: SizePrefixTypeNode, { self }) {
|
|
504
|
+
const innerManifest = visit(node.type, self);
|
|
505
|
+
const prefixManifest = visit(node.prefix, self);
|
|
506
|
+
|
|
507
|
+
return {
|
|
508
|
+
type: innerManifest.type,
|
|
509
|
+
encoder: fragment`${use(
|
|
510
|
+
"addEncoderSizePrefix",
|
|
511
|
+
"solanaCodecsCore",
|
|
512
|
+
)}(${innerManifest.encoder}, ${prefixManifest.encoder})`,
|
|
513
|
+
decoder: fragment`${use(
|
|
514
|
+
"addDecoderSizePrefix",
|
|
515
|
+
"solanaCodecsCore",
|
|
516
|
+
)}(${innerManifest.decoder}, ${prefixManifest.decoder})`,
|
|
517
|
+
value: innerManifest.value,
|
|
518
|
+
isEnum: innerManifest.isEnum,
|
|
519
|
+
};
|
|
520
|
+
},
|
|
521
|
+
|
|
522
|
+
visitHiddenPrefixType(node: HiddenPrefixTypeNode, { self }) {
|
|
523
|
+
const innerManifest = visit(node.type, self);
|
|
524
|
+
const prefixes = node.prefix.map((p) => visit(p, self));
|
|
525
|
+
const prefixEncoders = prefixes
|
|
526
|
+
.map((p) => p.encoder.content)
|
|
527
|
+
.join(", ");
|
|
528
|
+
const prefixDecoders = prefixes
|
|
529
|
+
.map((p) => p.decoder.content)
|
|
530
|
+
.join(", ");
|
|
531
|
+
|
|
532
|
+
const encoderFrag = fragment`${use(
|
|
533
|
+
"getHiddenPrefixEncoder",
|
|
534
|
+
"solanaCodecsDataStructures",
|
|
535
|
+
)}(${innerManifest.encoder}, [${fragmentFromString(prefixEncoders)}])`;
|
|
536
|
+
for (const p of prefixes) {
|
|
537
|
+
encoderFrag.imports.mergeWith(p.encoder.imports);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const decoderFrag = fragment`${use(
|
|
541
|
+
"getHiddenPrefixDecoder",
|
|
542
|
+
"solanaCodecsDataStructures",
|
|
543
|
+
)}(${innerManifest.decoder}, [${fragmentFromString(prefixDecoders)}])`;
|
|
544
|
+
for (const p of prefixes) {
|
|
545
|
+
decoderFrag.imports.mergeWith(p.decoder.imports);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return {
|
|
549
|
+
type: innerManifest.type,
|
|
550
|
+
encoder: encoderFrag,
|
|
551
|
+
decoder: decoderFrag,
|
|
552
|
+
value: innerManifest.value,
|
|
553
|
+
isEnum: innerManifest.isEnum,
|
|
554
|
+
};
|
|
555
|
+
},
|
|
556
|
+
|
|
557
|
+
visitHiddenSuffixType(node: HiddenSuffixTypeNode, { self }) {
|
|
558
|
+
const innerManifest = visit(node.type, self);
|
|
559
|
+
const suffixes = node.suffix.map((s) => visit(s, self));
|
|
560
|
+
const suffixEncoders = suffixes
|
|
561
|
+
.map((s) => s.encoder.content)
|
|
562
|
+
.join(", ");
|
|
563
|
+
const suffixDecoders = suffixes
|
|
564
|
+
.map((s) => s.decoder.content)
|
|
565
|
+
.join(", ");
|
|
566
|
+
|
|
567
|
+
const encoderFrag = fragment`${use(
|
|
568
|
+
"getHiddenSuffixEncoder",
|
|
569
|
+
"solanaCodecsDataStructures",
|
|
570
|
+
)}(${innerManifest.encoder}, [${fragmentFromString(suffixEncoders)}])`;
|
|
571
|
+
for (const s of suffixes) {
|
|
572
|
+
encoderFrag.imports.mergeWith(s.encoder.imports);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const decoderFrag = fragment`${use(
|
|
576
|
+
"getHiddenSuffixDecoder",
|
|
577
|
+
"solanaCodecsDataStructures",
|
|
578
|
+
)}(${innerManifest.decoder}, [${fragmentFromString(suffixDecoders)}])`;
|
|
579
|
+
for (const s of suffixes) {
|
|
580
|
+
decoderFrag.imports.mergeWith(s.decoder.imports);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
return {
|
|
584
|
+
type: innerManifest.type,
|
|
585
|
+
encoder: encoderFrag,
|
|
586
|
+
decoder: decoderFrag,
|
|
587
|
+
value: innerManifest.value,
|
|
588
|
+
isEnum: innerManifest.isEnum,
|
|
589
|
+
};
|
|
590
|
+
},
|
|
591
|
+
|
|
592
|
+
visitSentinelType(node: SentinelTypeNode, { self }) {
|
|
593
|
+
const innerManifest = visit(node.type, self);
|
|
594
|
+
const sentinelManifest = visit(node.sentinel, self);
|
|
595
|
+
|
|
596
|
+
return {
|
|
597
|
+
type: innerManifest.type,
|
|
598
|
+
encoder: fragment`${use(
|
|
599
|
+
"addEncoderSentinel",
|
|
600
|
+
"solanaCodecsCore",
|
|
601
|
+
)}(${innerManifest.encoder}, ${sentinelManifest.encoder})`,
|
|
602
|
+
decoder: fragment`${use(
|
|
603
|
+
"addDecoderSentinel",
|
|
604
|
+
"solanaCodecsCore",
|
|
605
|
+
)}(${innerManifest.decoder}, ${sentinelManifest.decoder})`,
|
|
606
|
+
value: innerManifest.value,
|
|
607
|
+
isEnum: innerManifest.isEnum,
|
|
608
|
+
};
|
|
609
|
+
},
|
|
610
|
+
|
|
611
|
+
visitPreOffsetType(node: PreOffsetTypeNode, { self }) {
|
|
612
|
+
// Pre-offset types pass through to inner type in Dart
|
|
613
|
+
return visit(node.type, self);
|
|
614
|
+
},
|
|
615
|
+
|
|
616
|
+
visitPostOffsetType(node: PostOffsetTypeNode, { self }) {
|
|
617
|
+
// Post-offset types pass through to inner type in Dart
|
|
618
|
+
return visit(node.type, self);
|
|
619
|
+
},
|
|
620
|
+
|
|
621
|
+
visitSolAmountType(node: SolAmountTypeNode, { self }) {
|
|
622
|
+
// SolAmount maps to BigInt (lamports)
|
|
623
|
+
return visit(node.number, self);
|
|
624
|
+
},
|
|
625
|
+
|
|
626
|
+
visitAmountType(node: AmountTypeNode, { self }) {
|
|
627
|
+
// Amount types pass through to their number type
|
|
628
|
+
return visit(node.number, self);
|
|
629
|
+
},
|
|
630
|
+
|
|
631
|
+
visitDateTimeType(node: DateTimeTypeNode, { self }) {
|
|
632
|
+
// DateTime types pass through to their number type
|
|
633
|
+
return visit(node.number, self);
|
|
634
|
+
},
|
|
635
|
+
|
|
636
|
+
visitDefinedTypeLink(
|
|
637
|
+
node: DefinedTypeLinkNode,
|
|
638
|
+
{ self }: { self: Visitor<TypeManifest> },
|
|
639
|
+
) {
|
|
640
|
+
const name = node.name as string;
|
|
641
|
+
const typeName = nameApi.dataType(name);
|
|
642
|
+
const isNonScalar = nonScalarEnums.includes(node.name);
|
|
643
|
+
const moduleKey = `definedType:${snakeCase(name)}`;
|
|
644
|
+
|
|
645
|
+
return {
|
|
646
|
+
type: fragment`${use(typeName, moduleKey)}`,
|
|
647
|
+
encoder: fragment`${use(nameApi.encoderFunction(name), moduleKey)}()`,
|
|
648
|
+
decoder: fragment`${use(nameApi.decoderFunction(name), moduleKey)}()`,
|
|
649
|
+
value: emptyTypeManifest().value,
|
|
650
|
+
isEnum: !isNonScalar,
|
|
651
|
+
};
|
|
652
|
+
},
|
|
653
|
+
|
|
654
|
+
visitDefinedType(node: DefinedTypeNode, { self }) {
|
|
655
|
+
return visit(node.type, self);
|
|
656
|
+
},
|
|
657
|
+
|
|
658
|
+
visitAccount(node: AccountNode, { self }) {
|
|
659
|
+
return visit(node.data, self);
|
|
660
|
+
},
|
|
661
|
+
|
|
662
|
+
visitInstruction(node: InstructionNode, { self }) {
|
|
663
|
+
return emptyTypeManifest();
|
|
664
|
+
},
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// --- Helper functions ---
|
|
669
|
+
|
|
670
|
+
function getNumberDartType(
|
|
671
|
+
format: string,
|
|
672
|
+
): string {
|
|
673
|
+
switch (format) {
|
|
674
|
+
case "u8":
|
|
675
|
+
case "u16":
|
|
676
|
+
case "u32":
|
|
677
|
+
case "i8":
|
|
678
|
+
case "i16":
|
|
679
|
+
case "i32":
|
|
680
|
+
case "shortU16":
|
|
681
|
+
return "int";
|
|
682
|
+
case "u64":
|
|
683
|
+
case "u128":
|
|
684
|
+
case "i64":
|
|
685
|
+
case "i128":
|
|
686
|
+
return "BigInt";
|
|
687
|
+
case "f32":
|
|
688
|
+
case "f64":
|
|
689
|
+
return "double";
|
|
690
|
+
default:
|
|
691
|
+
return "int";
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function getNumberCodecName(format: string): string {
|
|
696
|
+
switch (format) {
|
|
697
|
+
case "u8":
|
|
698
|
+
return "U8";
|
|
699
|
+
case "u16":
|
|
700
|
+
return "U16";
|
|
701
|
+
case "u32":
|
|
702
|
+
return "U32";
|
|
703
|
+
case "u64":
|
|
704
|
+
return "U64";
|
|
705
|
+
case "u128":
|
|
706
|
+
return "U128";
|
|
707
|
+
case "i8":
|
|
708
|
+
return "I8";
|
|
709
|
+
case "i16":
|
|
710
|
+
return "I16";
|
|
711
|
+
case "i32":
|
|
712
|
+
return "I32";
|
|
713
|
+
case "i64":
|
|
714
|
+
return "I64";
|
|
715
|
+
case "i128":
|
|
716
|
+
return "I128";
|
|
717
|
+
case "f32":
|
|
718
|
+
return "F32";
|
|
719
|
+
case "f64":
|
|
720
|
+
return "F64";
|
|
721
|
+
case "shortU16":
|
|
722
|
+
return "ShortU16";
|
|
723
|
+
default:
|
|
724
|
+
return "U8";
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
function getNumberCodecExpression(
|
|
729
|
+
format: string,
|
|
730
|
+
codecType: "Encoder" | "Decoder",
|
|
731
|
+
): string {
|
|
732
|
+
const name = getNumberCodecName(format);
|
|
733
|
+
return `get${name}${codecType}()`;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
function getArraySizeExpression(node: ArrayTypeNode): string {
|
|
737
|
+
if (!("count" in node) || !node.count) return "";
|
|
738
|
+
const count = node.count;
|
|
739
|
+
switch (count.kind) {
|
|
740
|
+
case "fixedCountNode":
|
|
741
|
+
return `, size: FixedArraySize(${count.value})`;
|
|
742
|
+
case "remainderCountNode":
|
|
743
|
+
return ", size: RemainderArraySize()";
|
|
744
|
+
case "prefixedCountNode": {
|
|
745
|
+
const resolvedPrefix = resolveNestedTypeNode(count.prefix);
|
|
746
|
+
if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
|
|
747
|
+
return ""; // Default
|
|
748
|
+
}
|
|
749
|
+
return `, size: PrefixedArraySize(get${getNumberCodecName(resolvedPrefix.format)}Encoder())`;
|
|
750
|
+
}
|
|
751
|
+
default:
|
|
752
|
+
return "";
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
function getMapSizeExpression(node: MapTypeNode): string {
|
|
757
|
+
if (!("count" in node) || !node.count) return "";
|
|
758
|
+
const count = node.count;
|
|
759
|
+
switch (count.kind) {
|
|
760
|
+
case "fixedCountNode":
|
|
761
|
+
return `, size: FixedArraySize(${count.value})`;
|
|
762
|
+
case "remainderCountNode":
|
|
763
|
+
return ", size: RemainderArraySize()";
|
|
764
|
+
case "prefixedCountNode": {
|
|
765
|
+
const resolvedPrefix = resolveNestedTypeNode(count.prefix);
|
|
766
|
+
if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
|
|
767
|
+
return "";
|
|
768
|
+
}
|
|
769
|
+
return `, size: PrefixedArraySize(get${getNumberCodecName(resolvedPrefix.format)}Encoder())`;
|
|
770
|
+
}
|
|
771
|
+
default:
|
|
772
|
+
return "";
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function getSetSizeExpression(node: SetTypeNode): string {
|
|
777
|
+
if (!("count" in node) || !node.count) return "";
|
|
778
|
+
const count = node.count;
|
|
779
|
+
switch (count.kind) {
|
|
780
|
+
case "fixedCountNode":
|
|
781
|
+
return `, size: FixedArraySize(${count.value})`;
|
|
782
|
+
case "remainderCountNode":
|
|
783
|
+
return ", size: RemainderArraySize()";
|
|
784
|
+
case "prefixedCountNode": {
|
|
785
|
+
const resolvedPrefix = resolveNestedTypeNode(count.prefix);
|
|
786
|
+
if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
|
|
787
|
+
return "";
|
|
788
|
+
}
|
|
789
|
+
return `, size: PrefixedArraySize(get${getNumberCodecName(resolvedPrefix.format)}Encoder())`;
|
|
790
|
+
}
|
|
791
|
+
default:
|
|
792
|
+
return "";
|
|
793
|
+
}
|
|
794
|
+
}
|