@codama/renderers-rust 1.2.5 → 1.2.7

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/README.md CHANGED
@@ -15,24 +15,23 @@ This package generates Rust clients from your Codama IDLs.
15
15
  pnpm install @codama/renderers-rust
16
16
  ```
17
17
 
18
- > [!NOTE]
19
- > This package is **not** included in the main [`codama`](../library) package.
20
- >
21
- > However, note that the [`renderers`](../renderers) package re-exports the `renderVisitor` function of this package as `renderRustVisitor`.
22
-
23
18
  ## Usage
24
19
 
25
- Once you have a Codama IDL, you can use the `renderVisitor` of this package to generate Rust clients. You will need to provide the base directory where the generated files will be saved and an optional set of options to customize the output.
26
-
27
- ```ts
28
- // node ./codama.mjs
29
- import { renderVisitor } from '@codama/renderers-rust';
30
-
31
- const pathToGeneratedFolder = path.join(__dirname, 'clients', 'rust', 'src', 'generated');
32
- const options = {}; // See below.
33
- codama.accept(renderVisitor(pathToGeneratedFolder, options));
20
+ Add the following script to your Codama configuration file.
21
+
22
+ ```json
23
+ {
24
+ "scripts": {
25
+ "rust": {
26
+ "from": "@codama/renderers-rust",
27
+ "args": ["clients/rust/src/generated"]
28
+ }
29
+ }
30
+ }
34
31
  ```
35
32
 
33
+ An object can be passed as a second argument to further configure the renderer. See the [Options](#options) section below for more details.
34
+
36
35
  ## Options
37
36
 
38
37
  The `renderVisitor` accepts the following options.
@@ -386,6 +386,10 @@ var render = (template, context, options) => {
386
386
  env.addFilter("kebabCase", import_nodes3.kebabCase);
387
387
  env.addFilter("titleCase", import_nodes3.titleCase);
388
388
  env.addFilter("rustDocblock", rustDocblock);
389
+ env.addFilter("hasTrait", (traits, ...traitNames) => {
390
+ if (typeof traits !== "string") return false;
391
+ return traitNames.some((traitName) => traits.includes(traitName));
392
+ });
389
393
  return env.render(template, context);
390
394
  };
391
395
 
@@ -490,14 +494,51 @@ function extractFullyQualifiedNames(traits, imports) {
490
494
  return trait.slice(index + 2);
491
495
  });
492
496
  }
497
+ function getSerdeFieldAttribute(serdeWith, node, userOptions = {}) {
498
+ (0, import_nodes4.assertIsNode)(node, ["accountNode", "definedTypeNode", "instructionNode"]);
499
+ const options = { ...DEFAULT_TRAIT_OPTIONS, ...userOptions };
500
+ const nodeType = getNodeType(node);
501
+ if (nodeType === "alias") {
502
+ return "";
503
+ }
504
+ const sanitizedOverrides = Object.fromEntries(
505
+ Object.entries(options.overrides).map(([key, value]) => [(0, import_nodes4.camelCase)(key), value])
506
+ );
507
+ const nodeOverrides = sanitizedOverrides[node.name];
508
+ const allTraits = nodeOverrides === void 0 ? getDefaultTraits(nodeType, options) : nodeOverrides;
509
+ const hasSerdeSerialize = allTraits.some((t) => t === "serde::Serialize" || t === "Serialize");
510
+ const hasSerdeDeserialize = allTraits.some((t) => t === "serde::Deserialize" || t === "Deserialize");
511
+ if (!hasSerdeSerialize && !hasSerdeDeserialize) {
512
+ return "";
513
+ }
514
+ const partitionedTraits = partitionTraitsInFeatures(allTraits, options.featureFlags);
515
+ const featuredTraits = partitionedTraits[1];
516
+ let serdeFeatureName;
517
+ for (const [feature, traits] of Object.entries(featuredTraits)) {
518
+ if (traits.some(
519
+ (t) => t === "serde::Serialize" || t === "serde::Deserialize" || t === "Serialize" || t === "Deserialize"
520
+ )) {
521
+ serdeFeatureName = feature;
522
+ break;
523
+ }
524
+ }
525
+ if (serdeFeatureName) {
526
+ return `#[cfg_attr(feature = "${serdeFeatureName}", serde(with = "${serdeWith}"))]
527
+ `;
528
+ } else {
529
+ return `#[serde(with = "${serdeWith}")]
530
+ `;
531
+ }
532
+ }
493
533
 
494
534
  // src/getTypeManifestVisitor.ts
495
535
  function getTypeManifestVisitor(options) {
496
- const { getImportFrom, getTraitsFromNode: getTraitsFromNode2 } = options;
536
+ const { getImportFrom, getTraitsFromNode: getTraitsFromNode2, traitOptions } = options;
497
537
  let parentName = options.parentName ?? null;
498
538
  let nestedStruct = options.nestedStruct ?? false;
499
539
  let inlineStruct = false;
500
540
  let parentSize = null;
541
+ let parentNode = null;
501
542
  return (0, import_visitors_core3.pipe)(
502
543
  (0, import_visitors_core3.mergeVisitor)(
503
544
  () => ({ imports: new ImportMap(), nestedStructs: [], type: "" }),
@@ -510,10 +551,12 @@ function getTypeManifestVisitor(options) {
510
551
  (v) => (0, import_visitors_core3.extendVisitor)(v, {
511
552
  visitAccount(account, { self }) {
512
553
  parentName = (0, import_nodes5.pascalCase)(account.name);
554
+ parentNode = account;
513
555
  const manifest = (0, import_visitors_core3.visit)(account.data, self);
514
556
  const traits = getTraitsFromNode2(account);
515
557
  manifest.imports.mergeWith(traits.imports);
516
558
  parentName = null;
559
+ parentNode = null;
517
560
  return {
518
561
  ...manifest,
519
562
  type: traits.render + manifest.type
@@ -588,10 +631,12 @@ function getTypeManifestVisitor(options) {
588
631
  },
589
632
  visitDefinedType(definedType, { self }) {
590
633
  parentName = (0, import_nodes5.pascalCase)(definedType.name);
634
+ parentNode = definedType;
591
635
  const manifest = (0, import_visitors_core3.visit)(definedType.type, self);
592
636
  const traits = getTraitsFromNode2(definedType);
593
637
  manifest.imports.mergeWith(traits.imports);
594
638
  parentName = null;
639
+ parentNode = null;
595
640
  const renderedType = (0, import_nodes5.isNode)(definedType.type, ["enumTypeNode", "structTypeNode"]) ? manifest.type : `pub type ${(0, import_nodes5.pascalCase)(definedType.name)} = ${manifest.type};`;
596
641
  return { ...manifest, type: `${traits.render}${renderedType}` };
597
642
  },
@@ -638,10 +683,18 @@ function getTypeManifestVisitor(options) {
638
683
  const childManifest = (0, import_visitors_core3.visit)(enumTupleVariantType.tuple, self);
639
684
  parentName = originalParentName;
640
685
  let derive = "";
641
- if (childManifest.type === "(Pubkey)") {
642
- derive = '#[cfg_attr(feature = "serde", serde(with = "serde_with::As::<serde_with::DisplayFromStr>"))]\n';
643
- } else if (childManifest.type === "(Vec<Pubkey>)") {
644
- derive = '#[cfg_attr(feature = "serde", serde(with = "serde_with::As::<Vec<serde_with::DisplayFromStr>>"))]\n';
686
+ if (parentNode && childManifest.type === "(Pubkey)") {
687
+ derive = getSerdeFieldAttribute(
688
+ "serde_with::As::<serde_with::DisplayFromStr>",
689
+ parentNode,
690
+ traitOptions
691
+ );
692
+ } else if (parentNode && childManifest.type === "(Vec<Pubkey>)") {
693
+ derive = getSerdeFieldAttribute(
694
+ "serde_with::As::<Vec<serde_with::DisplayFromStr>>",
695
+ parentNode,
696
+ traitOptions
697
+ );
645
698
  }
646
699
  return {
647
700
  ...childManifest,
@@ -788,14 +841,28 @@ ${variantNames}
788
841
  const docblock = rustDocblock((0, import_nodes5.parseDocs)(structFieldType.docs));
789
842
  const resolvedNestedType = (0, import_nodes5.resolveNestedTypeNode)(structFieldType.type);
790
843
  let derive = "";
791
- if (fieldManifest.type === "Pubkey") {
792
- derive = '#[cfg_attr(feature = "serde", serde(with = "serde_with::As::<serde_with::DisplayFromStr>"))]\n';
793
- } else if (fieldManifest.type === "Vec<Pubkey>") {
794
- derive = '#[cfg_attr(feature = "serde", serde(with = "serde_with::As::<Vec<serde_with::DisplayFromStr>>"))]\n';
795
- } else if ((0, import_nodes5.isNode)(resolvedNestedType, "arrayTypeNode") && (0, import_nodes5.isNode)(resolvedNestedType.count, "fixedCountNode") && resolvedNestedType.count.value > 32) {
796
- derive = '#[cfg_attr(feature = "serde", serde(with = "serde_big_array::BigArray"))]\n';
797
- } else if ((0, import_nodes5.isNode)(resolvedNestedType, ["bytesTypeNode", "stringTypeNode"]) && (0, import_nodes5.isNode)(structFieldType.type, "fixedSizeTypeNode") && structFieldType.type.size > 32) {
798
- derive = '#[cfg_attr(feature = "serde", serde(with = "serde_with::As::<serde_with::Bytes>"))]\n';
844
+ if (parentNode) {
845
+ if (fieldManifest.type === "Pubkey") {
846
+ derive = getSerdeFieldAttribute(
847
+ "serde_with::As::<serde_with::DisplayFromStr>",
848
+ parentNode,
849
+ traitOptions
850
+ );
851
+ } else if (fieldManifest.type === "Vec<Pubkey>") {
852
+ derive = getSerdeFieldAttribute(
853
+ "serde_with::As::<Vec<serde_with::DisplayFromStr>>",
854
+ parentNode,
855
+ traitOptions
856
+ );
857
+ } else if ((0, import_nodes5.isNode)(resolvedNestedType, "arrayTypeNode") && (0, import_nodes5.isNode)(resolvedNestedType.count, "fixedCountNode") && resolvedNestedType.count.value > 32) {
858
+ derive = getSerdeFieldAttribute("serde_big_array::BigArray", parentNode, traitOptions);
859
+ } else if ((0, import_nodes5.isNode)(resolvedNestedType, ["bytesTypeNode", "stringTypeNode"]) && (0, import_nodes5.isNode)(structFieldType.type, "fixedSizeTypeNode") && structFieldType.type.size > 32) {
860
+ derive = getSerdeFieldAttribute(
861
+ "serde_with::As::<serde_with::Bytes>",
862
+ parentNode,
863
+ traitOptions
864
+ );
865
+ }
799
866
  }
800
867
  return {
801
868
  ...fieldManifest,
@@ -868,7 +935,11 @@ function getRenderMapVisitor(options = {}) {
868
935
  const dependencyMap = options.dependencyMap ?? {};
869
936
  const getImportFrom = getImportFromFactory(options.linkOverrides ?? {});
870
937
  const getTraitsFromNode2 = getTraitsFromNodeFactory(options.traitOptions);
871
- const typeManifestVisitor = getTypeManifestVisitor({ getImportFrom, getTraitsFromNode: getTraitsFromNode2 });
938
+ const typeManifestVisitor = getTypeManifestVisitor({
939
+ getImportFrom,
940
+ getTraitsFromNode: getTraitsFromNode2,
941
+ traitOptions: options.traitOptions
942
+ });
872
943
  const anchorTraits = options.anchorTraits ?? true;
873
944
  return (0, import_visitors_core4.pipe)(
874
945
  (0, import_visitors_core4.staticVisitor)(() => (0, import_renderers_core.createRenderMap)(), {
@@ -1091,8 +1162,13 @@ function renderVisitor(path, options = {}) {
1091
1162
  (0, import_visitors_core5.visit)(root, (0, import_renderers_core2.writeRenderMapVisitor)(getRenderMapVisitor(options), path));
1092
1163
  if (options.formatCode) {
1093
1164
  if (options.crateFolder) {
1094
- const toolchain = options.toolchain ?? "+stable";
1095
- runFormatter("cargo", [toolchain, "fmt", "--manifest-path", `${options.crateFolder}/Cargo.toml`]);
1165
+ const removeFalsy = (arg) => Boolean(arg);
1166
+ runFormatter(
1167
+ "cargo",
1168
+ [options.toolchain, "fmt", "--manifest-path", `${options.crateFolder}/Cargo.toml`].filter(
1169
+ removeFalsy
1170
+ )
1171
+ );
1096
1172
  } else {
1097
1173
  (0, import_errors4.logWarn)("No crate folder specified, skipping formatting.");
1098
1174
  }