codama_attributes/codama_directives/
error_directive.rs1use crate::{utils::SetOnce, Attribute, CodamaAttribute, CodamaDirective};
2use codama_errors::CodamaError;
3use codama_syn_helpers::{extensions::*, Meta};
4
5#[derive(Debug, PartialEq, Default, Clone)]
6pub struct ErrorDirective {
7 pub code: Option<usize>,
8 pub message: Option<String>,
9}
10
11impl ErrorDirective {
12 pub fn is_empty(&self) -> bool {
13 self.code.is_none() && self.message.is_none()
14 }
15}
16
17impl ErrorDirective {
18 pub fn parse(meta: &Meta) -> syn::Result<Self> {
19 let pl = meta.assert_directive("error")?.as_path_list()?;
20 let mut code = SetOnce::<usize>::new("code");
21 let mut message = SetOnce::<String>::new("message");
22 pl.each(|ref meta| match meta.path_str().as_str() {
23 "code" => code.set(meta.as_value()?.as_expr()?.as_unsigned_integer()?, meta),
24 "message" => message.set(meta.as_value()?.as_expr()?.as_string()?, meta),
25 _ => {
26 if let Ok(expr) = meta.as_expr() {
27 if let Ok(value) = expr.as_unsigned_integer() {
28 return code.set(value, meta);
29 } else if let Ok(value) = expr.as_string() {
30 return message.set(value, meta);
31 }
32 }
33 Err(meta.error("unrecognized attribute"))
34 }
35 })?;
36 let directive = Self {
37 code: code.option(),
38 message: message.option(),
39 };
40 if directive.is_empty() {
41 return Err(pl.error("expected at least one `code` or `message` attribute"));
42 }
43 Ok(directive)
44 }
45}
46
47impl<'a> TryFrom<&'a CodamaAttribute<'a>> for &'a ErrorDirective {
48 type Error = CodamaError;
49
50 fn try_from(attribute: &'a CodamaAttribute) -> Result<Self, Self::Error> {
51 match attribute.directive {
52 CodamaDirective::Error(ref a) => Ok(a),
53 _ => Err(CodamaError::InvalidCodamaDirective {
54 expected: "error".to_string(),
55 actual: attribute.directive.name().to_string(),
56 }),
57 }
58 }
59}
60
61impl<'a> TryFrom<&'a Attribute<'a>> for &'a ErrorDirective {
62 type Error = CodamaError;
63
64 fn try_from(attribute: &'a Attribute) -> Result<Self, Self::Error> {
65 <&CodamaAttribute>::try_from(attribute)?.try_into()
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn ok() {
75 let meta: Meta = syn::parse_quote! { error(42, "my message") };
76 let directive = ErrorDirective::parse(&meta).unwrap();
77 assert_eq!(
78 directive,
79 ErrorDirective {
80 code: Some(42),
81 message: Some("my message".to_string()),
82 }
83 );
84 }
85
86 #[test]
87 fn ok_with_explicit_labels() {
88 let meta: Meta = syn::parse_quote! { error(code = 42, message = "my message") };
89 let directive = ErrorDirective::parse(&meta).unwrap();
90 assert_eq!(
91 directive,
92 ErrorDirective {
93 code: Some(42),
94 message: Some("my message".to_string()),
95 }
96 );
97 }
98
99 #[test]
100 fn fail_if_nothing_is_provided() {
101 let meta: Meta = syn::parse_quote! { error() };
102 let error = ErrorDirective::parse(&meta).unwrap_err();
103 assert_eq!(
104 error.to_string(),
105 "expected at least one `code` or `message` attribute"
106 );
107 }
108}