codama_attributes/codama_directives/
argument_directive.rs1use crate::{
2    codama_directives::type_nodes::StructFieldMetaConsumer,
3    utils::{FromMeta, MetaConsumer},
4    Attribute, CodamaAttribute, CodamaDirective,
5};
6use codama_errors::CodamaError;
7use codama_nodes::{Docs, InstructionArgumentNode};
8use codama_syn_helpers::Meta;
9
10#[derive(Debug, PartialEq)]
11pub struct ArgumentDirective {
12    pub after: bool,
13    pub argument: InstructionArgumentNode,
14}
15
16impl ArgumentDirective {
17    pub fn parse(meta: &Meta) -> syn::Result<Self> {
18        meta.assert_directive("argument")?;
19        let consumer = StructFieldMetaConsumer::from_meta(meta)?
20            .consume_field()?
21            .consume_argument_default_value()?
22            .consume_after()?
23            .assert_fully_consumed()?;
24
25        Ok(ArgumentDirective {
26            after: consumer.after.option().unwrap_or(false),
27            argument: InstructionArgumentNode {
28                name: consumer.name.take(meta)?,
29                r#type: consumer.r#type.take(meta)?,
30                docs: Docs::default(),
31                default_value: consumer.argument_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 ArgumentDirective {
39    type Error = CodamaError;
40
41    fn try_from(attribute: &'a CodamaAttribute) -> Result<Self, Self::Error> {
42        match attribute.directive {
43            CodamaDirective::Argument(ref a) => Ok(a),
44            _ => Err(CodamaError::InvalidCodamaDirective {
45                expected: "argument".to_string(),
46                actual: attribute.directive.name().to_string(),
47            }),
48        }
49    }
50}
51
52impl<'a> TryFrom<&'a Attribute<'a>> for &'a ArgumentDirective {
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, PayerValueNode};
64
65    #[test]
66    fn ok() {
67        let meta: Meta = syn::parse_quote! { argument("age", number(u8)) };
68        let directive = ArgumentDirective::parse(&meta).unwrap();
69        assert_eq!(
70            directive,
71            ArgumentDirective {
72                after: false,
73                argument: InstructionArgumentNode::new("age", NumberTypeNode::le(U8)),
74            }
75        );
76    }
77
78    #[test]
79    fn after() {
80        let meta: Meta = syn::parse_quote! { argument(after, "age", number(u8)) };
81        let directive = ArgumentDirective::parse(&meta).unwrap();
82        assert_eq!(
83            directive,
84            ArgumentDirective {
85                after: true,
86                argument: InstructionArgumentNode::new("age", NumberTypeNode::le(U8)),
87            }
88        );
89    }
90
91    #[test]
92    fn with_default_value() {
93        let meta: Meta = syn::parse_quote! { argument("age", number(u8), default_value = payer) };
94        let directive = ArgumentDirective::parse(&meta).unwrap();
95        assert_eq!(
96            directive,
97            ArgumentDirective {
98                after: false,
99                argument: InstructionArgumentNode {
100                    default_value: Some(PayerValueNode::new().into()),
101                    ..InstructionArgumentNode::new("age", NumberTypeNode::le(U8))
102                },
103            }
104        );
105    }
106}