codama_attributes/
repr_attribute.rs

1use crate::Attribute;
2use codama_errors::CodamaError;
3use codama_nodes::{NumberFormat, NumberTypeNode};
4use codama_syn_helpers::extensions::*;
5
6#[derive(Debug, PartialEq)]
7pub struct ReprAttribute<'a> {
8    pub ast: &'a syn::Attribute,
9    pub metas: Vec<syn::Meta>,
10}
11
12impl<'a> ReprAttribute<'a> {
13    pub fn parse(ast: &'a syn::Attribute) -> syn::Result<Self> {
14        // Check if the attribute is feature-gated.
15        let unfeatured = ast.unfeatured();
16        let attr = unfeatured.as_ref().unwrap_or(ast);
17
18        // Check if the attribute is a #[repr(...)] attribute.
19        let list = attr.meta.require_list()?;
20        if !list.path.is_strict("repr") {
21            return Err(list.path.error("expected #[repr(...)]"));
22        };
23
24        // Parse the list of metas.
25        let metas = list.parse_comma_args::<syn::Meta>()?;
26        Ok(Self { ast, metas })
27    }
28
29    pub fn get_number_type_node(&self) -> Option<NumberTypeNode> {
30        self.metas.iter().find_map(|meta| match meta {
31            syn::Meta::Path(p) => match NumberFormat::try_from(p.to_string()) {
32                Ok(n) => Some(NumberTypeNode::le(n)),
33                Err(_) => None,
34            },
35            _ => None,
36        })
37    }
38}
39
40impl<'a> TryFrom<&'a Attribute<'a>> for &'a ReprAttribute<'a> {
41    type Error = CodamaError;
42
43    fn try_from(attribute: &'a Attribute) -> Result<Self, Self::Error> {
44        match attribute {
45            Attribute::Repr(a) => Ok(a),
46            _ => Err(CodamaError::InvalidAttribute {
47                expected: "repr".to_string(),
48                actual: attribute.name(),
49            }),
50        }
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57    use codama_nodes::NumberFormat::U32;
58    use syn::parse_quote;
59
60    #[test]
61    fn test_repr_attribute() {
62        let ast = parse_quote! { #[repr(u32, align(4))] };
63        let attribute = ReprAttribute::parse(&ast).unwrap();
64
65        assert_eq!(attribute.ast, &ast);
66        assert_eq!(
67            attribute.metas,
68            [(parse_quote! { u32 }), (parse_quote! { align(4) })]
69        );
70    }
71
72    #[test]
73    fn test_feature_gated_repr_attribute() {
74        let ast = parse_quote! { #[cfg_attr(feature = "some_feature", repr(u32, align(4)))] };
75        let attribute = ReprAttribute::parse(&ast).unwrap();
76
77        assert_eq!(attribute.ast, &ast);
78        assert_eq!(
79            attribute.metas,
80            [(parse_quote! { u32 }), (parse_quote! { align(4) })]
81        );
82    }
83
84    #[test]
85    fn test_get_number_type_node() {
86        let ast = parse_quote! { #[repr(u32, align(4), u64)] };
87        let attribute = ReprAttribute::parse(&ast).unwrap();
88
89        assert_eq!(
90            attribute.get_number_type_node(),
91            Some(NumberTypeNode::le(U32))
92        );
93    }
94}