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