@codama/renderers-rust 1.0.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.
Files changed (43) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +48 -0
  3. package/dist/index.node.cjs +909 -0
  4. package/dist/index.node.cjs.map +1 -0
  5. package/dist/index.node.mjs +900 -0
  6. package/dist/index.node.mjs.map +1 -0
  7. package/dist/templates/accountsMod.njk +13 -0
  8. package/dist/templates/accountsPage.njk +160 -0
  9. package/dist/templates/definedTypesMod.njk +13 -0
  10. package/dist/templates/definedTypesPage.njk +15 -0
  11. package/dist/templates/errorsMod.njk +17 -0
  12. package/dist/templates/errorsPage.njk +23 -0
  13. package/dist/templates/instructionsCpiPage.njk +185 -0
  14. package/dist/templates/instructionsCpiPageBuilder.njk +145 -0
  15. package/dist/templates/instructionsMod.njk +13 -0
  16. package/dist/templates/instructionsPage.njk +159 -0
  17. package/dist/templates/instructionsPageBuilder.njk +128 -0
  18. package/dist/templates/layout.njk +7 -0
  19. package/dist/templates/macros.njk +6 -0
  20. package/dist/templates/programsMod.njk +13 -0
  21. package/dist/templates/rootMod.njk +26 -0
  22. package/dist/templates/sharedPage.njk +93 -0
  23. package/dist/types/ImportMap.d.ts +16 -0
  24. package/dist/types/ImportMap.d.ts.map +1 -0
  25. package/dist/types/getRenderMapVisitor.d.ts +9 -0
  26. package/dist/types/getRenderMapVisitor.d.ts.map +1 -0
  27. package/dist/types/getTypeManifestVisitor.d.ts +13 -0
  28. package/dist/types/getTypeManifestVisitor.d.ts.map +1 -0
  29. package/dist/types/index.d.ts +5 -0
  30. package/dist/types/index.d.ts.map +1 -0
  31. package/dist/types/renderValueNodeVisitor.d.ts +13 -0
  32. package/dist/types/renderValueNodeVisitor.d.ts.map +1 -0
  33. package/dist/types/renderVisitor.d.ts +9 -0
  34. package/dist/types/renderVisitor.d.ts.map +1 -0
  35. package/dist/types/utils/codecs.d.ts +3 -0
  36. package/dist/types/utils/codecs.d.ts.map +1 -0
  37. package/dist/types/utils/index.d.ts +4 -0
  38. package/dist/types/utils/index.d.ts.map +1 -0
  39. package/dist/types/utils/linkOverrides.d.ts +14 -0
  40. package/dist/types/utils/linkOverrides.d.ts.map +1 -0
  41. package/dist/types/utils/render.d.ts +4 -0
  42. package/dist/types/utils/render.d.ts.map +1 -0
  43. package/package.json +67 -0
@@ -0,0 +1,900 @@
1
+ import { CodamaError, CODAMA_ERROR__RENDERERS__UNSUPPORTED_NODE, logWarn, logError, CODAMA_ERROR__UNEXPECTED_NODE_KIND } from '@codama/errors';
2
+ import { REGISTERED_TYPE_NODE_KINDS, pascalCase, isNode, resolveNestedTypeNode, remainderCountNode, arrayTypeNode, numberTypeNode, isScalarEnum, snakeCase, isNodeFilter, VALUE_NODES, structTypeNodeFromInstructionArgumentNodes, getAllInstructionsWithSubs, getAllPrograms, getAllAccounts, getAllDefinedTypes, camelCase, kebabCase, titleCase, fixedCountNode, prefixedCountNode, numberValueNode, arrayValueNode, bytesValueNode } from '@codama/nodes';
3
+ import { RenderMap, deleteDirectory, writeRenderMapVisitor } from '@codama/renderers-core';
4
+ import { pipe, mergeVisitor, extendVisitor, visit, LinkableDictionary, staticVisitor, recordLinkablesVisitor, rootNodeVisitor } from '@codama/visitors-core';
5
+ import { getBase64Encoder, getBase58Encoder, getBase16Encoder, getUtf8Encoder } from '@solana/codecs-strings';
6
+ import { dirname, join } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+ import nunjucks from 'nunjucks';
9
+ import { spawnSync } from 'child_process';
10
+
11
+ // src/ImportMap.ts
12
+ var DEFAULT_MODULE_MAP = {
13
+ generated: "crate::generated",
14
+ generatedAccounts: "crate::generated::accounts",
15
+ generatedErrors: "crate::generated::errors",
16
+ generatedInstructions: "crate::generated::instructions",
17
+ generatedTypes: "crate::generated::types",
18
+ hooked: "crate::hooked",
19
+ mplEssentials: "mpl_toolbox",
20
+ mplToolbox: "mpl_toolbox"
21
+ };
22
+ var ImportMap = class _ImportMap {
23
+ _imports = /* @__PURE__ */ new Set();
24
+ _aliases = /* @__PURE__ */ new Map();
25
+ get imports() {
26
+ return this._imports;
27
+ }
28
+ get aliases() {
29
+ return this._aliases;
30
+ }
31
+ add(imports) {
32
+ const newImports = typeof imports === "string" ? [imports] : imports;
33
+ newImports.forEach((i) => this._imports.add(i));
34
+ return this;
35
+ }
36
+ remove(imports) {
37
+ const importsToRemove = typeof imports === "string" ? [imports] : imports;
38
+ importsToRemove.forEach((i) => this._imports.delete(i));
39
+ return this;
40
+ }
41
+ mergeWith(...others) {
42
+ others.forEach((other) => {
43
+ this.add(other._imports);
44
+ other._aliases.forEach((alias, importName) => this.addAlias(importName, alias));
45
+ });
46
+ return this;
47
+ }
48
+ mergeWithManifest(manifest) {
49
+ return this.mergeWith(manifest.imports);
50
+ }
51
+ addAlias(importName, alias) {
52
+ this._aliases.set(importName, alias);
53
+ return this;
54
+ }
55
+ isEmpty() {
56
+ return this._imports.size === 0;
57
+ }
58
+ resolveDependencyMap(dependencies) {
59
+ const dependencyMap = { ...DEFAULT_MODULE_MAP, ...dependencies };
60
+ const newImportMap = new _ImportMap();
61
+ const resolveDependency = (i) => {
62
+ const dependencyKey = Object.keys(dependencyMap).find((key) => i.startsWith(`${key}::`));
63
+ if (!dependencyKey) return i;
64
+ const dependencyValue = dependencyMap[dependencyKey];
65
+ return dependencyValue + i.slice(dependencyKey.length);
66
+ };
67
+ this._imports.forEach((i) => newImportMap.add(resolveDependency(i)));
68
+ this._aliases.forEach((alias, i) => newImportMap.addAlias(resolveDependency(i), alias));
69
+ return newImportMap;
70
+ }
71
+ toString(dependencies) {
72
+ const resolvedMap = this.resolveDependencyMap(dependencies);
73
+ const importStatements = [...resolvedMap.imports].map((i) => {
74
+ const alias = resolvedMap.aliases.get(i);
75
+ if (alias) return `use ${i} as ${alias};`;
76
+ return `use ${i};`;
77
+ });
78
+ return importStatements.join("\n");
79
+ }
80
+ };
81
+ function getBytesFromBytesValueNode(node) {
82
+ switch (node.encoding) {
83
+ case "utf8":
84
+ return getUtf8Encoder().encode(node.data);
85
+ case "base16":
86
+ return getBase16Encoder().encode(node.data);
87
+ case "base58":
88
+ return getBase58Encoder().encode(node.data);
89
+ case "base64":
90
+ default:
91
+ return getBase64Encoder().encode(node.data);
92
+ }
93
+ }
94
+ function getImportFromFactory(overrides) {
95
+ const linkOverrides = {
96
+ accounts: overrides.accounts ?? {},
97
+ definedTypes: overrides.definedTypes ?? {},
98
+ instructions: overrides.instructions ?? {},
99
+ pdas: overrides.pdas ?? {},
100
+ programs: overrides.programs ?? {},
101
+ resolvers: overrides.resolvers ?? {}
102
+ };
103
+ return (node) => {
104
+ const kind = node.kind;
105
+ switch (kind) {
106
+ case "accountLinkNode":
107
+ return linkOverrides.accounts[node.name] ?? "generatedAccounts";
108
+ case "definedTypeLinkNode":
109
+ return linkOverrides.definedTypes[node.name] ?? "generatedTypes";
110
+ case "instructionLinkNode":
111
+ return linkOverrides.instructions[node.name] ?? "generatedInstructions";
112
+ case "pdaLinkNode":
113
+ return linkOverrides.pdas[node.name] ?? "generatedAccounts";
114
+ case "programLinkNode":
115
+ return linkOverrides.programs[node.name] ?? "generatedPrograms";
116
+ case "resolverValueNode":
117
+ return linkOverrides.resolvers[node.name] ?? "hooked";
118
+ default:
119
+ throw new CodamaError(CODAMA_ERROR__UNEXPECTED_NODE_KIND, {
120
+ expectedKinds: [
121
+ "AccountLinkNode",
122
+ "DefinedTypeLinkNode",
123
+ "InstructionLinkNode",
124
+ "PdaLinkNode",
125
+ "ProgramLinkNode",
126
+ "resolverValueNode"
127
+ ],
128
+ kind,
129
+ node
130
+ });
131
+ }
132
+ };
133
+ }
134
+ function rustDocblock(docs) {
135
+ if (docs.length <= 0) return "";
136
+ const lines = docs.map((doc) => `/// ${doc}`);
137
+ return `${lines.join("\n")}
138
+ `;
139
+ }
140
+ var render = (template, context, options) => {
141
+ const dirname$1 = dirname(fileURLToPath(import.meta.url)) ;
142
+ const templates = join(dirname$1, "templates");
143
+ const env = nunjucks.configure(templates, { autoescape: false, trimBlocks: true, ...options });
144
+ env.addFilter("pascalCase", pascalCase);
145
+ env.addFilter("camelCase", camelCase);
146
+ env.addFilter("snakeCase", snakeCase);
147
+ env.addFilter("kebabCase", kebabCase);
148
+ env.addFilter("titleCase", titleCase);
149
+ env.addFilter("rustDocblock", rustDocblock);
150
+ return env.render(template, context);
151
+ };
152
+
153
+ // src/getTypeManifestVisitor.ts
154
+ function getTypeManifestVisitor(options) {
155
+ const { getImportFrom } = options;
156
+ let parentName = options.parentName ?? null;
157
+ let nestedStruct = options.nestedStruct ?? false;
158
+ let inlineStruct = false;
159
+ let parentSize = null;
160
+ return pipe(
161
+ mergeVisitor(
162
+ () => ({ imports: new ImportMap(), nestedStructs: [], type: "" }),
163
+ (_, values) => ({
164
+ ...mergeManifests(values),
165
+ type: values.map((v) => v.type).join("\n")
166
+ }),
167
+ [...REGISTERED_TYPE_NODE_KINDS, "definedTypeLinkNode", "definedTypeNode", "accountNode"]
168
+ ),
169
+ (v) => extendVisitor(v, {
170
+ visitAccount(account, { self }) {
171
+ parentName = pascalCase(account.name);
172
+ const manifest = visit(account.data, self);
173
+ manifest.imports.add(["borsh::BorshSerialize", "borsh::BorshDeserialize"]);
174
+ parentName = null;
175
+ return {
176
+ ...manifest,
177
+ type: `#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
178
+ #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
179
+ ${manifest.type}`
180
+ };
181
+ },
182
+ visitArrayType(arrayType, { self }) {
183
+ const childManifest = visit(arrayType.item, self);
184
+ if (isNode(arrayType.count, "fixedCountNode")) {
185
+ return {
186
+ ...childManifest,
187
+ type: `[${childManifest.type}; ${arrayType.count.value}]`
188
+ };
189
+ }
190
+ if (isNode(arrayType.count, "remainderCountNode")) {
191
+ childManifest.imports.add("kaigan::types::RemainderVec");
192
+ return {
193
+ ...childManifest,
194
+ type: `RemainderVec<${childManifest.type}>`
195
+ };
196
+ }
197
+ const prefix = resolveNestedTypeNode(arrayType.count.prefix);
198
+ if (prefix.endian === "le") {
199
+ switch (prefix.format) {
200
+ case "u32":
201
+ return {
202
+ ...childManifest,
203
+ type: `Vec<${childManifest.type}>`
204
+ };
205
+ case "u8":
206
+ case "u16":
207
+ case "u64": {
208
+ const prefixFormat = prefix.format.toUpperCase();
209
+ childManifest.imports.add(`kaigan::types::${prefixFormat}PrefixVec`);
210
+ return {
211
+ ...childManifest,
212
+ type: `${prefixFormat}PrefixVec<${childManifest.type}>`
213
+ };
214
+ }
215
+ case "shortU16": {
216
+ childManifest.imports.add("solana_program::short_vec::ShortVec");
217
+ return {
218
+ ...childManifest,
219
+ type: `ShortVec<${childManifest.type}>`
220
+ };
221
+ }
222
+ default:
223
+ throw new Error(`Array prefix not supported: ${prefix.format}`);
224
+ }
225
+ }
226
+ throw new Error("Array size not supported by Borsh");
227
+ },
228
+ visitBooleanType(booleanType) {
229
+ const resolvedSize = resolveNestedTypeNode(booleanType.size);
230
+ if (resolvedSize.format === "u8" && resolvedSize.endian === "le") {
231
+ return {
232
+ imports: new ImportMap(),
233
+ nestedStructs: [],
234
+ type: "bool"
235
+ };
236
+ }
237
+ throw new Error("Bool size not supported by Borsh");
238
+ },
239
+ visitBytesType(_bytesType, { self }) {
240
+ let arraySize = remainderCountNode();
241
+ if (typeof parentSize === "number") {
242
+ arraySize = fixedCountNode(parentSize);
243
+ } else if (parentSize && typeof parentSize === "object") {
244
+ arraySize = prefixedCountNode(parentSize);
245
+ }
246
+ const arrayType = arrayTypeNode(numberTypeNode("u8"), arraySize);
247
+ return visit(arrayType, self);
248
+ },
249
+ visitDefinedType(definedType, { self }) {
250
+ parentName = pascalCase(definedType.name);
251
+ const manifest = visit(definedType.type, self);
252
+ parentName = null;
253
+ const traits = ["BorshSerialize", "BorshDeserialize", "Clone", "Debug", "Eq", "PartialEq"];
254
+ if (isNode(definedType.type, "enumTypeNode") && isScalarEnum(definedType.type)) {
255
+ traits.push("Copy", "PartialOrd", "Hash", "FromPrimitive");
256
+ manifest.imports.add(["num_derive::FromPrimitive"]);
257
+ }
258
+ const nestedStructs = manifest.nestedStructs.map(
259
+ (struct) => `#[derive(${traits.join(", ")})]
260
+ #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
261
+ ${struct}`
262
+ );
263
+ if (!isNode(definedType.type, ["enumTypeNode", "structTypeNode"])) {
264
+ if (nestedStructs.length > 0) {
265
+ manifest.imports.add(["borsh::BorshSerialize", "borsh::BorshDeserialize"]);
266
+ }
267
+ return {
268
+ ...manifest,
269
+ nestedStructs,
270
+ type: `pub type ${pascalCase(definedType.name)} = ${manifest.type}`
271
+ };
272
+ }
273
+ manifest.imports.add(["borsh::BorshSerialize", "borsh::BorshDeserialize"]);
274
+ return {
275
+ ...manifest,
276
+ nestedStructs,
277
+ type: `#[derive(${traits.join(", ")})]
278
+ #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
279
+ ${manifest.type}`
280
+ };
281
+ },
282
+ visitDefinedTypeLink(node) {
283
+ const pascalCaseDefinedType = pascalCase(node.name);
284
+ const importFrom = getImportFrom(node);
285
+ return {
286
+ imports: new ImportMap().add(`${importFrom}::${pascalCaseDefinedType}`),
287
+ nestedStructs: [],
288
+ type: pascalCaseDefinedType
289
+ };
290
+ },
291
+ visitEnumEmptyVariantType(enumEmptyVariantType) {
292
+ const name = pascalCase(enumEmptyVariantType.name);
293
+ return {
294
+ imports: new ImportMap(),
295
+ nestedStructs: [],
296
+ type: `${name},`
297
+ };
298
+ },
299
+ visitEnumStructVariantType(enumStructVariantType, { self }) {
300
+ const name = pascalCase(enumStructVariantType.name);
301
+ const originalParentName = parentName;
302
+ if (!originalParentName) {
303
+ throw new Error("Enum struct variant type must have a parent name.");
304
+ }
305
+ inlineStruct = true;
306
+ parentName = pascalCase(originalParentName) + name;
307
+ const typeManifest = visit(enumStructVariantType.struct, self);
308
+ inlineStruct = false;
309
+ parentName = originalParentName;
310
+ return {
311
+ ...typeManifest,
312
+ type: `${name} ${typeManifest.type},`
313
+ };
314
+ },
315
+ visitEnumTupleVariantType(enumTupleVariantType, { self }) {
316
+ const name = pascalCase(enumTupleVariantType.name);
317
+ const originalParentName = parentName;
318
+ if (!originalParentName) {
319
+ throw new Error("Enum struct variant type must have a parent name.");
320
+ }
321
+ parentName = pascalCase(originalParentName) + name;
322
+ const childManifest = visit(enumTupleVariantType.tuple, self);
323
+ parentName = originalParentName;
324
+ let derive = "";
325
+ if (childManifest.type === "(Pubkey)") {
326
+ derive = '#[cfg_attr(feature = "serde", serde(with = "serde_with::As::<serde_with::DisplayFromStr>"))]\n';
327
+ } else if (childManifest.type === "(Vec<Pubkey>)") {
328
+ derive = '#[cfg_attr(feature = "serde", serde(with = "serde_with::As::<Vec<serde_with::DisplayFromStr>>"))]\n';
329
+ }
330
+ return {
331
+ ...childManifest,
332
+ type: `${derive}${name}${childManifest.type},`
333
+ };
334
+ },
335
+ visitEnumType(enumType, { self }) {
336
+ const originalParentName = parentName;
337
+ if (!originalParentName) {
338
+ throw new Error("Enum type must have a parent name.");
339
+ }
340
+ const variants = enumType.variants.map((variant) => visit(variant, self));
341
+ const variantNames = variants.map((variant) => variant.type).join("\n");
342
+ const mergedManifest = mergeManifests(variants);
343
+ return {
344
+ ...mergedManifest,
345
+ type: `pub enum ${pascalCase(originalParentName)} {
346
+ ${variantNames}
347
+ }`
348
+ };
349
+ },
350
+ visitFixedSizeType(fixedSizeType, { self }) {
351
+ parentSize = fixedSizeType.size;
352
+ const manifest = visit(fixedSizeType.type, self);
353
+ parentSize = null;
354
+ return manifest;
355
+ },
356
+ visitMapType(mapType, { self }) {
357
+ const key = visit(mapType.key, self);
358
+ const value = visit(mapType.value, self);
359
+ const mergedManifest = mergeManifests([key, value]);
360
+ mergedManifest.imports.add("std::collections::HashMap");
361
+ return {
362
+ ...mergedManifest,
363
+ type: `HashMap<${key.type}, ${value.type}>`
364
+ };
365
+ },
366
+ visitNumberType(numberType) {
367
+ if (numberType.endian !== "le") {
368
+ throw new Error("Number endianness not supported by Borsh");
369
+ }
370
+ if (numberType.format === "shortU16") {
371
+ return {
372
+ imports: new ImportMap().add("solana_program::short_vec::ShortU16"),
373
+ nestedStructs: [],
374
+ type: "ShortU16"
375
+ };
376
+ }
377
+ return {
378
+ imports: new ImportMap(),
379
+ nestedStructs: [],
380
+ type: numberType.format
381
+ };
382
+ },
383
+ visitOptionType(optionType, { self }) {
384
+ const childManifest = visit(optionType.item, self);
385
+ const optionPrefix = resolveNestedTypeNode(optionType.prefix);
386
+ if (optionPrefix.format === "u8" && optionPrefix.endian === "le") {
387
+ return {
388
+ ...childManifest,
389
+ type: `Option<${childManifest.type}>`
390
+ };
391
+ }
392
+ throw new Error("Option size not supported by Borsh");
393
+ },
394
+ visitPublicKeyType() {
395
+ return {
396
+ imports: new ImportMap().add("solana_program::pubkey::Pubkey"),
397
+ nestedStructs: [],
398
+ type: "Pubkey"
399
+ };
400
+ },
401
+ visitRemainderOptionType(node) {
402
+ throw new CodamaError(CODAMA_ERROR__RENDERERS__UNSUPPORTED_NODE, { kind: node.kind, node });
403
+ },
404
+ visitSetType(setType, { self }) {
405
+ const childManifest = visit(setType.item, self);
406
+ childManifest.imports.add("std::collections::HashSet");
407
+ return {
408
+ ...childManifest,
409
+ type: `HashSet<${childManifest.type}>`
410
+ };
411
+ },
412
+ visitSizePrefixType(sizePrefixType, { self }) {
413
+ parentSize = resolveNestedTypeNode(sizePrefixType.prefix);
414
+ const manifest = visit(sizePrefixType.type, self);
415
+ parentSize = null;
416
+ return manifest;
417
+ },
418
+ visitStringType() {
419
+ if (!parentSize) {
420
+ return {
421
+ imports: new ImportMap().add(`kaigan::types::RemainderStr`),
422
+ nestedStructs: [],
423
+ type: `RemainderStr`
424
+ };
425
+ }
426
+ if (typeof parentSize === "number") {
427
+ return {
428
+ imports: new ImportMap(),
429
+ nestedStructs: [],
430
+ type: `[u8; ${parentSize}]`
431
+ };
432
+ }
433
+ if (isNode(parentSize, "numberTypeNode") && parentSize.endian === "le") {
434
+ switch (parentSize.format) {
435
+ case "u32":
436
+ return {
437
+ imports: new ImportMap(),
438
+ nestedStructs: [],
439
+ type: "String"
440
+ };
441
+ case "u8":
442
+ case "u16":
443
+ case "u64": {
444
+ const prefix = parentSize.format.toUpperCase();
445
+ return {
446
+ imports: new ImportMap().add(`kaigan::types::${prefix}PrefixString`),
447
+ nestedStructs: [],
448
+ type: `${prefix}PrefixString`
449
+ };
450
+ }
451
+ default:
452
+ throw new Error(`'String size not supported: ${parentSize.format}`);
453
+ }
454
+ }
455
+ throw new Error("String size not supported by Borsh");
456
+ },
457
+ visitStructFieldType(structFieldType, { self }) {
458
+ const originalParentName = parentName;
459
+ const originalInlineStruct = inlineStruct;
460
+ const originalNestedStruct = nestedStruct;
461
+ if (!originalParentName) {
462
+ throw new Error("Struct field type must have a parent name.");
463
+ }
464
+ parentName = pascalCase(originalParentName) + pascalCase(structFieldType.name);
465
+ nestedStruct = true;
466
+ inlineStruct = false;
467
+ const fieldManifest = visit(structFieldType.type, self);
468
+ parentName = originalParentName;
469
+ inlineStruct = originalInlineStruct;
470
+ nestedStruct = originalNestedStruct;
471
+ const fieldName = snakeCase(structFieldType.name);
472
+ const docblock = rustDocblock(structFieldType.docs);
473
+ const resolvedNestedType = resolveNestedTypeNode(structFieldType.type);
474
+ let derive = "";
475
+ if (fieldManifest.type === "Pubkey") {
476
+ derive = '#[cfg_attr(feature = "serde", serde(with = "serde_with::As::<serde_with::DisplayFromStr>"))]\n';
477
+ } else if (fieldManifest.type === "Vec<Pubkey>") {
478
+ derive = '#[cfg_attr(feature = "serde", serde(with = "serde_with::As::<Vec<serde_with::DisplayFromStr>>"))]\n';
479
+ } else if (isNode(resolvedNestedType, "arrayTypeNode") && isNode(resolvedNestedType.count, "fixedCountNode") && resolvedNestedType.count.value > 32 || isNode(resolvedNestedType, ["bytesTypeNode", "stringTypeNode"]) && isNode(structFieldType.type, "fixedSizeTypeNode") && structFieldType.type.size > 32) {
480
+ derive = '#[cfg_attr(feature = "serde", serde(with = "serde_with::As::<serde_with::Bytes>"))]\n';
481
+ }
482
+ return {
483
+ ...fieldManifest,
484
+ type: inlineStruct ? `${docblock}${derive}${fieldName}: ${fieldManifest.type},` : `${docblock}${derive}pub ${fieldName}: ${fieldManifest.type},`
485
+ };
486
+ },
487
+ visitStructType(structType, { self }) {
488
+ const originalParentName = parentName;
489
+ if (!originalParentName) {
490
+ throw new Error("Struct type must have a parent name.");
491
+ }
492
+ const fields = structType.fields.map((field) => visit(field, self));
493
+ const fieldTypes = fields.map((field) => field.type).join("\n");
494
+ const mergedManifest = mergeManifests(fields);
495
+ if (nestedStruct) {
496
+ return {
497
+ ...mergedManifest,
498
+ nestedStructs: [
499
+ ...mergedManifest.nestedStructs,
500
+ `pub struct ${pascalCase(originalParentName)} {
501
+ ${fieldTypes}
502
+ }`
503
+ ],
504
+ type: pascalCase(originalParentName)
505
+ };
506
+ }
507
+ if (inlineStruct) {
508
+ return { ...mergedManifest, type: `{
509
+ ${fieldTypes}
510
+ }` };
511
+ }
512
+ return {
513
+ ...mergedManifest,
514
+ type: `pub struct ${pascalCase(originalParentName)} {
515
+ ${fieldTypes}
516
+ }`
517
+ };
518
+ },
519
+ visitTupleType(tupleType, { self }) {
520
+ const items = tupleType.items.map((item) => visit(item, self));
521
+ const mergedManifest = mergeManifests(items);
522
+ return {
523
+ ...mergedManifest,
524
+ type: `(${items.map((item) => item.type).join(", ")})`
525
+ };
526
+ },
527
+ visitZeroableOptionType(node) {
528
+ throw new CodamaError(CODAMA_ERROR__RENDERERS__UNSUPPORTED_NODE, { kind: node.kind, node });
529
+ }
530
+ })
531
+ );
532
+ }
533
+ function mergeManifests(manifests) {
534
+ return {
535
+ imports: new ImportMap().mergeWith(...manifests.map((td) => td.imports)),
536
+ nestedStructs: manifests.flatMap((m) => m.nestedStructs)
537
+ };
538
+ }
539
+ function renderValueNode(value, getImportFrom, useStr = false) {
540
+ return visit(value, renderValueNodeVisitor(getImportFrom, useStr));
541
+ }
542
+ function renderValueNodeVisitor(getImportFrom, useStr = false) {
543
+ return {
544
+ visitArrayValue(node) {
545
+ const list = node.items.map((v) => visit(v, this));
546
+ return {
547
+ imports: new ImportMap().mergeWith(...list.map((c) => c.imports)),
548
+ render: `[${list.map((c) => c.render).join(", ")}]`
549
+ };
550
+ },
551
+ visitBooleanValue(node) {
552
+ return {
553
+ imports: new ImportMap(),
554
+ render: JSON.stringify(node.boolean)
555
+ };
556
+ },
557
+ visitBytesValue(node) {
558
+ const bytes = getBytesFromBytesValueNode(node);
559
+ const numbers = Array.from(bytes).map(numberValueNode);
560
+ return visit(arrayValueNode(numbers), this);
561
+ },
562
+ visitConstantValue(node) {
563
+ if (isNode(node.value, "bytesValueNode")) {
564
+ return visit(node.value, this);
565
+ }
566
+ if (isNode(node.type, "stringTypeNode") && isNode(node.value, "stringValueNode")) {
567
+ return visit(bytesValueNode(node.type.encoding, node.value.string), this);
568
+ }
569
+ if (isNode(node.type, "numberTypeNode") && isNode(node.value, "numberValueNode")) {
570
+ const numberManifest = visit(node.value, this);
571
+ const { format, endian } = node.type;
572
+ const byteFunction = endian === "le" ? "to_le_bytes" : "to_be_bytes";
573
+ numberManifest.render = `${numberManifest.render}${format}.${byteFunction}()`;
574
+ return numberManifest;
575
+ }
576
+ throw new Error("Unsupported constant value type.");
577
+ },
578
+ visitEnumValue(node) {
579
+ const imports = new ImportMap();
580
+ const enumName = pascalCase(node.enum.name);
581
+ const variantName = pascalCase(node.variant);
582
+ const importFrom = getImportFrom(node.enum);
583
+ imports.add(`${importFrom}::${enumName}`);
584
+ if (!node.value) {
585
+ return { imports, render: `${enumName}::${variantName}` };
586
+ }
587
+ const enumValue = visit(node.value, this);
588
+ const fields = enumValue.render;
589
+ return {
590
+ imports: imports.mergeWith(enumValue.imports),
591
+ render: `${enumName}::${variantName} ${fields}`
592
+ };
593
+ },
594
+ visitMapEntryValue(node) {
595
+ const mapKey = visit(node.key, this);
596
+ const mapValue = visit(node.value, this);
597
+ return {
598
+ imports: mapKey.imports.mergeWith(mapValue.imports),
599
+ render: `[${mapKey.render}, ${mapValue.render}]`
600
+ };
601
+ },
602
+ visitMapValue(node) {
603
+ const map = node.entries.map((entry) => visit(entry, this));
604
+ const imports = new ImportMap().add("std::collection::HashMap");
605
+ return {
606
+ imports: imports.mergeWith(...map.map((c) => c.imports)),
607
+ render: `HashMap::from([${map.map((c) => c.render).join(", ")}])`
608
+ };
609
+ },
610
+ visitNoneValue() {
611
+ return {
612
+ imports: new ImportMap(),
613
+ render: "None"
614
+ };
615
+ },
616
+ visitNumberValue(node) {
617
+ return {
618
+ imports: new ImportMap(),
619
+ render: node.number.toString()
620
+ };
621
+ },
622
+ visitPublicKeyValue(node) {
623
+ return {
624
+ imports: new ImportMap().add("solana_program::pubkey"),
625
+ render: `pubkey!("${node.publicKey}")`
626
+ };
627
+ },
628
+ visitSetValue(node) {
629
+ const set = node.items.map((v) => visit(v, this));
630
+ const imports = new ImportMap().add("std::collection::HashSet");
631
+ return {
632
+ imports: imports.mergeWith(...set.map((c) => c.imports)),
633
+ render: `HashSet::from([${set.map((c) => c.render).join(", ")}])`
634
+ };
635
+ },
636
+ visitSomeValue(node) {
637
+ const child = visit(node.value, this);
638
+ return {
639
+ ...child,
640
+ render: `Some(${child.render})`
641
+ };
642
+ },
643
+ visitStringValue(node) {
644
+ return {
645
+ imports: new ImportMap(),
646
+ render: useStr ? `${JSON.stringify(node.string)}` : `String::from(${JSON.stringify(node.string)})`
647
+ };
648
+ },
649
+ visitStructFieldValue(node) {
650
+ const structValue = visit(node.value, this);
651
+ return {
652
+ imports: structValue.imports,
653
+ render: `${node.name}: ${structValue.render}`
654
+ };
655
+ },
656
+ visitStructValue(node) {
657
+ const struct = node.fields.map((field) => visit(field, this));
658
+ return {
659
+ imports: new ImportMap().mergeWith(...struct.map((c) => c.imports)),
660
+ render: `{ ${struct.map((c) => c.render).join(", ")} }`
661
+ };
662
+ },
663
+ visitTupleValue(node) {
664
+ const tuple = node.items.map((v) => visit(v, this));
665
+ return {
666
+ imports: new ImportMap().mergeWith(...tuple.map((c) => c.imports)),
667
+ render: `(${tuple.map((c) => c.render).join(", ")})`
668
+ };
669
+ }
670
+ };
671
+ }
672
+
673
+ // src/getRenderMapVisitor.ts
674
+ function getRenderMapVisitor(options = {}) {
675
+ const linkables = new LinkableDictionary();
676
+ let program = null;
677
+ const renderParentInstructions = options.renderParentInstructions ?? false;
678
+ const dependencyMap = options.dependencyMap ?? {};
679
+ const getImportFrom = getImportFromFactory(options.linkOverrides ?? {});
680
+ const typeManifestVisitor = getTypeManifestVisitor({ getImportFrom });
681
+ return pipe(
682
+ staticVisitor(
683
+ () => new RenderMap(),
684
+ ["rootNode", "programNode", "instructionNode", "accountNode", "definedTypeNode"]
685
+ ),
686
+ (v) => extendVisitor(v, {
687
+ visitAccount(node) {
688
+ const typeManifest = visit(node, typeManifestVisitor);
689
+ const seedsImports = new ImportMap();
690
+ const pda = node.pda ? linkables.get(node.pda) : void 0;
691
+ const pdaSeeds = pda?.seeds ?? [];
692
+ const seeds = pdaSeeds.map((seed) => {
693
+ if (isNode(seed, "variablePdaSeedNode")) {
694
+ const seedManifest2 = visit(seed.type, typeManifestVisitor);
695
+ seedsImports.mergeWith(seedManifest2.imports);
696
+ const resolvedType2 = resolveNestedTypeNode(seed.type);
697
+ return { ...seed, resolvedType: resolvedType2, typeManifest: seedManifest2 };
698
+ }
699
+ if (isNode(seed.value, "programIdValueNode")) {
700
+ return seed;
701
+ }
702
+ const seedManifest = visit(seed.type, typeManifestVisitor);
703
+ const valueManifest = renderValueNode(seed.value, getImportFrom, true);
704
+ seedsImports.mergeWith(valueManifest.imports);
705
+ const resolvedType = resolveNestedTypeNode(seed.type);
706
+ return { ...seed, resolvedType, typeManifest: seedManifest, valueManifest };
707
+ });
708
+ const hasVariableSeeds = pdaSeeds.filter(isNodeFilter("variablePdaSeedNode")).length > 0;
709
+ const constantSeeds = seeds.filter(isNodeFilter("constantPdaSeedNode")).filter((seed) => !isNode(seed.value, "programIdValueNode"));
710
+ const { imports } = typeManifest;
711
+ if (hasVariableSeeds) {
712
+ imports.mergeWith(seedsImports);
713
+ }
714
+ return new RenderMap().add(
715
+ `accounts/${snakeCase(node.name)}.rs`,
716
+ render("accountsPage.njk", {
717
+ account: node,
718
+ constantSeeds,
719
+ hasVariableSeeds,
720
+ imports: imports.remove(`generatedAccounts::${pascalCase(node.name)}`).toString(dependencyMap),
721
+ pda,
722
+ program,
723
+ seeds,
724
+ typeManifest
725
+ })
726
+ );
727
+ },
728
+ visitDefinedType(node) {
729
+ const typeManifest = visit(node, typeManifestVisitor);
730
+ const imports = new ImportMap().mergeWithManifest(typeManifest);
731
+ return new RenderMap().add(
732
+ `types/${snakeCase(node.name)}.rs`,
733
+ render("definedTypesPage.njk", {
734
+ definedType: node,
735
+ imports: imports.remove(`generatedTypes::${pascalCase(node.name)}`).toString(dependencyMap),
736
+ typeManifest
737
+ })
738
+ );
739
+ },
740
+ visitInstruction(node) {
741
+ const imports = new ImportMap();
742
+ imports.add(["borsh::BorshDeserialize", "borsh::BorshSerialize"]);
743
+ const accountsAndArgsConflicts = getConflictsForInstructionAccountsAndArgs(node);
744
+ if (accountsAndArgsConflicts.length > 0) {
745
+ logWarn(
746
+ `[Rust] Accounts and args of instruction [${node.name}] have the following conflicting attributes [${accountsAndArgsConflicts.join(", ")}]. Thus, the conflicting arguments will be suffixed with "_arg". You may want to rename the conflicting attributes.`
747
+ );
748
+ }
749
+ const instructionArgs = [];
750
+ let hasArgs = false;
751
+ let hasOptional = false;
752
+ node.arguments.forEach((argument) => {
753
+ const argumentVisitor = getTypeManifestVisitor({
754
+ getImportFrom,
755
+ nestedStruct: true,
756
+ parentName: `${pascalCase(node.name)}InstructionData`
757
+ });
758
+ const manifest = visit(argument.type, argumentVisitor);
759
+ imports.mergeWith(manifest.imports);
760
+ const innerOptionType = isNode(argument.type, "optionTypeNode") ? manifest.type.slice("Option<".length, -1) : null;
761
+ const hasDefaultValue = !!argument.defaultValue && isNode(argument.defaultValue, VALUE_NODES);
762
+ let renderValue = null;
763
+ if (hasDefaultValue) {
764
+ const { imports: argImports, render: value } = renderValueNode(
765
+ argument.defaultValue,
766
+ getImportFrom
767
+ );
768
+ imports.mergeWith(argImports);
769
+ renderValue = value;
770
+ }
771
+ hasArgs = hasArgs || argument.defaultValueStrategy !== "omitted";
772
+ hasOptional = hasOptional || hasDefaultValue && argument.defaultValueStrategy !== "omitted";
773
+ const name = accountsAndArgsConflicts.includes(argument.name) ? `${argument.name}_arg` : argument.name;
774
+ instructionArgs.push({
775
+ default: hasDefaultValue && argument.defaultValueStrategy === "omitted",
776
+ innerOptionType,
777
+ name,
778
+ optional: hasDefaultValue && argument.defaultValueStrategy !== "omitted",
779
+ type: manifest.type,
780
+ value: renderValue
781
+ });
782
+ });
783
+ const struct = structTypeNodeFromInstructionArgumentNodes(node.arguments);
784
+ const structVisitor = getTypeManifestVisitor({
785
+ getImportFrom,
786
+ parentName: `${pascalCase(node.name)}InstructionData`
787
+ });
788
+ const typeManifest = visit(struct, structVisitor);
789
+ return new RenderMap().add(
790
+ `instructions/${snakeCase(node.name)}.rs`,
791
+ render("instructionsPage.njk", {
792
+ hasArgs,
793
+ hasOptional,
794
+ imports: imports.remove(`generatedInstructions::${pascalCase(node.name)}`).toString(dependencyMap),
795
+ instruction: node,
796
+ instructionArgs,
797
+ program,
798
+ typeManifest
799
+ })
800
+ );
801
+ },
802
+ visitProgram(node, { self }) {
803
+ program = node;
804
+ const renderMap = new RenderMap().mergeWith(...node.accounts.map((account) => visit(account, self))).mergeWith(...node.definedTypes.map((type) => visit(type, self))).mergeWith(
805
+ ...getAllInstructionsWithSubs(node, {
806
+ leavesOnly: !renderParentInstructions
807
+ }).map((ix) => visit(ix, self))
808
+ );
809
+ if (node.errors.length > 0) {
810
+ renderMap.add(
811
+ `errors/${snakeCase(node.name)}.rs`,
812
+ render("errorsPage.njk", {
813
+ errors: node.errors,
814
+ imports: new ImportMap().toString(dependencyMap),
815
+ program: node
816
+ })
817
+ );
818
+ }
819
+ program = null;
820
+ return renderMap;
821
+ },
822
+ visitRoot(node, { self }) {
823
+ const programsToExport = getAllPrograms(node);
824
+ const accountsToExport = getAllAccounts(node);
825
+ const instructionsToExport = getAllInstructionsWithSubs(node, {
826
+ leavesOnly: !renderParentInstructions
827
+ });
828
+ const definedTypesToExport = getAllDefinedTypes(node);
829
+ const hasAnythingToExport = programsToExport.length > 0 || accountsToExport.length > 0 || instructionsToExport.length > 0 || definedTypesToExport.length > 0;
830
+ const ctx = {
831
+ accountsToExport,
832
+ definedTypesToExport,
833
+ hasAnythingToExport,
834
+ instructionsToExport,
835
+ programsToExport,
836
+ root: node
837
+ };
838
+ const map = new RenderMap();
839
+ if (programsToExport.length > 0) {
840
+ map.add("programs.rs", render("programsMod.njk", ctx)).add(
841
+ "errors/mod.rs",
842
+ render("errorsMod.njk", ctx)
843
+ );
844
+ }
845
+ if (accountsToExport.length > 0) {
846
+ map.add("accounts/mod.rs", render("accountsMod.njk", ctx));
847
+ }
848
+ if (instructionsToExport.length > 0) {
849
+ map.add("instructions/mod.rs", render("instructionsMod.njk", ctx));
850
+ }
851
+ if (definedTypesToExport.length > 0) {
852
+ map.add("types/mod.rs", render("definedTypesMod.njk", ctx));
853
+ }
854
+ return map.add("mod.rs", render("rootMod.njk", ctx)).mergeWith(...getAllPrograms(node).map((p) => visit(p, self)));
855
+ }
856
+ }),
857
+ (v) => recordLinkablesVisitor(v, linkables)
858
+ );
859
+ }
860
+ function getConflictsForInstructionAccountsAndArgs(instruction) {
861
+ const allNames = [
862
+ ...instruction.accounts.map((account) => account.name),
863
+ ...instruction.arguments.map((argument) => argument.name)
864
+ ];
865
+ const duplicates = allNames.filter((e, i, a) => a.indexOf(e) !== i);
866
+ return [...new Set(duplicates)];
867
+ }
868
+ function renderVisitor(path, options = {}) {
869
+ return rootNodeVisitor((root) => {
870
+ if (options.deleteFolderBeforeRendering ?? true) {
871
+ deleteDirectory(path);
872
+ }
873
+ visit(root, writeRenderMapVisitor(getRenderMapVisitor(options), path));
874
+ if (options.formatCode) {
875
+ if (options.crateFolder) {
876
+ const toolchain = options.toolchain ?? "+stable";
877
+ runFormatter("cargo", [toolchain, "fmt", "--manifest-path", `${options.crateFolder}/Cargo.toml`]);
878
+ } else {
879
+ logWarn("No crate folder specified, skipping formatting.");
880
+ }
881
+ }
882
+ });
883
+ }
884
+ function runFormatter(cmd, args) {
885
+ const { stdout, stderr, error } = spawnSync(cmd, args);
886
+ if (error?.message?.includes("ENOENT")) {
887
+ logWarn(`Could not find ${cmd}, skipping formatting.`);
888
+ return;
889
+ }
890
+ if (stdout.length > 0) {
891
+ logWarn(`(cargo-fmt) ${stdout || error}`);
892
+ }
893
+ if (stderr.length > 0) {
894
+ logError(`(cargo-fmt) ${stderr || error}`);
895
+ }
896
+ }
897
+
898
+ export { ImportMap, getRenderMapVisitor, getTypeManifestVisitor, renderVisitor };
899
+ //# sourceMappingURL=index.node.mjs.map
900
+ //# sourceMappingURL=index.node.mjs.map