From ed60255073b9b591deaba15e7ccfb3134ee9ff5b Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Fri, 5 Jun 2026 09:44:00 +0100 Subject: [PATCH] Generate contextualValue nodes; align Rust variant names with the derived rule This PR brings the `contextualValue` category into the generator (10 nodes, 6 emittable inline unions, and the `RegisteredContextualValueNode` category union via the `RegisteredNodes` derive). All inline unions flow through the PR #6 derived rule with no per-union config. To keep the generator override-free, the Rust crate is updated to match the derived variant names: `PdaValuePda::{Linked, Nested}` becomes `{PdaLink, Pda}` (`pdaLinkNode` and `pdaNode` share only the `Node` suffix, so that's all the rule can strip), and the 23 `*ValueNode` variants of `InstructionInputValueNode` gain a `Value` suffix while `ProgramLink` stays unchanged (members share only `Node` as a common suffix, since `programLinkNode` mixes with value nodes). The `From` / `TryFrom<\u2026 > for ValueNode` bridges in `shared/instruction_input_value_node.rs` are preserved and updated for the new variant names; the enum itself moves to `generated/contextual_value_nodes/`. `resolverValueNode.dependsOn` changes from `Option>` to bare `Vec` to match the optional-array convention every other node uses. Hand-written `contextual_value_nodes/*.rs` files shrink to constructors and `#[cfg(test)] mod tests`. The `RegisteredNodes` renderer is fixed to compute its variant-name strip suffix from the actual leaf members (`ValueNode`) rather than the union's own pascal-case name (`ContextualValueNode`); this was a latent bug that worked by coincidence for `value` but would have produced wrong variant names for `contextualValue`. --- .../account_bump_value_node.rs | 21 +--- .../account_value_node.rs | 21 +--- .../argument_value_node.rs | 21 +--- .../conditional_value_node.rs | 59 +++------- .../contextual_value_node.rs | 26 +---- .../identity_value_node.rs | 12 +- .../src/contextual_value_nodes/mod.rs | 12 -- .../payer_value_node.rs | 12 +- .../pda_seed_value_node.rs | 62 ++-------- .../contextual_value_nodes/pda_value_node.rs | 50 ++------- .../program_id_value_node.rs | 12 +- .../resolver_value_node.rs | 46 ++------ .../account_bump_value_node.rs | 20 ++++ .../account_value_node.rs | 20 ++++ .../argument_value_node.rs | 20 ++++ .../conditional_value_condition.rs | 19 ++++ .../conditional_value_node.rs | 20 ++++ .../contextual_value_node.rs | 23 ++++ .../identity_value_node.rs | 11 ++ .../instruction_input_value_node.rs | 36 ++++++ .../generated/contextual_value_nodes/mod.rs | 35 ++++++ .../payer_value_node.rs | 11 ++ .../pda_seed_value_node.rs | 23 ++++ .../pda_seed_value_value.rs | 27 +++++ .../contextual_value_nodes/pda_value_node.rs | 17 +++ .../contextual_value_nodes/pda_value_pda.rs | 17 +++ .../pda_value_program_id.rs | 17 +++ .../program_id_value_node.rs | 11 ++ .../resolver_dependency.rs | 17 +++ .../resolver_value_node.rs | 26 +++++ codama-nodes/src/generated/mod.rs | 2 + codama-nodes/src/instruction_account_node.rs | 6 +- codama-nodes/src/instruction_argument_node.rs | 6 +- codama-nodes/src/lib.rs | 1 - .../shared/instruction_input_value_node.rs | 106 ++++++------------ codama-nodes/src/shared/mod.rs | 1 - spec-generators/src/defaults.ts | 1 + .../src/fragments/registeredUnionPage.ts | 9 +- spec-generators/src/unions.ts | 38 +++++-- spec-generators/test/generate.test.ts | 82 +++++++++++++- 40 files changed, 582 insertions(+), 394 deletions(-) create mode 100644 codama-nodes/src/generated/contextual_value_nodes/account_bump_value_node.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/account_value_node.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/argument_value_node.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/conditional_value_condition.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/conditional_value_node.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/contextual_value_node.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/identity_value_node.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/instruction_input_value_node.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/mod.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/payer_value_node.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/pda_seed_value_node.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/pda_seed_value_value.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/pda_value_node.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/pda_value_pda.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/pda_value_program_id.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/program_id_value_node.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/resolver_dependency.rs create mode 100644 codama-nodes/src/generated/contextual_value_nodes/resolver_value_node.rs diff --git a/codama-nodes/src/contextual_value_nodes/account_bump_value_node.rs b/codama-nodes/src/contextual_value_nodes/account_bump_value_node.rs index 871efb72..d2a6430e 100644 --- a/codama-nodes/src/contextual_value_nodes/account_bump_value_node.rs +++ b/codama-nodes/src/contextual_value_nodes/account_bump_value_node.rs @@ -1,17 +1,4 @@ -use crate::{CamelCaseString, HasName}; -use codama_nodes_derive::node; - -#[node] -pub struct AccountBumpValueNode { - // Data. - pub name: CamelCaseString, -} - -impl From for crate::Node { - fn from(val: AccountBumpValueNode) -> Self { - crate::Node::ContextualValue(val.into()) - } -} +use crate::{AccountBumpValueNode, CamelCaseString}; impl AccountBumpValueNode { pub fn new(name: T) -> Self @@ -22,12 +9,6 @@ impl AccountBumpValueNode { } } -impl HasName for AccountBumpValueNode { - fn name(&self) -> &CamelCaseString { - &self.name - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/codama-nodes/src/contextual_value_nodes/account_value_node.rs b/codama-nodes/src/contextual_value_nodes/account_value_node.rs index 19092225..88ea617d 100644 --- a/codama-nodes/src/contextual_value_nodes/account_value_node.rs +++ b/codama-nodes/src/contextual_value_nodes/account_value_node.rs @@ -1,17 +1,4 @@ -use crate::{CamelCaseString, HasName}; -use codama_nodes_derive::node; - -#[node] -pub struct AccountValueNode { - // Data. - pub name: CamelCaseString, -} - -impl From for crate::Node { - fn from(val: AccountValueNode) -> Self { - crate::Node::ContextualValue(val.into()) - } -} +use crate::{AccountValueNode, CamelCaseString}; impl AccountValueNode { pub fn new(name: T) -> Self @@ -22,12 +9,6 @@ impl AccountValueNode { } } -impl HasName for AccountValueNode { - fn name(&self) -> &CamelCaseString { - &self.name - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/codama-nodes/src/contextual_value_nodes/argument_value_node.rs b/codama-nodes/src/contextual_value_nodes/argument_value_node.rs index 893211f5..cacb3f13 100644 --- a/codama-nodes/src/contextual_value_nodes/argument_value_node.rs +++ b/codama-nodes/src/contextual_value_nodes/argument_value_node.rs @@ -1,17 +1,4 @@ -use crate::{CamelCaseString, HasName}; -use codama_nodes_derive::node; - -#[node] -pub struct ArgumentValueNode { - // Data. - pub name: CamelCaseString, -} - -impl From for crate::Node { - fn from(val: ArgumentValueNode) -> Self { - crate::Node::ContextualValue(val.into()) - } -} +use crate::{ArgumentValueNode, CamelCaseString}; impl ArgumentValueNode { pub fn new(name: T) -> Self @@ -22,12 +9,6 @@ impl ArgumentValueNode { } } -impl HasName for ArgumentValueNode { - fn name(&self) -> &CamelCaseString { - &self.name - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/codama-nodes/src/contextual_value_nodes/conditional_value_node.rs b/codama-nodes/src/contextual_value_nodes/conditional_value_node.rs index ca578262..74933d17 100644 --- a/codama-nodes/src/contextual_value_nodes/conditional_value_node.rs +++ b/codama-nodes/src/contextual_value_nodes/conditional_value_node.rs @@ -1,60 +1,31 @@ -use crate::{ - AccountValueNode, ArgumentValueNode, InstructionInputValueNode, ResolverValueNode, ValueNode, -}; -use codama_nodes_derive::{node, node_union}; - -#[node] -pub struct ConditionalValueNode { - // Children. - pub condition: ConditionalValueCondition, - #[serde(skip_serializing_if = "crate::is_default")] - pub value: Option, - #[serde(skip_serializing_if = "crate::is_default")] - pub if_true: Box>, - #[serde(skip_serializing_if = "crate::is_default")] - pub if_false: Box>, -} - -impl From for crate::Node { - fn from(val: ConditionalValueNode) -> Self { - crate::Node::ContextualValue(val.into()) - } -} - -#[node_union] -pub enum ConditionalValueCondition { - Account(AccountValueNode), - Argument(ArgumentValueNode), - Resolver(ResolverValueNode), -} - #[cfg(test)] mod tests { - use crate::NumberValueNode; - - use super::*; + use crate::{ + AccountValueNode, ArgumentValueNode, ConditionalValueCondition, ConditionalValueNode, + InstructionInputValueNode, NumberValueNode, ValueNode, + }; #[test] fn direct_instantiation() { let node = ConditionalValueNode { - condition: ArgumentValueNode::new("myArgument").into(), - value: Some(NumberValueNode::new(42).into()), + condition: Box::new(ArgumentValueNode::new("myArgument").into()), + value: Box::new(Some(NumberValueNode::new(42).into())), if_true: Box::new(Some(AccountValueNode::new("myOtherAccount").into())), if_false: Box::new(None), }; assert_eq!( - node.condition, + *node.condition, ConditionalValueCondition::Argument(ArgumentValueNode::new("myArgument")) ); assert_eq!( - node.value, + *node.value, Some(ValueNode::Number(NumberValueNode::new(42))) ); assert_eq!( *node.if_true, - Some(InstructionInputValueNode::Account(AccountValueNode::new( - "myOtherAccount" - ))) + Some(InstructionInputValueNode::AccountValue( + AccountValueNode::new("myOtherAccount") + )) ); assert_eq!(*node.if_false, None); } @@ -62,8 +33,8 @@ mod tests { #[test] fn to_json() { let node = ConditionalValueNode { - condition: ArgumentValueNode::new("myArgument").into(), - value: Some(NumberValueNode::new(42).into()), + condition: Box::new(ArgumentValueNode::new("myArgument").into()), + value: Box::new(Some(NumberValueNode::new(42).into())), if_true: Box::new(Some(AccountValueNode::new("myOtherAccount").into())), if_false: Box::new(None), }; @@ -81,8 +52,8 @@ mod tests { assert_eq!( node, ConditionalValueNode { - condition: ArgumentValueNode::new("myArgument").into(), - value: Some(NumberValueNode::new(42u32).into()), + condition: Box::new(ArgumentValueNode::new("myArgument").into()), + value: Box::new(Some(NumberValueNode::new(42u32).into())), if_true: Box::new(Some(AccountValueNode::new("myOtherAccount").into())), if_false: Box::new(None), } diff --git a/codama-nodes/src/contextual_value_nodes/contextual_value_node.rs b/codama-nodes/src/contextual_value_nodes/contextual_value_node.rs index f7febac0..e9db638f 100644 --- a/codama-nodes/src/contextual_value_nodes/contextual_value_node.rs +++ b/codama-nodes/src/contextual_value_nodes/contextual_value_node.rs @@ -1,30 +1,6 @@ -use crate::{ - AccountBumpValueNode, AccountValueNode, ArgumentValueNode, ConditionalValueNode, HasKind, - IdentityValueNode, PayerValueNode, PdaSeedValueNode, PdaValueNode, ProgramIdValueNode, - ResolverValueNode, -}; -use codama_nodes_derive::{node_union, RegisteredNodes}; - -#[derive(RegisteredNodes)] -#[node_union] -pub enum RegisteredContextualValueNode { - Account(AccountValueNode), - AccountBump(AccountBumpValueNode), - Argument(ArgumentValueNode), - Conditional(ConditionalValueNode), - Identity(IdentityValueNode), - Payer(PayerValueNode), - Pda(PdaValueNode), - ProgramId(ProgramIdValueNode), - Resolver(ResolverValueNode), - - #[registered] - PdaSeed(PdaSeedValueNode), -} - #[cfg(test)] mod tests { - use super::*; + use crate::{ContextualValueNode, HasKind, ProgramIdValueNode, RegisteredContextualValueNode}; #[test] fn kind_from_standalone() { diff --git a/codama-nodes/src/contextual_value_nodes/identity_value_node.rs b/codama-nodes/src/contextual_value_nodes/identity_value_node.rs index 4f7688bf..3f638736 100644 --- a/codama-nodes/src/contextual_value_nodes/identity_value_node.rs +++ b/codama-nodes/src/contextual_value_nodes/identity_value_node.rs @@ -1,14 +1,4 @@ -use codama_nodes_derive::node; - -#[node] -#[derive(Copy, Default)] -pub struct IdentityValueNode {} - -impl From for crate::Node { - fn from(val: IdentityValueNode) -> Self { - crate::Node::ContextualValue(val.into()) - } -} +use crate::IdentityValueNode; impl IdentityValueNode { pub fn new() -> Self { diff --git a/codama-nodes/src/contextual_value_nodes/mod.rs b/codama-nodes/src/contextual_value_nodes/mod.rs index 587011f3..4e1c096c 100644 --- a/codama-nodes/src/contextual_value_nodes/mod.rs +++ b/codama-nodes/src/contextual_value_nodes/mod.rs @@ -9,15 +9,3 @@ mod pda_seed_value_node; mod pda_value_node; mod program_id_value_node; mod resolver_value_node; - -pub use account_bump_value_node::*; -pub use account_value_node::*; -pub use argument_value_node::*; -pub use conditional_value_node::*; -pub use contextual_value_node::*; -pub use identity_value_node::*; -pub use payer_value_node::*; -pub use pda_seed_value_node::*; -pub use pda_value_node::*; -pub use program_id_value_node::*; -pub use resolver_value_node::*; diff --git a/codama-nodes/src/contextual_value_nodes/payer_value_node.rs b/codama-nodes/src/contextual_value_nodes/payer_value_node.rs index 983b6922..c744e26f 100644 --- a/codama-nodes/src/contextual_value_nodes/payer_value_node.rs +++ b/codama-nodes/src/contextual_value_nodes/payer_value_node.rs @@ -1,14 +1,4 @@ -use codama_nodes_derive::node; - -#[node] -#[derive(Copy, Default)] -pub struct PayerValueNode {} - -impl From for crate::Node { - fn from(val: PayerValueNode) -> Self { - crate::Node::ContextualValue(val.into()) - } -} +use crate::PayerValueNode; impl PayerValueNode { pub fn new() -> Self { diff --git a/codama-nodes/src/contextual_value_nodes/pda_seed_value_node.rs b/codama-nodes/src/contextual_value_nodes/pda_seed_value_node.rs index af8f6fb7..f38c04aa 100644 --- a/codama-nodes/src/contextual_value_nodes/pda_seed_value_node.rs +++ b/codama-nodes/src/contextual_value_nodes/pda_seed_value_node.rs @@ -1,25 +1,4 @@ -use crate::{ - AccountValueNode, ArgumentValueNode, ArrayValueNode, BooleanValueNode, BytesValueNode, - CamelCaseString, ConstantValueNode, EnumValueNode, HasName, MapValueNode, NoneValueNode, - NumberValueNode, PublicKeyValueNode, SetValueNode, SomeValueNode, StringValueNode, - StructValueNode, TupleValueNode, ValueNode, -}; -use codama_nodes_derive::{node, node_union}; - -#[node] -pub struct PdaSeedValueNode { - // Data. - pub name: CamelCaseString, - - // Children. - pub value: PdaSeedValueValue, -} - -impl From for crate::Node { - fn from(val: PdaSeedValueNode) -> Self { - crate::Node::ContextualValue(val.into()) - } -} +use crate::{CamelCaseString, PdaSeedValueNode, PdaSeedValueValue, ValueNode}; impl PdaSeedValueNode { pub fn new(name: T, value: U) -> Self @@ -29,39 +8,15 @@ impl PdaSeedValueNode { { Self { name: name.into(), - value: value.into(), + value: Box::new(value.into()), } } } -impl HasName for PdaSeedValueNode { - fn name(&self) -> &CamelCaseString { - &self.name - } -} - -#[node_union] -pub enum PdaSeedValueValue { - Account(AccountValueNode), - Argument(ArgumentValueNode), - - // ValueNodes. - Array(ArrayValueNode), - Boolean(BooleanValueNode), - Bytes(BytesValueNode), - Constant(ConstantValueNode), - Enum(EnumValueNode), - Map(MapValueNode), - None(NoneValueNode), - Number(NumberValueNode), - PublicKey(PublicKeyValueNode), - Set(SetValueNode), - Some(SomeValueNode), - String(StringValueNode), - Struct(StructValueNode), - Tuple(TupleValueNode), -} - +/// Bridge from the broader `ValueNode` union into `PdaSeedValueValue`. +/// `PdaSeedValueValue` is `accountValueNode | argumentValueNode | valueNode` +/// (16 variants total) — every value-node leaf maps to its same-named +/// `PdaSeedValueValue` variant. impl From for PdaSeedValueValue { fn from(value: ValueNode) -> Self { match value { @@ -85,16 +40,15 @@ impl From for PdaSeedValueValue { #[cfg(test)] mod tests { - use crate::NumberValueNode; - use super::*; + use crate::NumberValueNode; #[test] fn new() { let node = PdaSeedValueNode::new("answer", NumberValueNode::new(42)); assert_eq!(node.name, CamelCaseString::from("answer")); assert_eq!( - node.value, + *node.value, PdaSeedValueValue::Number(NumberValueNode::new(42)) ); } diff --git a/codama-nodes/src/contextual_value_nodes/pda_value_node.rs b/codama-nodes/src/contextual_value_nodes/pda_value_node.rs index 30ee5dbf..e8c14560 100644 --- a/codama-nodes/src/contextual_value_nodes/pda_value_node.rs +++ b/codama-nodes/src/contextual_value_nodes/pda_value_node.rs @@ -1,26 +1,4 @@ -use crate::{AccountValueNode, ArgumentValueNode, PdaLinkNode, PdaNode, PdaSeedValueNode}; -use codama_nodes_derive::{node, node_union}; - -#[node] -pub struct PdaValueNode { - // Children. - pub pda: PdaValuePda, - pub seeds: Vec, - #[serde(skip_serializing_if = "crate::is_default")] - pub program_id: Box>, -} - -#[node_union] -pub enum PdaValueProgramId { - Account(AccountValueNode), - Argument(ArgumentValueNode), -} - -impl From for crate::Node { - fn from(val: PdaValueNode) -> Self { - crate::Node::ContextualValue(val.into()) - } -} +use crate::{PdaSeedValueNode, PdaValueNode, PdaValuePda, PdaValueProgramId}; impl PdaValueNode { pub fn new(pda: T, seeds: Vec) -> Self @@ -28,7 +6,7 @@ impl PdaValueNode { T: Into, { Self { - pda: pda.into(), + pda: Box::new(pda.into()), seeds, program_id: Box::new(None), } @@ -40,24 +18,20 @@ impl PdaValueNode { U: Into, { Self { - pda: pda.into(), + pda: Box::new(pda.into()), seeds, program_id: Box::new(Some(program_id.into())), } } } -#[node_union] -pub enum PdaValuePda { - Linked(PdaLinkNode), - Nested(PdaNode), -} - #[cfg(test)] mod tests { - use crate::{NumberTypeNode, NumberValueNode, PublicKeyValueNode, VariablePdaSeedNode, U32}; - use super::*; + use crate::{ + AccountValueNode, NumberTypeNode, NumberValueNode, PdaLinkNode, PdaNode, + PublicKeyValueNode, VariablePdaSeedNode, U32, + }; #[test] fn new_linked() { @@ -72,8 +46,8 @@ mod tests { ], ); assert_eq!( - node.pda, - PdaValuePda::Linked(PdaLinkNode::new("masterEdition")) + *node.pda, + PdaValuePda::PdaLink(PdaLinkNode::new("masterEdition")) ); assert_eq!( node.seeds, @@ -97,8 +71,8 @@ mod tests { vec![PdaSeedValueNode::new("value", NumberValueNode::new(42))], ); assert_eq!( - node.pda, - PdaValuePda::Nested(PdaNode::new( + *node.pda, + PdaValuePda::Pda(PdaNode::new( "counter", vec![VariablePdaSeedNode::new("value", NumberTypeNode::le(U32)).into()], )) @@ -130,7 +104,7 @@ mod tests { #[test] fn to_json_with_program_id() { let node = PdaValueNode::new_with_program_id( - PdaValuePda::Linked(PdaLinkNode::new("myPda")), + PdaValuePda::PdaLink(PdaLinkNode::new("myPda")), vec![], AccountValueNode::new("myProgramAccount"), ); diff --git a/codama-nodes/src/contextual_value_nodes/program_id_value_node.rs b/codama-nodes/src/contextual_value_nodes/program_id_value_node.rs index b6d05b05..f815b480 100644 --- a/codama-nodes/src/contextual_value_nodes/program_id_value_node.rs +++ b/codama-nodes/src/contextual_value_nodes/program_id_value_node.rs @@ -1,14 +1,4 @@ -use codama_nodes_derive::node; - -#[node] -#[derive(Copy, Default)] -pub struct ProgramIdValueNode {} - -impl From for crate::Node { - fn from(val: ProgramIdValueNode) -> Self { - crate::Node::ContextualValue(val.into()) - } -} +use crate::ProgramIdValueNode; impl ProgramIdValueNode { pub fn new() -> Self { diff --git a/codama-nodes/src/contextual_value_nodes/resolver_value_node.rs b/codama-nodes/src/contextual_value_nodes/resolver_value_node.rs index cf4dd4a5..a45ad1cf 100644 --- a/codama-nodes/src/contextual_value_nodes/resolver_value_node.rs +++ b/codama-nodes/src/contextual_value_nodes/resolver_value_node.rs @@ -1,23 +1,4 @@ -use crate::{AccountValueNode, ArgumentValueNode, CamelCaseString, Docs, HasName}; -use codama_nodes_derive::{node, node_union}; - -#[node] -pub struct ResolverValueNode { - // Data. - pub name: CamelCaseString, - #[serde(default, skip_serializing_if = "crate::is_default")] - pub docs: Docs, - - // Children. - #[serde(skip_serializing_if = "crate::is_default")] - pub depends_on: Option>, -} - -impl From for crate::Node { - fn from(val: ResolverValueNode) -> Self { - crate::Node::ContextualValue(val.into()) - } -} +use crate::{CamelCaseString, Docs, ResolverValueNode}; impl ResolverValueNode { pub fn new(name: T) -> Self @@ -27,33 +8,22 @@ impl ResolverValueNode { Self { name: name.into(), docs: Docs::default(), - depends_on: None, + depends_on: vec![], } } } -#[node_union] -pub enum ResolverDependency { - Account(AccountValueNode), - Argument(ArgumentValueNode), -} - -impl HasName for ResolverValueNode { - fn name(&self) -> &CamelCaseString { - &self.name - } -} - #[cfg(test)] mod tests { use super::*; + use crate::{AccountValueNode, ArgumentValueNode, ResolverDependency}; #[test] fn new() { let node = ResolverValueNode::new("my_resolver"); assert_eq!(node.name, CamelCaseString::new("myResolver")); assert_eq!(node.docs, Docs::default()); - assert_eq!(node.depends_on, None); + assert_eq!(node.depends_on, vec![]); } #[test] @@ -61,10 +31,10 @@ mod tests { let node = ResolverValueNode { name: "myResolver".into(), docs: vec!["I am some resolver docs.".to_string()].into(), - depends_on: Some(vec![ + depends_on: vec![ AccountValueNode::new("myDependentAccount").into(), ArgumentValueNode::new("myDependentArgument").into(), - ]), + ], }; assert_eq!(node.name, CamelCaseString::new("myResolver")); assert_eq!( @@ -73,10 +43,10 @@ mod tests { ); assert_eq!( node.depends_on, - Some(vec![ + vec![ ResolverDependency::Account(AccountValueNode::new("myDependentAccount")), ResolverDependency::Argument(ArgumentValueNode::new("myDependentArgument")), - ]) + ] ); } diff --git a/codama-nodes/src/generated/contextual_value_nodes/account_bump_value_node.rs b/codama-nodes/src/generated/contextual_value_nodes/account_bump_value_node.rs new file mode 100644 index 00000000..4fedf71f --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/account_bump_value_node.rs @@ -0,0 +1,20 @@ +use crate::{CamelCaseString, HasName}; +use codama_nodes_derive::node; + +#[node] +pub struct AccountBumpValueNode { + // Data. + pub name: CamelCaseString, +} + +impl From for crate::Node { + fn from(val: AccountBumpValueNode) -> Self { + crate::Node::ContextualValue(val.into()) + } +} + +impl HasName for AccountBumpValueNode { + fn name(&self) -> &CamelCaseString { + &self.name + } +} diff --git a/codama-nodes/src/generated/contextual_value_nodes/account_value_node.rs b/codama-nodes/src/generated/contextual_value_nodes/account_value_node.rs new file mode 100644 index 00000000..699b81f5 --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/account_value_node.rs @@ -0,0 +1,20 @@ +use crate::{CamelCaseString, HasName}; +use codama_nodes_derive::node; + +#[node] +pub struct AccountValueNode { + // Data. + pub name: CamelCaseString, +} + +impl From for crate::Node { + fn from(val: AccountValueNode) -> Self { + crate::Node::ContextualValue(val.into()) + } +} + +impl HasName for AccountValueNode { + fn name(&self) -> &CamelCaseString { + &self.name + } +} diff --git a/codama-nodes/src/generated/contextual_value_nodes/argument_value_node.rs b/codama-nodes/src/generated/contextual_value_nodes/argument_value_node.rs new file mode 100644 index 00000000..ccc92402 --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/argument_value_node.rs @@ -0,0 +1,20 @@ +use crate::{CamelCaseString, HasName}; +use codama_nodes_derive::node; + +#[node] +pub struct ArgumentValueNode { + // Data. + pub name: CamelCaseString, +} + +impl From for crate::Node { + fn from(val: ArgumentValueNode) -> Self { + crate::Node::ContextualValue(val.into()) + } +} + +impl HasName for ArgumentValueNode { + fn name(&self) -> &CamelCaseString { + &self.name + } +} diff --git a/codama-nodes/src/generated/contextual_value_nodes/conditional_value_condition.rs b/codama-nodes/src/generated/contextual_value_nodes/conditional_value_condition.rs new file mode 100644 index 00000000..702b4bca --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/conditional_value_condition.rs @@ -0,0 +1,19 @@ +use crate::{AccountValueNode, ArgumentValueNode, CamelCaseString, HasName, ResolverValueNode}; +use codama_nodes_derive::node_union; + +#[node_union] +pub enum ConditionalValueCondition { + Account(AccountValueNode), + Argument(ArgumentValueNode), + Resolver(ResolverValueNode), +} + +impl HasName for ConditionalValueCondition { + fn name(&self) -> &CamelCaseString { + match self { + ConditionalValueCondition::Account(node) => node.name(), + ConditionalValueCondition::Argument(node) => node.name(), + ConditionalValueCondition::Resolver(node) => node.name(), + } + } +} diff --git a/codama-nodes/src/generated/contextual_value_nodes/conditional_value_node.rs b/codama-nodes/src/generated/contextual_value_nodes/conditional_value_node.rs new file mode 100644 index 00000000..b44900c9 --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/conditional_value_node.rs @@ -0,0 +1,20 @@ +use crate::{ConditionalValueCondition, InstructionInputValueNode, ValueNode}; +use codama_nodes_derive::node; + +#[node] +pub struct ConditionalValueNode { + // Children. + pub condition: Box, + #[serde(skip_serializing_if = "crate::is_default")] + pub value: Box>, + #[serde(skip_serializing_if = "crate::is_default")] + pub if_true: Box>, + #[serde(skip_serializing_if = "crate::is_default")] + pub if_false: Box>, +} + +impl From for crate::Node { + fn from(val: ConditionalValueNode) -> Self { + crate::Node::ContextualValue(val.into()) + } +} diff --git a/codama-nodes/src/generated/contextual_value_nodes/contextual_value_node.rs b/codama-nodes/src/generated/contextual_value_nodes/contextual_value_node.rs new file mode 100644 index 00000000..0a7e3e38 --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/contextual_value_node.rs @@ -0,0 +1,23 @@ +use crate::{ + AccountBumpValueNode, AccountValueNode, ArgumentValueNode, ConditionalValueNode, HasKind, + IdentityValueNode, PayerValueNode, PdaSeedValueNode, PdaValueNode, ProgramIdValueNode, + ResolverValueNode, +}; +use codama_nodes_derive::{node_union, RegisteredNodes}; + +#[derive(RegisteredNodes)] +#[node_union] +pub enum RegisteredContextualValueNode { + Account(AccountValueNode), + AccountBump(AccountBumpValueNode), + Argument(ArgumentValueNode), + Conditional(ConditionalValueNode), + Identity(IdentityValueNode), + Payer(PayerValueNode), + Pda(PdaValueNode), + ProgramId(ProgramIdValueNode), + Resolver(ResolverValueNode), + + #[registered] + PdaSeed(PdaSeedValueNode), +} diff --git a/codama-nodes/src/generated/contextual_value_nodes/identity_value_node.rs b/codama-nodes/src/generated/contextual_value_nodes/identity_value_node.rs new file mode 100644 index 00000000..4aa34eee --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/identity_value_node.rs @@ -0,0 +1,11 @@ +use codama_nodes_derive::node; + +#[node] +#[derive(Copy, Default)] +pub struct IdentityValueNode {} + +impl From for crate::Node { + fn from(val: IdentityValueNode) -> Self { + crate::Node::ContextualValue(val.into()) + } +} diff --git a/codama-nodes/src/generated/contextual_value_nodes/instruction_input_value_node.rs b/codama-nodes/src/generated/contextual_value_nodes/instruction_input_value_node.rs new file mode 100644 index 00000000..b1438d4b --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/instruction_input_value_node.rs @@ -0,0 +1,36 @@ +use crate::{ + AccountBumpValueNode, AccountValueNode, ArgumentValueNode, ArrayValueNode, BooleanValueNode, + BytesValueNode, ConditionalValueNode, ConstantValueNode, EnumValueNode, IdentityValueNode, + MapValueNode, NoneValueNode, NumberValueNode, PayerValueNode, PdaValueNode, ProgramIdValueNode, + ProgramLinkNode, PublicKeyValueNode, ResolverValueNode, SetValueNode, SomeValueNode, + StringValueNode, StructValueNode, TupleValueNode, +}; +use codama_nodes_derive::node_union; + +#[node_union] +pub enum InstructionInputValueNode { + AccountBumpValue(AccountBumpValueNode), + AccountValue(AccountValueNode), + ArgumentValue(ArgumentValueNode), + ArrayValue(ArrayValueNode), + BooleanValue(BooleanValueNode), + BytesValue(BytesValueNode), + ConditionalValue(ConditionalValueNode), + ConstantValue(ConstantValueNode), + EnumValue(EnumValueNode), + IdentityValue(IdentityValueNode), + MapValue(MapValueNode), + NoneValue(NoneValueNode), + NumberValue(NumberValueNode), + PayerValue(PayerValueNode), + PdaValue(PdaValueNode), + ProgramIdValue(ProgramIdValueNode), + ProgramLink(ProgramLinkNode), + PublicKeyValue(PublicKeyValueNode), + ResolverValue(ResolverValueNode), + SetValue(SetValueNode), + SomeValue(SomeValueNode), + StringValue(StringValueNode), + StructValue(StructValueNode), + TupleValue(TupleValueNode), +} diff --git a/codama-nodes/src/generated/contextual_value_nodes/mod.rs b/codama-nodes/src/generated/contextual_value_nodes/mod.rs new file mode 100644 index 00000000..7d5a42ee --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/mod.rs @@ -0,0 +1,35 @@ +mod account_bump_value_node; +mod account_value_node; +mod argument_value_node; +mod conditional_value_condition; +mod conditional_value_node; +mod contextual_value_node; +mod identity_value_node; +mod instruction_input_value_node; +mod payer_value_node; +mod pda_seed_value_node; +mod pda_seed_value_value; +mod pda_value_node; +mod pda_value_pda; +mod pda_value_program_id; +mod program_id_value_node; +mod resolver_dependency; +mod resolver_value_node; + +pub use account_bump_value_node::*; +pub use account_value_node::*; +pub use argument_value_node::*; +pub use conditional_value_condition::*; +pub use conditional_value_node::*; +pub use contextual_value_node::*; +pub use identity_value_node::*; +pub use instruction_input_value_node::*; +pub use payer_value_node::*; +pub use pda_seed_value_node::*; +pub use pda_seed_value_value::*; +pub use pda_value_node::*; +pub use pda_value_pda::*; +pub use pda_value_program_id::*; +pub use program_id_value_node::*; +pub use resolver_dependency::*; +pub use resolver_value_node::*; diff --git a/codama-nodes/src/generated/contextual_value_nodes/payer_value_node.rs b/codama-nodes/src/generated/contextual_value_nodes/payer_value_node.rs new file mode 100644 index 00000000..89e2b1d5 --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/payer_value_node.rs @@ -0,0 +1,11 @@ +use codama_nodes_derive::node; + +#[node] +#[derive(Copy, Default)] +pub struct PayerValueNode {} + +impl From for crate::Node { + fn from(val: PayerValueNode) -> Self { + crate::Node::ContextualValue(val.into()) + } +} diff --git a/codama-nodes/src/generated/contextual_value_nodes/pda_seed_value_node.rs b/codama-nodes/src/generated/contextual_value_nodes/pda_seed_value_node.rs new file mode 100644 index 00000000..2a67a887 --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/pda_seed_value_node.rs @@ -0,0 +1,23 @@ +use crate::{CamelCaseString, HasName, PdaSeedValueValue}; +use codama_nodes_derive::node; + +#[node] +pub struct PdaSeedValueNode { + // Data. + pub name: CamelCaseString, + + // Children. + pub value: Box, +} + +impl From for crate::Node { + fn from(val: PdaSeedValueNode) -> Self { + crate::Node::ContextualValue(val.into()) + } +} + +impl HasName for PdaSeedValueNode { + fn name(&self) -> &CamelCaseString { + &self.name + } +} diff --git a/codama-nodes/src/generated/contextual_value_nodes/pda_seed_value_value.rs b/codama-nodes/src/generated/contextual_value_nodes/pda_seed_value_value.rs new file mode 100644 index 00000000..a83942db --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/pda_seed_value_value.rs @@ -0,0 +1,27 @@ +use crate::{ + AccountValueNode, ArgumentValueNode, ArrayValueNode, BooleanValueNode, BytesValueNode, + ConstantValueNode, EnumValueNode, MapValueNode, NoneValueNode, NumberValueNode, + PublicKeyValueNode, SetValueNode, SomeValueNode, StringValueNode, StructValueNode, + TupleValueNode, +}; +use codama_nodes_derive::node_union; + +#[node_union] +pub enum PdaSeedValueValue { + Account(AccountValueNode), + Argument(ArgumentValueNode), + Array(ArrayValueNode), + Boolean(BooleanValueNode), + Bytes(BytesValueNode), + Constant(ConstantValueNode), + Enum(EnumValueNode), + Map(MapValueNode), + None(NoneValueNode), + Number(NumberValueNode), + PublicKey(PublicKeyValueNode), + Set(SetValueNode), + Some(SomeValueNode), + String(StringValueNode), + Struct(StructValueNode), + Tuple(TupleValueNode), +} diff --git a/codama-nodes/src/generated/contextual_value_nodes/pda_value_node.rs b/codama-nodes/src/generated/contextual_value_nodes/pda_value_node.rs new file mode 100644 index 00000000..bf39b267 --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/pda_value_node.rs @@ -0,0 +1,17 @@ +use crate::{PdaSeedValueNode, PdaValuePda, PdaValueProgramId}; +use codama_nodes_derive::node; + +#[node] +pub struct PdaValueNode { + // Children. + pub pda: Box, + pub seeds: Vec, + #[serde(skip_serializing_if = "crate::is_default")] + pub program_id: Box>, +} + +impl From for crate::Node { + fn from(val: PdaValueNode) -> Self { + crate::Node::ContextualValue(val.into()) + } +} diff --git a/codama-nodes/src/generated/contextual_value_nodes/pda_value_pda.rs b/codama-nodes/src/generated/contextual_value_nodes/pda_value_pda.rs new file mode 100644 index 00000000..de3ea4e7 --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/pda_value_pda.rs @@ -0,0 +1,17 @@ +use crate::{CamelCaseString, HasName, PdaLinkNode, PdaNode}; +use codama_nodes_derive::node_union; + +#[node_union] +pub enum PdaValuePda { + Pda(PdaNode), + PdaLink(PdaLinkNode), +} + +impl HasName for PdaValuePda { + fn name(&self) -> &CamelCaseString { + match self { + PdaValuePda::Pda(node) => node.name(), + PdaValuePda::PdaLink(node) => node.name(), + } + } +} diff --git a/codama-nodes/src/generated/contextual_value_nodes/pda_value_program_id.rs b/codama-nodes/src/generated/contextual_value_nodes/pda_value_program_id.rs new file mode 100644 index 00000000..625d0e05 --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/pda_value_program_id.rs @@ -0,0 +1,17 @@ +use crate::{AccountValueNode, ArgumentValueNode, CamelCaseString, HasName}; +use codama_nodes_derive::node_union; + +#[node_union] +pub enum PdaValueProgramId { + Account(AccountValueNode), + Argument(ArgumentValueNode), +} + +impl HasName for PdaValueProgramId { + fn name(&self) -> &CamelCaseString { + match self { + PdaValueProgramId::Account(node) => node.name(), + PdaValueProgramId::Argument(node) => node.name(), + } + } +} diff --git a/codama-nodes/src/generated/contextual_value_nodes/program_id_value_node.rs b/codama-nodes/src/generated/contextual_value_nodes/program_id_value_node.rs new file mode 100644 index 00000000..fd6fded2 --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/program_id_value_node.rs @@ -0,0 +1,11 @@ +use codama_nodes_derive::node; + +#[node] +#[derive(Copy, Default)] +pub struct ProgramIdValueNode {} + +impl From for crate::Node { + fn from(val: ProgramIdValueNode) -> Self { + crate::Node::ContextualValue(val.into()) + } +} diff --git a/codama-nodes/src/generated/contextual_value_nodes/resolver_dependency.rs b/codama-nodes/src/generated/contextual_value_nodes/resolver_dependency.rs new file mode 100644 index 00000000..3d816e90 --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/resolver_dependency.rs @@ -0,0 +1,17 @@ +use crate::{AccountValueNode, ArgumentValueNode, CamelCaseString, HasName}; +use codama_nodes_derive::node_union; + +#[node_union] +pub enum ResolverDependency { + Account(AccountValueNode), + Argument(ArgumentValueNode), +} + +impl HasName for ResolverDependency { + fn name(&self) -> &CamelCaseString { + match self { + ResolverDependency::Account(node) => node.name(), + ResolverDependency::Argument(node) => node.name(), + } + } +} diff --git a/codama-nodes/src/generated/contextual_value_nodes/resolver_value_node.rs b/codama-nodes/src/generated/contextual_value_nodes/resolver_value_node.rs new file mode 100644 index 00000000..c8f1f2b0 --- /dev/null +++ b/codama-nodes/src/generated/contextual_value_nodes/resolver_value_node.rs @@ -0,0 +1,26 @@ +use crate::{CamelCaseString, Docs, HasName, ResolverDependency}; +use codama_nodes_derive::node; + +#[node] +pub struct ResolverValueNode { + // Data. + pub name: CamelCaseString, + #[serde(default, skip_serializing_if = "crate::is_default")] + pub docs: Docs, + + // Children. + #[serde(default, skip_serializing_if = "crate::is_default")] + pub depends_on: Vec, +} + +impl From for crate::Node { + fn from(val: ResolverValueNode) -> Self { + crate::Node::ContextualValue(val.into()) + } +} + +impl HasName for ResolverValueNode { + fn name(&self) -> &CamelCaseString { + &self.name + } +} diff --git a/codama-nodes/src/generated/mod.rs b/codama-nodes/src/generated/mod.rs index c60fdb44..78d1f717 100644 --- a/codama-nodes/src/generated/mod.rs +++ b/codama-nodes/src/generated/mod.rs @@ -1,9 +1,11 @@ +mod contextual_value_nodes; mod count_nodes; mod discriminator_nodes; mod link_nodes; mod pda_seed_nodes; mod value_nodes; +pub use contextual_value_nodes::*; pub use count_nodes::*; pub use discriminator_nodes::*; pub use link_nodes::*; diff --git a/codama-nodes/src/instruction_account_node.rs b/codama-nodes/src/instruction_account_node.rs index 9f437cd3..717f1aa6 100644 --- a/codama-nodes/src/instruction_account_node.rs +++ b/codama-nodes/src/instruction_account_node.rs @@ -73,9 +73,9 @@ mod tests { assert_eq!(*node.docs, vec!["Hello".to_string()]); assert_eq!( node.default_value, - Some(InstructionInputValueNode::Account(AccountValueNode::new( - "myOtherAccount" - ))) + Some(InstructionInputValueNode::AccountValue( + AccountValueNode::new("myOtherAccount") + )) ); } diff --git a/codama-nodes/src/instruction_argument_node.rs b/codama-nodes/src/instruction_argument_node.rs index 45d10301..a1ebfd1c 100644 --- a/codama-nodes/src/instruction_argument_node.rs +++ b/codama-nodes/src/instruction_argument_node.rs @@ -93,9 +93,9 @@ mod tests { assert_eq!(node.r#type, TypeNode::Number(NumberTypeNode::le(U32))); assert_eq!( node.default_value, - Some(InstructionInputValueNode::Argument(ArgumentValueNode::new( - "myOtherArgument" - ))) + Some(InstructionInputValueNode::ArgumentValue( + ArgumentValueNode::new("myOtherArgument") + )) ); } diff --git a/codama-nodes/src/lib.rs b/codama-nodes/src/lib.rs index 481eb164..2c44cf61 100644 --- a/codama-nodes/src/lib.rs +++ b/codama-nodes/src/lib.rs @@ -28,7 +28,6 @@ mod value_nodes; pub use account_node::*; pub use constant_node::*; -pub use contextual_value_nodes::*; pub use defined_type_node::*; pub use error_node::*; pub use event_node::*; diff --git a/codama-nodes/src/shared/instruction_input_value_node.rs b/codama-nodes/src/shared/instruction_input_value_node.rs index 94e80ca2..203c9cd5 100644 --- a/codama-nodes/src/shared/instruction_input_value_node.rs +++ b/codama-nodes/src/shared/instruction_input_value_node.rs @@ -1,86 +1,54 @@ -use crate::{ - AccountBumpValueNode, AccountValueNode, ArgumentValueNode, ArrayValueNode, BooleanValueNode, - BytesValueNode, ConditionalValueNode, ConstantValueNode, EnumValueNode, HasKind, - IdentityValueNode, MapValueNode, NoneValueNode, NumberValueNode, PayerValueNode, PdaValueNode, - ProgramIdValueNode, ProgramLinkNode, PublicKeyValueNode, ResolverValueNode, SetValueNode, - SomeValueNode, StringValueNode, StructValueNode, TupleValueNode, ValueNode, -}; +use crate::{HasKind, InstructionInputValueNode, ValueNode}; use codama_errors::CodamaError; -use codama_nodes_derive::node_union; - -#[node_union] -pub enum InstructionInputValueNode { - // ContextualValueNodes. - Account(AccountValueNode), - AccountBump(AccountBumpValueNode), - Argument(ArgumentValueNode), - Conditional(ConditionalValueNode), - Identity(IdentityValueNode), - Payer(PayerValueNode), - Pda(PdaValueNode), - ProgramId(ProgramIdValueNode), - Resolver(ResolverValueNode), - - // ValueNodes. - Array(ArrayValueNode), - Boolean(BooleanValueNode), - Bytes(BytesValueNode), - Constant(ConstantValueNode), - Enum(EnumValueNode), - Map(MapValueNode), - None(NoneValueNode), - Number(NumberValueNode), - PublicKey(PublicKeyValueNode), - Set(SetValueNode), - Some(SomeValueNode), - String(StringValueNode), - Struct(StructValueNode), - Tuple(TupleValueNode), - - // LinkNodes. - ProgramLink(ProgramLinkNode), -} +/// Bridge from the value-node union into the broader +/// `InstructionInputValueNode`. The generated `InstructionInputValueNode` +/// enum (in `generated/contextual_value_nodes/`) auto-derives +/// `From` for each leaf, but `ValueNode` itself (a union of +/// 14 value leaves) needs this explicit bridge. impl From for InstructionInputValueNode { fn from(value: ValueNode) -> Self { match value { - ValueNode::Array(value) => Self::Array(value), - ValueNode::Boolean(value) => Self::Boolean(value), - ValueNode::Bytes(value) => Self::Bytes(value), - ValueNode::Constant(value) => Self::Constant(value), - ValueNode::Enum(value) => Self::Enum(value), - ValueNode::Map(value) => Self::Map(value), - ValueNode::None(value) => Self::None(value), - ValueNode::Number(value) => Self::Number(value), - ValueNode::PublicKey(value) => Self::PublicKey(value), - ValueNode::Set(value) => Self::Set(value), - ValueNode::Some(value) => Self::Some(value), - ValueNode::String(value) => Self::String(value), - ValueNode::Struct(value) => Self::Struct(value), - ValueNode::Tuple(value) => Self::Tuple(value), + ValueNode::Array(value) => Self::ArrayValue(value), + ValueNode::Boolean(value) => Self::BooleanValue(value), + ValueNode::Bytes(value) => Self::BytesValue(value), + ValueNode::Constant(value) => Self::ConstantValue(value), + ValueNode::Enum(value) => Self::EnumValue(value), + ValueNode::Map(value) => Self::MapValue(value), + ValueNode::None(value) => Self::NoneValue(value), + ValueNode::Number(value) => Self::NumberValue(value), + ValueNode::PublicKey(value) => Self::PublicKeyValue(value), + ValueNode::Set(value) => Self::SetValue(value), + ValueNode::Some(value) => Self::SomeValue(value), + ValueNode::String(value) => Self::StringValue(value), + ValueNode::Struct(value) => Self::StructValue(value), + ValueNode::Tuple(value) => Self::TupleValue(value), } } } +/// Inverse bridge: extract the `ValueNode` subset out of +/// `InstructionInputValueNode`, returning `CodamaError::InvalidNodeConversion` +/// for the non-value variants (contextual / link members). impl TryFrom for ValueNode { type Error = CodamaError; fn try_from(value: InstructionInputValueNode) -> Result { match value { - InstructionInputValueNode::Array(value) => Ok(Self::Array(value)), - InstructionInputValueNode::Boolean(value) => Ok(Self::Boolean(value)), - InstructionInputValueNode::Bytes(value) => Ok(Self::Bytes(value)), - InstructionInputValueNode::Constant(value) => Ok(Self::Constant(value)), - InstructionInputValueNode::Enum(value) => Ok(Self::Enum(value)), - InstructionInputValueNode::Map(value) => Ok(Self::Map(value)), - InstructionInputValueNode::None(value) => Ok(Self::None(value)), - InstructionInputValueNode::Number(value) => Ok(Self::Number(value)), - InstructionInputValueNode::PublicKey(value) => Ok(Self::PublicKey(value)), - InstructionInputValueNode::Set(value) => Ok(Self::Set(value)), - InstructionInputValueNode::Some(value) => Ok(Self::Some(value)), - InstructionInputValueNode::String(value) => Ok(Self::String(value)), - InstructionInputValueNode::Struct(value) => Ok(Self::Struct(value)), - InstructionInputValueNode::Tuple(value) => Ok(Self::Tuple(value)), + InstructionInputValueNode::ArrayValue(value) => Ok(Self::Array(value)), + InstructionInputValueNode::BooleanValue(value) => Ok(Self::Boolean(value)), + InstructionInputValueNode::BytesValue(value) => Ok(Self::Bytes(value)), + InstructionInputValueNode::ConstantValue(value) => Ok(Self::Constant(value)), + InstructionInputValueNode::EnumValue(value) => Ok(Self::Enum(value)), + InstructionInputValueNode::MapValue(value) => Ok(Self::Map(value)), + InstructionInputValueNode::NoneValue(value) => Ok(Self::None(value)), + InstructionInputValueNode::NumberValue(value) => Ok(Self::Number(value)), + InstructionInputValueNode::PublicKeyValue(value) => Ok(Self::PublicKey(value)), + InstructionInputValueNode::SetValue(value) => Ok(Self::Set(value)), + InstructionInputValueNode::SomeValue(value) => Ok(Self::Some(value)), + InstructionInputValueNode::StringValue(value) => Ok(Self::String(value)), + InstructionInputValueNode::StructValue(value) => Ok(Self::Struct(value)), + InstructionInputValueNode::TupleValue(value) => Ok(Self::Tuple(value)), _ => Err(CodamaError::InvalidNodeConversion { from: value.kind().to_string(), into: "ValueNode".to_string(), diff --git a/codama-nodes/src/shared/mod.rs b/codama-nodes/src/shared/mod.rs index 7c955894..afdf1177 100644 --- a/codama-nodes/src/shared/mod.rs +++ b/codama-nodes/src/shared/mod.rs @@ -9,5 +9,4 @@ pub use bytes_encoding::*; pub use camel_case_string::*; pub use default_value_strategy::*; pub use docs::*; -pub use instruction_input_value_node::*; pub use is_account_signer::*; diff --git a/spec-generators/src/defaults.ts b/spec-generators/src/defaults.ts index e0be7b10..563c9a0b 100644 --- a/spec-generators/src/defaults.ts +++ b/spec-generators/src/defaults.ts @@ -23,6 +23,7 @@ export interface CategoryRouting { * that's every category except `link`). */ export const CATEGORY_ROUTING: ReadonlyMap = new Map([ + ['contextualValue', { nodeVariant: 'ContextualValue' }], ['count', { nodeVariant: 'Count' }], ['discriminator', { nodeVariant: 'Discriminator' }], ['link', { nodeVariant: 'Link' }], diff --git a/spec-generators/src/fragments/registeredUnionPage.ts b/spec-generators/src/fragments/registeredUnionPage.ts index bd10e310..0bafd678 100644 --- a/spec-generators/src/fragments/registeredUnionPage.ts +++ b/spec-generators/src/fragments/registeredUnionPage.ts @@ -2,7 +2,7 @@ import { pascalCase } from '@codama/fragments'; import { addFragmentImports, type Fragment, fragment, mergeFragments } from '@codama/fragments/rust'; import type { Spec, UnionSpec } from '@codama/spec'; -import { flattenNodeUnion, getRegisteredOnlyLeafKinds } from '../unions'; +import { flattenNodeUnion, getRegisteredOnlyLeafKinds, getRegisteredUnionStripSuffix } from '../unions'; import { use } from './helpers'; /** @@ -30,7 +30,12 @@ import { use } from './helpers'; */ export function getRegisteredUnionPageFragment(union: UnionSpec, spec: Spec): Fragment { const enumName = `Registered${pascalCase(union.name)}`; - const suffix = pascalCase(union.name); + // Strip the longest common PascalCase suffix across BOTH standalone + // and registered-only leaves. `valueNode` → `'ValueNode'` (leaves + // end in `ValueNode`); `contextualValueNode` → also `'ValueNode'` + // (leaves like `accountValueNode` end in `ValueNode`, NOT + // `ContextualValueNode`). + const suffix = getRegisteredUnionStripSuffix(union, spec); const registeredOnlyKinds = new Set(getRegisteredOnlyLeafKinds(union, spec)); const standaloneLeaves = [...flattenNodeUnion(union, spec)]; diff --git a/spec-generators/src/unions.ts b/spec-generators/src/unions.ts index 8537ee9d..8aef0eea 100644 --- a/spec-generators/src/unions.ts +++ b/spec-generators/src/unions.ts @@ -110,21 +110,40 @@ export function getReferencedUnionNames(spec: Spec): ReadonlySet { /** * The PascalCase suffix to strip from each leaf node's kind when - * deriving variant names for an inline union. Computed as the - * longest common PascalCase suffix shared by every leaf's - * `pascalCase(kind)`, trimmed back to start at an uppercase letter - * so we never strip mid-word. + * deriving variant names for an inline union. See + * {@link longestCommonPascalCaseSuffix}. * * - `constantPdaSeedValue` (15 leaves) → `'ValueNode'` * - `enumValuePayload` (2 leaves) → `'ValueNode'` * - `pdaValuePda` (2 leaves) → `'Node'` - * - * For category-main unions (those with a `registered` twin) the - * stripped suffix is the union's own pascalCase name — handled in - * {@link variantStripSuffix} of `unionPage.ts`. */ export function getInlineUnionStripSuffix(union: UnionSpec, spec: Spec): string { - const leaves = [...flattenNodeUnion(union, spec)].map(n => pascalCase(n.kind)); + return longestCommonPascalCaseSuffix([...flattenNodeUnion(union, spec)].map(n => pascalCase(n.kind))); +} + +/** + * The PascalCase suffix to strip from each variant of a + * `RegisteredNodes`-mode category union (e.g. `RegisteredValueNode`). + * Computed across BOTH the standalone leaves AND the registered-only + * leaves so the strip stays uniform across the whole enum. + * + * - `valueNode` (standalone + registered) → `'ValueNode'` + * - `contextualValueNode` (the leaves end in `ValueNode`, not + * `ContextualValueNode`) → `'ValueNode'` + */ +export function getRegisteredUnionStripSuffix(union: UnionSpec, spec: Spec): string { + const standalone = [...flattenNodeUnion(union, spec)].map(n => pascalCase(n.kind)); + const registeredOnly = getRegisteredOnlyLeafKinds(union, spec).map(k => pascalCase(k)); + return longestCommonPascalCaseSuffix([...standalone, ...registeredOnly]); +} + +/** + * Longest common PascalCase suffix shared by every input string, + * trimmed back to start at an uppercase letter so we never strip + * mid-word. Returns `''` when the inputs share no `Word`-aligned + * suffix or the list is empty. + */ +export function longestCommonPascalCaseSuffix(leaves: readonly string[]): string { if (leaves.length === 0) return ''; let suffix = ''; const minLen = Math.min(...leaves.map(s => s.length)); @@ -133,7 +152,6 @@ export function getInlineUnionStripSuffix(union: UnionSpec, spec: Spec): string if (!leaves.every(s => s[s.length - i] === ch)) break; suffix = ch + suffix; } - // Trim back so we always start at an uppercase letter (word boundary). for (let i = 0; i < suffix.length; i++) { const ch = suffix[i]; if (ch >= 'A' && ch <= 'Z') return suffix.slice(i); diff --git a/spec-generators/test/generate.test.ts b/spec-generators/test/generate.test.ts index b55606e8..df7b0ddb 100644 --- a/spec-generators/test/generate.test.ts +++ b/spec-generators/test/generate.test.ts @@ -37,6 +37,24 @@ describe('getRenderMap', () => { it('emits one .rs file per node, one per emittable union, a per-category mod.rs, and a root mod.rs', () => { const keys = [...map.keys()].toSorted(); expect(keys).toEqual([ + 'contextual_value_nodes/account_bump_value_node.rs', + 'contextual_value_nodes/account_value_node.rs', + 'contextual_value_nodes/argument_value_node.rs', + 'contextual_value_nodes/conditional_value_condition.rs', + 'contextual_value_nodes/conditional_value_node.rs', + 'contextual_value_nodes/contextual_value_node.rs', + 'contextual_value_nodes/identity_value_node.rs', + 'contextual_value_nodes/instruction_input_value_node.rs', + 'contextual_value_nodes/mod.rs', + 'contextual_value_nodes/payer_value_node.rs', + 'contextual_value_nodes/pda_seed_value_node.rs', + 'contextual_value_nodes/pda_seed_value_value.rs', + 'contextual_value_nodes/pda_value_node.rs', + 'contextual_value_nodes/pda_value_pda.rs', + 'contextual_value_nodes/pda_value_program_id.rs', + 'contextual_value_nodes/program_id_value_node.rs', + 'contextual_value_nodes/resolver_dependency.rs', + 'contextual_value_nodes/resolver_value_node.rs', 'count_nodes/count_node.rs', 'count_nodes/fixed_count_node.rs', 'count_nodes/mod.rs', @@ -136,7 +154,14 @@ describe('getRenderMap', () => { it('emits a root mod.rs that re-exports every per-category subdirectory', () => { const entry = getFromRenderMap(map, 'mod.rs'); - for (const dir of ['count_nodes', 'discriminator_nodes', 'link_nodes', 'pda_seed_nodes', 'value_nodes']) { + for (const dir of [ + 'contextual_value_nodes', + 'count_nodes', + 'discriminator_nodes', + 'link_nodes', + 'pda_seed_nodes', + 'value_nodes', + ]) { expect(entry.content).toContain(`mod ${dir};`); expect(entry.content).toContain(`pub use ${dir}::*;`); } @@ -285,4 +310,59 @@ describe('getRenderMap', () => { expect(entry.content).toContain('Tuple(TupleValueNode),'); }); }); + + describe('contextualValue category', () => { + it('routes every contextualValue node through Node::ContextualValue', () => { + const entry = getFromRenderMap(map, 'contextual_value_nodes/account_value_node.rs'); + expect(entry.content).toContain('crate::Node::ContextualValue(val.into())'); + }); + + it('emits the RegisteredContextualValueNode union via the RegisteredNodes derive', () => { + const entry = getFromRenderMap(map, 'contextual_value_nodes/contextual_value_node.rs'); + expect(entry.content).toContain('#[derive(RegisteredNodes)]'); + expect(entry.content).toContain('pub enum RegisteredContextualValueNode {'); + // Standalone variants (alphabetical, stripped ValueNode). + expect(entry.content).toContain('Account(AccountValueNode),'); + expect(entry.content).toContain('AccountBump(AccountBumpValueNode),'); + expect(entry.content).toContain('Resolver(ResolverValueNode),'); + // `#[registered]`-only: `pdaSeedValueNode`. + expect(entry.content).toContain('#[registered]\nPdaSeed(PdaSeedValueNode),'); + }); + + it('emits the PdaValuePda inline-union with derived variant names (PdaLink, Pda)', () => { + // Common suffix across [PdaLinkNode, PdaNode] is just `Node`, + // so variants are `PdaLink` and `Pda` — not the old hand-written + // `Linked` / `Nested`. + const entry = getFromRenderMap(map, 'contextual_value_nodes/pda_value_pda.rs'); + expect(entry.content).toContain('pub enum PdaValuePda {'); + expect(entry.content).toContain('Pda(PdaNode),'); + expect(entry.content).toContain('PdaLink(PdaLinkNode),'); + }); + + it('emits InstructionInputValueNode variants with the Value suffix (common suffix = Node)', () => { + // 23 ValueNode members keep their `Value` prefix when only + // `Node` is stripped; `ProgramLink` is the lone non-value + // member that drops to just `ProgramLink`. + const entry = getFromRenderMap(map, 'contextual_value_nodes/instruction_input_value_node.rs'); + expect(entry.content).toContain('pub enum InstructionInputValueNode {'); + expect(entry.content).toContain('AccountValue(AccountValueNode),'); + expect(entry.content).toContain('ArgumentValue(ArgumentValueNode),'); + expect(entry.content).toContain('NumberValue(NumberValueNode),'); + expect(entry.content).toContain('ProgramLink(ProgramLinkNode),'); + }); + + it('applies the box-all-union rule + Box> shapes on ConditionalValueNode', () => { + const entry = getFromRenderMap(map, 'contextual_value_nodes/conditional_value_node.rs'); + expect(entry.content).toContain('pub condition: Box,'); + expect(entry.content).toContain('pub value: Box>,'); + expect(entry.content).toContain('pub if_true: Box>,'); + expect(entry.content).toContain('pub if_false: Box>,'); + }); + + it('emits resolverValueNode.dependsOn as a bare `Vec` (matches the optional-array convention)', () => { + const entry = getFromRenderMap(map, 'contextual_value_nodes/resolver_value_node.rs'); + expect(entry.content).toContain('pub depends_on: Vec,'); + expect(entry.content).not.toContain('Option>'); + }); + }); });