1 use super::{atom_token, int_repr_tokens};
2 use crate::names::Names;
3 
4 use proc_macro2::TokenStream;
5 use quote::quote;
6 
define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream7 pub(super) fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream {
8     let ident = names.type_(&name);
9     let rt = names.runtime_mod();
10 
11     let repr = int_repr_tokens(e.repr);
12     let abi_repr = atom_token(match e.repr {
13         witx::IntRepr::U8 | witx::IntRepr::U16 | witx::IntRepr::U32 => witx::AtomType::I32,
14         witx::IntRepr::U64 => witx::AtomType::I64,
15     });
16 
17     let mut variant_names = vec![];
18     let mut tryfrom_repr_cases = vec![];
19     let mut to_repr_cases = vec![];
20     let mut to_display = vec![];
21 
22     for (n, variant) in e.variants.iter().enumerate() {
23         let variant_name = names.enum_variant(&variant.name);
24         let docs = variant.docs.trim();
25         let ident_str = ident.to_string();
26         let variant_str = variant_name.to_string();
27         tryfrom_repr_cases.push(quote!(#n => Ok(#ident::#variant_name)));
28         to_repr_cases.push(quote!(#ident::#variant_name => #n as #repr));
29         to_display.push(quote!(#ident::#variant_name => format!("{} ({}::{}({}))", #docs, #ident_str, #variant_str, #repr::from(*self))));
30         variant_names.push(variant_name);
31     }
32 
33     quote! {
34         #[repr(#repr)]
35         #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
36         pub enum #ident {
37             #(#variant_names),*
38         }
39 
40         impl ::std::fmt::Display for #ident {
41             fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
42                 let to_str = match self {
43                     #(#to_display,)*
44                 };
45                 write!(f, "{}", to_str)
46             }
47         }
48 
49         impl ::std::convert::TryFrom<#repr> for #ident {
50             type Error = #rt::GuestError;
51             fn try_from(value: #repr) -> Result<#ident, #rt::GuestError> {
52                 match value as usize {
53                     #(#tryfrom_repr_cases),*,
54                     _ => Err( #rt::GuestError::InvalidEnumValue(stringify!(#ident))),
55                 }
56             }
57         }
58 
59         impl ::std::convert::TryFrom<#abi_repr> for #ident {
60             type Error = #rt::GuestError;
61             fn try_from(value: #abi_repr) -> Result<#ident, #rt::GuestError> {
62                 #ident::try_from(value as #repr)
63             }
64         }
65 
66         impl From<#ident> for #repr {
67             fn from(e: #ident) -> #repr {
68                 match e {
69                     #(#to_repr_cases),*
70                 }
71             }
72         }
73 
74         impl From<#ident> for #abi_repr {
75             fn from(e: #ident) -> #abi_repr {
76                 #repr::from(e) as #abi_repr
77             }
78         }
79 
80         impl<'a> #rt::GuestType<'a> for #ident {
81             fn guest_size() -> u32 {
82                 #repr::guest_size()
83             }
84 
85             fn guest_align() -> usize {
86                 #repr::guest_align()
87             }
88 
89             fn read(location: & #rt::GuestPtr<#ident>) -> Result<#ident, #rt::GuestError> {
90                 use std::convert::TryFrom;
91                 let reprval = #repr::read(&location.cast())?;
92                 let value = #ident::try_from(reprval)?;
93                 Ok(value)
94             }
95 
96             fn write(location: & #rt::GuestPtr<'_, #ident>, val: Self)
97                 -> Result<(), #rt::GuestError>
98             {
99                 #repr::write(&location.cast(), #repr::from(val))
100             }
101         }
102 
103         unsafe impl <'a> #rt::GuestTypeTransparent<'a> for #ident {
104             #[inline]
105             fn validate(location: *mut #ident) -> Result<(), #rt::GuestError> {
106                 use std::convert::TryFrom;
107                 // Validate value in memory using #ident::try_from(reprval)
108                 let reprval = unsafe { (location as *mut #repr).read() };
109                 let _val = #ident::try_from(reprval)?;
110                 Ok(())
111             }
112         }
113     }
114 }
115 
116 impl super::WiggleType for witx::EnumDatatype {
impls_display(&self) -> bool117     fn impls_display(&self) -> bool {
118         true
119     }
120 }
121