Skip to main content

codama_attributes/codama_directives/
default_value_directive.rs

1use crate::{Attribute, CodamaAttribute, CodamaDirective, Resolvable};
2use codama_errors::CodamaError;
3use codama_nodes::{DefaultValueStrategy, InstructionInputValueNode, ValueNode};
4use codama_syn_helpers::{extensions::*, Meta};
5
6#[derive(Debug, PartialEq)]
7pub struct DefaultValueDirective {
8    pub node: Resolvable<InstructionInputValueNode>,
9    pub default_value_strategy: Option<DefaultValueStrategy>,
10}
11
12impl DefaultValueDirective {
13    pub fn parse(meta: &Meta) -> syn::Result<Self> {
14        Self::parse_logic(meta, false)
15    }
16
17    pub fn parse_value_nodes_only(meta: &Meta) -> syn::Result<Self> {
18        Self::parse_logic(meta, true)
19    }
20
21    fn parse_logic(meta: &Meta, value_nodes_only: bool) -> syn::Result<Self> {
22        let pv = meta.as_path_value()?;
23        let is_default_value = pv.path.is_strict("default_value");
24        let is_value = pv.path.is_strict("value");
25        if !is_default_value && !is_value {
26            return Err(pv
27                .path
28                .error("expected #[codama(default_value)] or #[codama(value)] attributes"));
29        };
30
31        let node = match value_nodes_only {
32            true => {
33                Resolvable::<ValueNode>::from_meta(&pv.value)?.map(InstructionInputValueNode::from)
34            }
35            false => Resolvable::<InstructionInputValueNode>::from_meta(&pv.value)?,
36        };
37
38        let default_value_strategy = match is_value {
39            true => Some(DefaultValueStrategy::Omitted),
40            false => None,
41        };
42
43        Ok(Self {
44            node,
45            default_value_strategy,
46        })
47    }
48}
49
50impl<'a> TryFrom<&'a CodamaAttribute<'a>> for &'a DefaultValueDirective {
51    type Error = CodamaError;
52
53    fn try_from(attribute: &'a CodamaAttribute) -> Result<Self, Self::Error> {
54        match attribute.directive.as_ref() {
55            CodamaDirective::DefaultValue(ref a) => Ok(a),
56            _ => Err(CodamaError::InvalidCodamaDirective {
57                expected: "default_value".to_string(),
58                actual: attribute.directive.name().to_string(),
59            }),
60        }
61    }
62}
63
64impl<'a> TryFrom<&'a Attribute<'a>> for &'a DefaultValueDirective {
65    type Error = CodamaError;
66
67    fn try_from(attribute: &'a Attribute) -> Result<Self, Self::Error> {
68        <&CodamaAttribute>::try_from(attribute)?.try_into()
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use codama_nodes::{BooleanValueNode, PayerValueNode};
76    use syn::parse_quote;
77
78    #[test]
79    fn default_value_ok() {
80        let meta: Meta = parse_quote! { default_value = payer };
81        let directive = DefaultValueDirective::parse(&meta).unwrap();
82
83        assert_eq!(
84            directive,
85            DefaultValueDirective {
86                node: Resolvable::Resolved(PayerValueNode::new().into()),
87                default_value_strategy: None,
88            }
89        );
90    }
91
92    #[test]
93    fn value_ok() {
94        let meta: Meta = parse_quote! { value = payer };
95        let directive = DefaultValueDirective::parse(&meta).unwrap();
96
97        assert_eq!(
98            directive,
99            DefaultValueDirective {
100                node: Resolvable::Resolved(PayerValueNode::new().into()),
101                default_value_strategy: Some(DefaultValueStrategy::Omitted),
102            }
103        );
104    }
105
106    #[test]
107    fn parse_value_nodes_only_ok() {
108        let meta: Meta = parse_quote! { value = true };
109        let directive = DefaultValueDirective::parse_value_nodes_only(&meta).unwrap();
110
111        assert_eq!(
112            directive,
113            DefaultValueDirective {
114                node: Resolvable::Resolved(BooleanValueNode::new(true).into()),
115                default_value_strategy: Some(DefaultValueStrategy::Omitted),
116            }
117        );
118    }
119
120    #[test]
121    fn parse_value_nodes_only_err() {
122        let meta: Meta = parse_quote! { value = payer };
123        let error = DefaultValueDirective::parse_value_nodes_only(&meta).unwrap_err();
124        assert_eq!(error.to_string(), "unrecognized value");
125    }
126
127    #[test]
128    fn no_input() {
129        let meta: Meta = parse_quote! { default_value =  };
130        let error = DefaultValueDirective::parse(&meta).unwrap_err();
131        assert_eq!(error.to_string(), "unrecognized value");
132    }
133
134    #[test]
135    fn multiple_inputs() {
136        let meta: Meta = parse_quote! { default_value = (true, false) };
137        let error = DefaultValueDirective::parse(&meta).unwrap_err();
138        assert_eq!(error.to_string(), "expected a single value, found a list");
139    }
140
141    #[test]
142    fn unrecognized_value() {
143        let meta: Meta = parse_quote! { default_value = banana };
144        let error = DefaultValueDirective::parse(&meta).unwrap_err();
145        assert_eq!(error.to_string(), "unrecognized value");
146    }
147}