Generate Rust topLevel nodes (account, constant, error, event, instruction*, pda, program, root, …)#102
Merged
Merged
Conversation
This was referenced Jun 10, 2026
Merged
Member
Author
This was referenced Jun 10, 2026
Member
Author
Merge activity
|
…ction*, pda, program, root, …) This PR teaches the generator to emit the 14 `topLevel` nodes (plus 2 inline category unions and a `CODAMA_VERSION` constant) directly to the root of `codama-nodes/src/generated/`. It is the largest of the node-generation PRs because topLevel nodes are direct `Node` variants (not wrapped through a category union), the spec's `literal` and previously-anonymous `programNode.origin` are first wired through to real Rust types, and the optional-scalar / `Option<T>` reconciliation cascades across `codama-nodes`, `codama-attributes`, and `codama-korok-visitors`. The generator gains four small features. First, `CategoryRouting` is a discriminated union with `wrapped` and `direct` modes; the `direct` mode (used by `topLevel`) deliberately emits no `impl From<Self> for crate::Node`, because the `Node` enum's `#[derive(From)]` auto-generates the impl and an explicit one would conflict. Second, `literal` TypeExpr renders as `String`; the literal value lives in the hand-written `Default` for that node. Third, the Default-derive heuristic now fires for any struct whose fields are all unconditionally Default-able — `Option`/`Vec`/`Docs`/scalars/`String`/`CamelCaseString`-from-`stringIdentifier`/`Box<Option<_>>`. The rule is sound by construction: required `union`/`enumeration`/`node`/`nestedUnion`/`literalUnion` fields and `FIELD_TYPE_OVERRIDES` targets are all treated as opaque and disqualify the struct. As a side effect, 28 previously-no-Default nodes across `link`/`count`/`discriminator`/`value`/`contextualValue` also gain a derived `Default` — pure addition, no consumer breakage. Fourth, a new `generated/codama_version.rs` page emits `pub const CODAMA_VERSION: &str = "<spec.version>";` pinned to the spec version at generation time, mirroring the JS `CODAMA_VERSION` constant used by `rootNode()`; `RootNode::default()` now uses this constant instead of the stale `"1.0.0"` placeholder, bringing the serialized form in line with JS codama. The reconciliation is the bulk of the diff. Spec-driven type changes: `errorNode.code: u32` (spec width, was `usize`), `accountNode.size: Option<u64>`, `programNode.origin: Option<ProgramOrigin>` (the enum generated in PR #9). The locked optional-scalar rule (optional scalar → `Option<T>` uniformly) flips ~48 read/write sites: `is_optional`, `is_writable` (remaining-accounts only), `is_signer` (remaining-accounts only, optional), `subtract`, `message`, and `optional_account_strategy`. The box-all-union rule wraps every direct union field, and the corresponding constructors gain `Box::new(...)` while tests deref through `*node.field`. The `InstructionByteDeltaValue` inline-union variants rename to `{AccountLink, ArgumentValue, NumberValue, ResolverValue}` (the common PascalCase suffix across its leaves is just `Node`); `InstructionRemainingAccountsValue` already matched the derived rule. Two nodes keep a hand-written `Default` for structural reasons: `InstructionStatusNode` has a required `lifecycle: InstructionLifecycle` (opaque enum), and `RootNode` pins both `standard: "codama"` and `version: CODAMA_VERSION`. Both are marked `#[allow(clippy::derivable_impls)]`. A new self-import filter in `nodeStructFragment.ts` strips the redundant `crate::Self` import that self-referential nodes (e.g. `instructionNode.subInstructions: Vec<InstructionNode>`) would otherwise accumulate. `ProgramNode::new()` defaults the program's own version to `"0.0.0"` to match the JS factory; `ProgramNode::default()` stays at `""` so `SetProgramMetadataVisitor`'s Cargo.toml-override path (`if program.version.is_empty()`) is unaffected. Workspace-wide: 1008 cargo tests pass unchanged (incl. the two golden-IDL tests updated for the new `"version": "1.6.0"` line), fmt and clippy stay clean, and `pnpm test` grew by 10 to 140 (new topLevel + Default-heuristic + literal + CODAMA_VERSION coverage). `pnpm generate` round-trips deterministically. The override surface is unchanged (3 maps, `FIELD_TYPE_OVERRIDES` still has 1 entry).
d17789f to
4906665
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

This PR teaches the generator to emit the 14
topLevelnodes (plus 2 inline category unions and aCODAMA_VERSIONconstant) directly to the root ofcodama-nodes/src/generated/. It is the largest of the node-generation PRs because topLevel nodes are directNodevariants (not wrapped through a category union), the spec'sliteraland previously-anonymousprogramNode.originare first wired through to real Rust types, and the optional-scalar /Option<T>reconciliation cascades acrosscodama-nodes,codama-attributes, andcodama-korok-visitors.The generator gains four small features. First,
CategoryRoutingis a discriminated union withwrappedanddirectmodes; thedirectmode (used bytopLevel) deliberately emits noimpl From<Self> for crate::Node, because theNodeenum's#[derive(From)]auto-generates the impl and an explicit one would conflict. Second,literalTypeExpr renders asString; the literal value lives in the hand-writtenDefaultfor that node. Third, the Default-derive heuristic now fires for any struct whose fields are all unconditionally Default-able —Option/Vec/Docs/scalars/String/CamelCaseString-from-stringIdentifier/Box<Option<_>>. The rule is sound by construction: requiredunion/enumeration/node/nestedUnion/literalUnionfields andFIELD_TYPE_OVERRIDEStargets are all treated as opaque and disqualify the struct. As a side effect, 28 previously-no-Default nodes acrosslink/count/discriminator/value/contextualValuealso gain a derivedDefault— pure addition, no consumer breakage. Fourth, a newgenerated/codama_version.rspage emitspub const CODAMA_VERSION: &str = "<spec.version>";pinned to the spec version at generation time, mirroring the JSCODAMA_VERSIONconstant used byrootNode();RootNode::default()now uses this constant instead of the stale"1.0.0"placeholder, bringing the serialized form in line with JS codama.The reconciliation is the bulk of the diff. Spec-driven type changes:
errorNode.code: u32(spec width, wasusize),accountNode.size: Option<u64>,programNode.origin: Option<ProgramOrigin>(the enum generated in PR #9). The locked optional-scalar rule (optional scalar →Option<T>uniformly) flips ~48 read/write sites:is_optional,is_writable(remaining-accounts only),is_signer(remaining-accounts only, optional),subtract,message, andoptional_account_strategy. The box-all-union rule wraps every direct union field, and the corresponding constructors gainBox::new(...)while tests deref through*node.field. TheInstructionByteDeltaValueinline-union variants rename to{AccountLink, ArgumentValue, NumberValue, ResolverValue}(the common PascalCase suffix across its leaves is justNode);InstructionRemainingAccountsValuealready matched the derived rule.Two nodes keep a hand-written
Defaultfor structural reasons:InstructionStatusNodehas a requiredlifecycle: InstructionLifecycle(opaque enum), andRootNodepins bothstandard: "codama"andversion: CODAMA_VERSION. Both are marked#[allow(clippy::derivable_impls)]. A new self-import filter innodeStructFragment.tsstrips the redundantcrate::Selfimport that self-referential nodes (e.g.instructionNode.subInstructions: Vec<InstructionNode>) would otherwise accumulate.ProgramNode::new()defaults the program's own version to"0.0.0"to match the JS factory;ProgramNode::default()stays at""soSetProgramMetadataVisitor's Cargo.toml-override path (if program.version.is_empty()) is unaffected.Workspace-wide: 1008 cargo tests pass unchanged (incl. the two golden-IDL tests updated for the new
"version": "1.6.0"line), fmt and clippy stay clean, andpnpm testgrew by 10 to 140 (new topLevel + Default-heuristic + literal + CODAMA_VERSION coverage).pnpm generateround-trips deterministically. The override surface is unchanged (3 maps,FIELD_TYPE_OVERRIDESstill has 1 entry).