codama_attributes/codama_directives/
default_value_directive.rs1use 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}