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            _ => match self.name() {
52                Some(name) => Some(StructFieldTypeNode::new(name, node).into()),
53                None => Some(node.into()),
54            },
55        }
56    }
57
58    pub fn set_type_node(&mut self, node: TypeNode) {
59        self.node = self.get_updated_type_node(node);
60    }
61}
62
63impl KorokTrait for FieldKorok<'_> {
64    fn node(&self) -> &Option<Node> {
65        &self.node
66    }
67
68    fn set_node(&mut self, node: Option<Node>) {
69        self.node = node;
70    }
71
72    fn attributes(&self) -> Option<&Attributes<'_>> {
73        Some(&self.attributes)
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80    use codama_nodes::{
81        NumberFormat::{U32, U64},
82        NumberTypeNode,
83    };
84
85    #[test]
86    fn get_updated_type_node_with_named_none() {
87        let korok = FieldKorok {
88            ast: &syn::parse_quote! { pub my_field: u32 },
89            attributes: Attributes(vec![]),
90            node: None,
91        };
92        assert_eq!(
93            korok.get_updated_type_node(NumberTypeNode::le(U32).into()),
94            Some(StructFieldTypeNode::new("my_field", NumberTypeNode::le(U32)).into())
95        );
96    }
97
98    #[test]
99    fn get_updated_type_node_with_unnamed_none() {
100        let korok = FieldKorok {
101            ast: &syn::parse_quote! { u32 },
102            attributes: Attributes(vec![]),
103            node: None,
104        };
105        assert_eq!(
106            korok.get_updated_type_node(NumberTypeNode::le(U32).into()),
107            Some(NumberTypeNode::le(U32).into())
108        );
109    }
110
111    #[test]
112    fn get_updated_type_node_with_some() {
113        let korok = FieldKorok {
114            ast: &syn::parse_quote! { pub my_field: u32 },
115            attributes: Attributes(vec![]),
116            node: Some(StructFieldTypeNode::new("my_node_name", NumberTypeNode::le(U32)).into()),
117        };
118        assert_eq!(
119            korok.get_updated_type_node(NumberTypeNode::le(U64).into()),
120            Some(StructFieldTypeNode::new("my_node_name", NumberTypeNode::le(U64)).into())
121        );
122    }
123}