bmart-derive 0.1.0

Bohemia Automation common tools library (derive macros)
Documentation
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, Lit, MetaNameValue};

macro_rules! litstr {
    ($lit: expr) => {
        if let Lit::Str(s) = $lit {
            s.value()
        } else {
            panic!("invalid string value")
        }
    };
}

#[proc_macro_derive(Sorting, attributes(sorting))]
pub fn sorting_derive(input: TokenStream) -> TokenStream {
    let sitem = parse_macro_input!(input as syn::ItemStruct);
    let sid = &sitem.ident;
    let mut owned = true;
    for param in sitem.generics.params {
        if let syn::GenericParam::Lifetime(_) = param {
            owned = false;
        }
    }
    let mut id = "id".to_owned();
    for a in &sitem.attrs {
        if a.path.is_ident("sorting") {
            if let Ok(nameval) = a.parse_args::<MetaNameValue>() {
                if nameval.path.is_ident("id") {
                    id = litstr!(nameval.lit);
                }
            }
        }
    }
    let i_id = format_ident!("{}", id);
    let tr = if owned {
        quote! {
            impl Eq for #sid {}
            impl Ord for #sid {
                fn cmp(&self, other: &Self) -> std::cmp::Ordering {
                    self.#i_id.cmp(&other.#i_id)
                }
            }
            impl PartialOrd for #sid {
                fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
                    Some(self.cmp(other))
                }
            }
            impl PartialEq for #sid {
                fn eq(&self, other: &Self) -> bool {
                    self.#i_id == other.#i_id
                }
            }
        }
    } else {
        quote! {
            impl<'srt> Eq for #sid<'srt> {}
            impl<'srt> Ord for #sid<'srt> {
                fn cmp(&self, other: &Self) -> std::cmp::Ordering {
                    self.#i_id.cmp(&other.#i_id)
                }
            }
            impl<'srt> PartialOrd for #sid<'srt> {
                fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
                    Some(self.cmp(other))
                }
            }
            impl<'srt> PartialEq for #sid<'srt> {
                fn eq(&self, other: &Self) -> bool {
                    self.#i_id == other.#i_id
                }
            }
        }
    };
    TokenStream::from(tr)
}