1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{parse_macro_input, Attribute, DeriveInput, Lit, LitStr, Meta};
6
7#[proc_macro_derive(Message, attributes(type_name, type_prefix))]
8pub fn ecal_message_derive(input: TokenStream) -> TokenStream {
9 let input = parse_macro_input!(input as DeriveInput);
10 let implementor = &input.ident;
11
12 let type_name = find_type_name(&input.attrs).unwrap_or_else(|| implementor.to_string());
13 let prefix = find_prefix(&input.attrs).unwrap_or_default();
14
15 let full_type_name = format!("{}{}", prefix, type_name);
16
17 let expanded = quote! {
18 impl ecal::Message for #implementor {
19 fn type_name() -> &'static str {
20 #full_type_name
21 }
22 }
23 };
24
25 proc_macro::TokenStream::from(expanded)
26}
27
28fn find_type_name(attrs: &[Attribute]) -> Option<String> {
29 for attr in attrs.iter().filter(|attr| attr.path.is_ident("type_name")) {
30 if let Some(inner) = extract_str_lit(attr) {
31 return Some(inner.value());
32 } else {
33 panic!("Please use #[type_name = \"...\"] attribute to specify a type name");
34 }
35 }
36
37 None
38}
39
40fn find_prefix(attrs: &[Attribute]) -> Option<String> {
41 for attr in attrs
42 .iter()
43 .filter(|attr| attr.path.is_ident("type_prefix"))
44 {
45 if let Some(inner) = extract_str_lit(attr) {
46 return Some(inner.value());
47 } else {
48 panic!("Please use #[type_prefix = \"...\"] attribute to specify a type prefix");
49 }
50 }
51
52 None
53}
54
55fn extract_str_lit(attr: &Attribute) -> Option<LitStr> {
56 if let Meta::NameValue(meta) = attr.parse_meta().ok()? {
57 if let Lit::Str(inner) = meta.lit {
58 return Some(inner);
59 }
60 }
61
62 None
63}