codama_koroks/
field_korok.rs

1use crate::KorokTrait;
2use codama_attributes::{Attributes, NameDirective, TryFromFilter};
3use codama_errors::{CodamaResult, IteratorCombineErrors};
4use codama_nodes::{CamelCaseString, Node, RegisteredTypeNode, StructFieldTypeNode, TypeNode};
5
6#[derive(Debug, PartialEq)]
7pub struct FieldKorok<'a> {
8    pub ast: &'a syn::Field,
9    pub attributes: Attributes<'a>,
10    pub node: Option<Node>,
11}
12
13impl<'a> FieldKorok<'a> {
14    pub fn parse(ast: &'a syn::Field) -> CodamaResult<Self> {
15        let attributes = Attributes::parse(&ast.attrs, ast.into())?;
16        Ok(Self {
17            ast,
18            attributes,
19            node: None,
20        })
21    }
22
23    pub fn parse_all(ast: &'a syn::Fields) -> CodamaResult<Vec<Self>> {
24        match ast {
25            syn::Fields::Named(f) => f.named.iter().map(Self::parse).collect_and_combine_errors(),
26            syn::Fields::Unnamed(f) => f
27                .unnamed
28                .iter()
29                .map(Self::parse)
30                .collect_and_combine_errors(),
31            syn::Fields::Unit => Ok(vec![]),
32        }
33    }
34
35    pub fn name(&self) -> Option<CamelCaseString> {
36        self.attributes
37            .get_last(NameDirective::filter)
38            .map(|n| n.name.clone())
39            .or_else(|| self.ast.ident.as_ref().map(|i| i.to_string().into()))
40    }
41
42    pub fn get_updated_type_node(&self, node: TypeNode) -> Option<Node> {
43        match &self.node {
44            Some(Node::Type(RegisteredTypeNode::StructField(field))) => Some(
45                StructFieldTypeNode {
46                    r#type: node,
47                    ..field.clone()
48                }
49                .into(),
50            ),
51            // Only wrap in StructFieldTypeNode for named fields (those with an AST ident).
52            // Tuple fields store the raw TypeNode; callers use field.name() to get custom
53            // names from #[codama(name = "...")] when needed.
54            _ => match self.ast.ident.as_ref() {
55                Some(_) => Some(StructFieldTypeNode::new(self.name().unwrap(), node).into()),
56                None => Some(node.into()),
57            },
58        }
59    }
60
61    pub fn set_type_node(&mut self, node: TypeNode) {
62        self.node = self.get_updated_type_node(node);
63    }
64}
65
66impl KorokTrait for FieldKorok<'_> {
67    fn node(&self) -> &Option<Node> {
68        &self.node
69    }
70
71    fn set_node(&mut self, node: Option<Node>) {
72        self.node = node;
73    }
74
75    fn attributes(&self) -> Option<&Attributes<'_>> {
76        Some(&self.attributes)
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83    use codama_nodes::{
84        NumberFormat::{U32, U64},
85        NumberTypeNode,
86    };
87
88    #[test]
89    fn get_updated_type_node_with_named_none() {
90        let korok = FieldKorok {
91            ast: &syn::parse_quote! { pub my_field: u32 },
92            attributes: Attributes(vec![]),
93            node: None,
94        };
95        assert_eq!(
96            korok.get_updated_type_node(NumberTypeNode::le(U32).into()),
97            Some(StructFieldTypeNode::new("my_field", NumberTypeNode::le(U32)).into())
98        );
99    }
100
101    #[test]
102    fn get_updated_type_node_with_unnamed_none() {
103        let korok = FieldKorok {
104            ast: &syn::parse_quote! { u32 },
105            attributes: Attributes(vec![]),
106            node: None,
107        };
108        assert_eq!(
109            korok.get_updated_type_node(NumberTypeNode::le(U32).into()),
110            Some(NumberTypeNode::le(U32).into())
111        );
112    }
113
114    #[test]
115    fn get_updated_type_node_with_some() {
116        let korok = FieldKorok {
117            ast: &syn::parse_quote! { pub my_field: u32 },
118            attributes: Attributes(vec![]),
119            node: Some(StructFieldTypeNode::new("my_node_name", NumberTypeNode::le(U32)).into()),
120        };
121        assert_eq!(
122            korok.get_updated_type_node(NumberTypeNode::le(U64).into()),
123            Some(StructFieldTypeNode::new("my_node_name", NumberTypeNode::le(U64)).into())
124        );
125    }
126}