diplomat_core/ast/
enums.rs

1use serde::Serialize;
2
3use super::docs::Docs;
4use super::{AttrInheritContext, Attrs, Ident, Method};
5use quote::ToTokens;
6
7/// A fieldless enum declaration in an FFI module.
8#[derive(Clone, Serialize, Debug, Hash, PartialEq, Eq)]
9#[non_exhaustive]
10pub struct Enum {
11    pub name: Ident,
12    pub docs: Docs,
13    /// A list of variants of the enum. (name, discriminant, docs, attrs)
14    pub variants: Vec<(Ident, isize, Docs, Attrs)>,
15    pub methods: Vec<Method>,
16    pub attrs: Attrs,
17}
18
19impl Enum {
20    /// Extract an [`Enum`] metadata value from an AST node.
21    pub fn new(enm: &syn::ItemEnum, parent_attrs: &Attrs) -> Enum {
22        let mut last_discriminant = -1;
23        if !enm.generics.params.is_empty() {
24            // Generic types are not allowed.
25            // Assuming all enums cannot have lifetimes? We don't even have a
26            // `lifetimes` field. If we change our minds we can adjust this later
27            // and update the `CustomType::lifetimes` API accordingly.
28            panic!("Enums cannot have generic parameters");
29        }
30
31        let mut attrs = parent_attrs.clone();
32        attrs.add_attrs(&enm.attrs);
33        let variant_parent_attrs = attrs.attrs_for_inheritance(AttrInheritContext::Variant);
34
35        Enum {
36            name: (&enm.ident).into(),
37            docs: Docs::from_attrs(&enm.attrs),
38            variants: enm
39                .variants
40                .iter()
41                .map(|v| {
42                    let new_discriminant = v
43                        .discriminant
44                        .as_ref()
45                        .map(|d| {
46                            // Reparsing, signed literals are represented
47                            // as a negation expression
48                            let lit: Result<syn::Lit, _> = syn::parse2(d.1.to_token_stream());
49                            if let Ok(syn::Lit::Int(ref lit_int)) = lit {
50                                lit_int.base10_parse::<isize>().unwrap()
51                            } else {
52                                panic!("Expected a discriminant to be a constant integer");
53                            }
54                        })
55                        .unwrap_or_else(|| last_discriminant + 1);
56
57                    last_discriminant = new_discriminant;
58                    let mut v_attrs = variant_parent_attrs.clone();
59                    v_attrs.add_attrs(&v.attrs);
60                    (
61                        (&v.ident).into(),
62                        new_discriminant,
63                        Docs::from_attrs(&v.attrs),
64                        v_attrs,
65                    )
66                })
67                .collect(),
68            methods: vec![],
69            attrs,
70        }
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use insta::{self, Settings};
77
78    use syn;
79
80    use super::Enum;
81
82    #[test]
83    fn simple_enum() {
84        let mut settings = Settings::new();
85        settings.set_sort_maps(true);
86
87        settings.bind(|| {
88            insta::assert_yaml_snapshot!(Enum::new(
89                &syn::parse_quote! {
90                    /// Some docs.
91                    #[diplomat::rust_link(foo::Bar, Enum)]
92                    enum MyLocalEnum {
93                        Abc,
94                        /// Some more docs.
95                        Def
96                    }
97                },
98                &Default::default()
99            ));
100        });
101    }
102
103    #[test]
104    fn enum_with_discr() {
105        let mut settings = Settings::new();
106        settings.set_sort_maps(true);
107
108        settings.bind(|| {
109            insta::assert_yaml_snapshot!(Enum::new(
110                &syn::parse_quote! {
111                    /// Some docs.
112                    #[diplomat::rust_link(foo::Bar, Enum)]
113                    enum DiscriminantedEnum {
114                        Abc = -1,
115                        Def = 0,
116                        Ghi = 1,
117                        Jkl = 2,
118                    }
119                },
120                &Default::default()
121            ));
122        });
123    }
124}