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}