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