codama_attributes/
derive_attribute.rs

1use crate::Attribute;
2use codama_errors::CodamaError;
3use codama_syn_helpers::extensions::*;
4
5#[derive(Debug, PartialEq)]
6pub struct DeriveAttribute<'a> {
7    pub ast: &'a syn::Attribute,
8    pub derives: Vec<syn::Path>,
9}
10
11impl<'a> DeriveAttribute<'a> {
12    pub fn parse(ast: &'a syn::Attribute) -> syn::Result<Self> {
13        let unfeatured = ast.unfeatured();
14        let effective = unfeatured.as_ref().unwrap_or(ast);
15        Self::parse_from(ast, effective)
16    }
17
18    /// Parse a derive attribute using the effective attribute for content extraction.
19    /// `ast` is stored as the original attribute reference (for error spans).
20    /// `effective` is used to parse the actual derive list.
21    pub fn parse_from(ast: &'a syn::Attribute, effective: &syn::Attribute) -> syn::Result<Self> {
22        let list = effective.meta.require_list()?;
23        if !list.path.is_strict("derive") {
24            return Err(list.path.error("expected #[derive(...)]"));
25        };
26
27        let derives = list.parse_comma_args::<syn::Path>()?;
28        Ok(Self { ast, derives })
29    }
30}
31
32impl<'a> TryFrom<&'a Attribute<'a>> for &'a DeriveAttribute<'a> {
33    type Error = CodamaError;
34
35    fn try_from(attribute: &'a Attribute) -> Result<Self, Self::Error> {
36        match attribute {
37            Attribute::Derive(a) => Ok(a),
38            _ => Err(CodamaError::InvalidAttribute {
39                expected: "derive".to_string(),
40                actual: attribute.name(),
41            }),
42        }
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use syn::parse_quote;
50
51    #[test]
52    fn test_derive_attribute() {
53        let ast = parse_quote! { #[derive(Debug, PartialEq)] };
54        let attribute = DeriveAttribute::parse(&ast).unwrap();
55
56        assert_eq!(attribute.ast, &ast);
57        assert_eq!(
58            attribute.derives,
59            [(parse_quote! { Debug }), (parse_quote! { PartialEq }),]
60        );
61    }
62
63    #[test]
64    fn test_feature_gated_derive_attribute() {
65        let ast = parse_quote! { #[cfg_attr(feature = "some_feature", derive(Debug, PartialEq))] };
66        let attribute = DeriveAttribute::parse(&ast).unwrap();
67
68        assert_eq!(attribute.ast, &ast);
69        assert_eq!(
70            attribute.derives,
71            [(parse_quote! { Debug }), (parse_quote! { PartialEq })]
72        );
73    }
74}