diff --git a/codama-korok-visitors/src/set_instructions_visitors.rs b/codama-korok-visitors/src/set_instructions_visitors.rs index e7917908..5c89aac7 100644 --- a/codama-korok-visitors/src/set_instructions_visitors.rs +++ b/codama-korok-visitors/src/set_instructions_visitors.rs @@ -292,14 +292,23 @@ fn parse_enum_variant( EnumVariantTypeNode::Empty(node) => { return Ok((node.name, StructTypeNode::new(vec![]))) } - // Or a tuple variant — convert items to struct fields with synthetic names. + // Or a tuple variant — convert items to struct fields. + // Use field.name() which returns #[codama(name = "...")] if provided, + // otherwise fall back to synthetic names like "arg0", "arg1". EnumVariantTypeNode::Tuple(node) => { if let NestedTypeNode::Value(tuple) = node.tuple { let fields = tuple .items .into_iter() .enumerate() - .map(|(i, item)| StructFieldTypeNode::new(format!("arg{}", i), item)) + .map(|(i, item)| { + let name = korok + .fields + .get(i) + .and_then(|f| f.name()) + .unwrap_or_else(|| format!("arg{}", i).into()); + StructFieldTypeNode::new(name, item) + }) .collect(); return Ok((node.name, StructTypeNode::new(fields))); }; diff --git a/codama-korok-visitors/tests/set_instructions_visitor/from_codama_instructions.rs b/codama-korok-visitors/tests/set_instructions_visitor/from_codama_instructions.rs index c23e3454..e2e16f9d 100644 --- a/codama-korok-visitors/tests/set_instructions_visitor/from_codama_instructions.rs +++ b/codama-korok-visitors/tests/set_instructions_visitor/from_codama_instructions.rs @@ -897,3 +897,63 @@ fn from_enum_with_mixed_variants() -> CodamaResult<()> { ); Ok(()) } + +#[test] +fn from_enum_with_tuple_variants_with_custom_names() -> CodamaResult<()> { + // Tuple args can have custom names via #[codama(name = "...")]. + let store = CrateStore::hydrate(quote! { + #[derive(CodamaType)] + struct Percentage(u64); + + #[derive(CodamaInstructions)] + enum FluxCapacitorInstructions { + #[codama(account(name = "clock_sysvar"))] + Charge( + #[codama(name = "percentage")] + Percentage, + #[codama(name = "is_valid")] + bool, + ), + } + }) + .unwrap(); + let mut korok = CrateKorok::parse(&store)?; + + korok.accept(&mut IdentifyFieldTypesVisitor::new())?; + korok.accept(&mut SetInstructionsVisitor::new())?; + + let codama_koroks::ItemKorok::Enum(instructions_korok) = &korok.items[1] else { + panic!("Expected enum korok"); + }; + + assert_eq!( + instructions_korok.node, + Some( + ProgramNode { + instructions: vec![InstructionNode { + name: "charge".into(), + accounts: vec![InstructionAccountNode::new("clock_sysvar", false, false)], + arguments: vec![ + InstructionArgumentNode { + name: "discriminator".into(), + default_value_strategy: Some(DefaultValueStrategy::Omitted), + docs: Docs::default(), + r#type: NumberTypeNode::le(U8).into(), + default_value: Some(NumberValueNode::new(0u8).into()), + }, + InstructionArgumentNode::new( + "percentage", + DefinedTypeLinkNode::new("percentage") + ), + InstructionArgumentNode::new("isValid", BooleanTypeNode::default()), + ], + discriminators: vec![FieldDiscriminatorNode::new("discriminator", 0).into()], + ..InstructionNode::default() + }], + ..ProgramNode::default() + } + .into() + ) + ); + Ok(()) +} diff --git a/codama-koroks/src/field_korok.rs b/codama-koroks/src/field_korok.rs index 16677195..24139e87 100644 --- a/codama-koroks/src/field_korok.rs +++ b/codama-koroks/src/field_korok.rs @@ -48,8 +48,11 @@ impl<'a> FieldKorok<'a> { } .into(), ), - _ => match self.name() { - Some(name) => Some(StructFieldTypeNode::new(name, node).into()), + // Only wrap in StructFieldTypeNode for named fields (those with an AST ident). + // Tuple fields store the raw TypeNode; callers use field.name() to get custom + // names from #[codama(name = "...")] when needed. + _ => match self.ast.ident.as_ref() { + Some(_) => Some(StructFieldTypeNode::new(self.name().unwrap(), node).into()), None => Some(node.into()), }, }