ipmb_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use std::env;
4use syn::{parse_macro_input, Data, DeriveInput, Fields, Path};
5
6#[proc_macro_derive(MessageBox)]
7pub fn derive_message_box(input: TokenStream) -> TokenStream {
8    let input = parse_macro_input!(input as DeriveInput);
9
10    let crate_name = env::var("CARGO_CRATE_NAME").unwrap();
11
12    let crate_path: Path = syn::parse_str(if crate_name == "ipmb" {
13        "crate"
14    } else {
15        "::ipmb"
16    })
17    .unwrap();
18
19    let indent = input.ident;
20
21    let data_enum = if let Data::Enum(data_enum) = input.data {
22        data_enum
23    } else {
24        panic!("#[derive(MessageBox)] is only defined for enums.");
25    };
26
27    let variants_ident: Vec<_> = data_enum
28        .variants
29        .iter()
30        .map(|variant| &variant.ident)
31        .collect();
32
33    let variants_ty: Vec<_> = data_enum
34        .variants
35        .iter()
36        .map(|variant| {
37            if let Fields::Unnamed(field) = &variant.fields {
38                if field.unnamed.len() != 1 {
39                    panic!("#[derive(MessageBox)] is only support unnamed single field.");
40                } else {
41                    &field.unnamed[0].ty
42                }
43            } else {
44                panic!("#[derive(MessageBox)] is only support unnamed single field.");
45            }
46        })
47        .collect();
48
49    let expanded = quote! {
50        impl #crate_path::MessageBox for #indent {
51            fn decode(uuid: type_uuid::Bytes, data: &[u8]) -> std::result::Result<Self, #crate_path::Error> {
52                match uuid {
53                    #(<#variants_ty as type_uuid::TypeUuid>::UUID => {
54                        let variant: #variants_ty = #crate_path::decode(data)?;
55                        Ok(Self::#variants_ident(variant))
56                    })*
57                    _ => Err(#crate_path::Error::TypeUuidNotFound),
58                }
59            }
60
61            fn encode(&self) -> std::result::Result<Vec<u8>, #crate_path::Error> {
62                match self {
63                    #(Self::#variants_ident(t) => #crate_path::encode(t),)*
64                }
65            }
66
67            fn uuid(&self) -> type_uuid::Bytes {
68                match self {
69                    #(Self::#variants_ident(_) => <#variants_ty as type_uuid::TypeUuid>::UUID,)*
70                }
71            }
72        }
73    };
74
75    TokenStream::from(expanded)
76}