codama_attributes/codama_directives/
argument_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, 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        let default_value = consumer.default_instruction_input_value_node();
26        let default_value_strategy = consumer.default_value_strategy();
27
28        Ok(ArgumentDirective {
29            after: consumer.after.option().unwrap_or(false),
30            argument: InstructionArgumentNode {
31                name: consumer.name.take(meta)?,
32                r#type: consumer.r#type.take(meta)?,
33                docs: Docs::default(),
34                default_value,
35                default_value_strategy,
36            },
37        })
38    }
39}
40
41impl<'a> TryFrom<&'a CodamaAttribute<'a>> for &'a ArgumentDirective {
42    type Error = CodamaError;
43
44    fn try_from(attribute: &'a CodamaAttribute) -> Result<Self, Self::Error> {
45        match attribute.directive {
46            CodamaDirective::Argument(ref a) => Ok(a),
47            _ => Err(CodamaError::InvalidCodamaDirective {
48                expected: "argument".to_string(),
49                actual: attribute.directive.name().to_string(),
50            }),
51        }
52    }
53}
54
55impl<'a> TryFrom<&'a Attribute<'a>> for &'a ArgumentDirective {
56    type Error = CodamaError;
57
58    fn try_from(attribute: &'a Attribute) -> Result<Self, Self::Error> {
59        <&CodamaAttribute>::try_from(attribute)?.try_into()
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66    use codama_nodes::{NumberFormat::U8, NumberTypeNode, PayerValueNode};
67
68    #[test]
69    fn ok() {
70        let meta: Meta = syn::parse_quote! { argument("age", number(u8)) };
71        let directive = ArgumentDirective::parse(&meta).unwrap();
72        assert_eq!(
73            directive,
74            ArgumentDirective {
75                after: false,
76                argument: InstructionArgumentNode::new("age", NumberTypeNode::le(U8)),
77            }
78        );
79    }
80
81    #[test]
82    fn after() {
83        let meta: Meta = syn::parse_quote! { argument(after, "age", number(u8)) };
84        let directive = ArgumentDirective::parse(&meta).unwrap();
85        assert_eq!(
86            directive,
87            ArgumentDirective {
88                after: true,
89                argument: InstructionArgumentNode::new("age", NumberTypeNode::le(U8)),
90            }
91        );
92    }
93
94    #[test]
95    fn with_default_value() {
96        let meta: Meta = syn::parse_quote! { argument("age", number(u8), default_value = payer) };
97        let directive = ArgumentDirective::parse(&meta).unwrap();
98        assert_eq!(
99            directive,
100            ArgumentDirective {
101                after: false,
102                argument: InstructionArgumentNode {
103                    default_value: Some(PayerValueNode::new().into()),
104                    ..InstructionArgumentNode::new("age", NumberTypeNode::le(U8))
105                },
106            }
107        );
108    }
109}