Skip to main content

ecal_derive/
lib.rs

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}