codama_attributes/codama_directives/
field_directive.rs

1use crate::{
2    codama_directives::type_nodes::StructFieldMetaConsumer,
3    utils::{FromMeta, MetaConsumer},
4    Attribute, CodamaAttribute, CodamaDirective,
5};
6use codama_errors::CodamaError;
7use codama_nodes::{Docs, StructFieldTypeNode};
8use codama_syn_helpers::Meta;
9
10#[derive(Debug, PartialEq)]
11pub struct FieldDirective {
12    pub after: bool,
13    pub field: StructFieldTypeNode,
14}
15
16impl FieldDirective {
17    pub fn parse(meta: &Meta) -> syn::Result<Self> {
18        meta.assert_directive("field")?;
19        let consumer = StructFieldMetaConsumer::from_meta(meta)?
20            .consume_field()?
21            .consume_default_value()?
22            .consume_after()?
23            .assert_fully_consumed()?;
24
25        Ok(FieldDirective {
26            after: consumer.after.option().unwrap_or(false),
27            field: StructFieldTypeNode {
28                name: consumer.name.take(meta)?,
29                r#type: consumer.r#type.take(meta)?,
30                docs: Docs::default(),
31                default_value: consumer.default_value.option(),
32                default_value_strategy: consumer.default_value_strategy.option(),
33            },
34        })
35    }
36}
37
38impl<'a> TryFrom<&'a CodamaAttribute<'a>> for &'a FieldDirective {
39    type Error = CodamaError;
40
41    fn try_from(attribute: &'a CodamaAttribute) -> Result<Self, Self::Error> {
42        match attribute.directive {
43            CodamaDirective::Field(ref a) => Ok(a),
44            _ => Err(CodamaError::InvalidCodamaDirective {
45                expected: "field".to_string(),
46                actual: attribute.directive.name().to_string(),
47            }),
48        }
49    }
50}
51
52impl<'a> TryFrom<&'a Attribute<'a>> for &'a FieldDirective {
53    type Error = CodamaError;
54
55    fn try_from(attribute: &'a Attribute) -> Result<Self, Self::Error> {
56        <&CodamaAttribute>::try_from(attribute)?.try_into()
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63    use codama_nodes::{NumberFormat::U8, NumberTypeNode, NumberValueNode};
64
65    #[test]
66    fn ok() {
67        let meta: Meta = syn::parse_quote! { field("age", number(u8)) };
68        let directive = FieldDirective::parse(&meta).unwrap();
69        assert_eq!(
70            directive,
71            FieldDirective {
72                after: false,
73                field: StructFieldTypeNode::new("age", NumberTypeNode::le(U8)),
74            }
75        );
76    }
77
78    #[test]
79    fn after() {
80        let meta: Meta = syn::parse_quote! { field(after, "age", number(u8)) };
81        let directive = FieldDirective::parse(&meta).unwrap();
82        assert_eq!(
83            directive,
84            FieldDirective {
85                after: true,
86                field: StructFieldTypeNode::new("age", NumberTypeNode::le(U8)),
87            }
88        );
89    }
90
91    #[test]
92    fn with_default_value() {
93        let meta: Meta = syn::parse_quote! { field("age", number(u8), default_value = 42) };
94        let directive = FieldDirective::parse(&meta).unwrap();
95        assert_eq!(
96            directive,
97            FieldDirective {
98                after: false,
99                field: StructFieldTypeNode {
100                    default_value: Some(NumberValueNode::new(42u8).into()),
101                    ..StructFieldTypeNode::new("age", NumberTypeNode::le(U8))
102                },
103            }
104        );
105    }
106}