1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{DeriveInput, Lit, Meta, MetaNameValue, parse_macro_input};
4
5#[proc_macro_derive(UmiTagged, attributes(umi_tagged))]
6pub fn derive_umi_tagged(input: TokenStream) -> TokenStream {
7 let input = parse_macro_input!(input as DeriveInput);
8
9 let name = &input.ident;
10 let generics = &input.generics;
11 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
12
13 let field_name = get_umi_field_name(&input);
15
16 let field_ident = syn::Ident::new(&field_name, proc_macro2::Span::call_site());
17
18 let expanded = quote! {
19 impl #impl_generics UmiTaggedRecord for #name #ty_generics #where_clause {
20 fn umi(&self) -> u64 {
21 self.#field_ident
22 }
23 }
24 };
25
26 TokenStream::from(expanded)
27}
28
29fn get_umi_field_name(input: &DeriveInput) -> String {
30 for attr in &input.attrs {
31 if attr.path().is_ident("umi_tagged")
32 && let Meta::List(meta_list) = &attr.meta
33 {
34 let tokens = &meta_list.tokens;
35 let parsed: Result<MetaNameValue, _> = syn::parse2(tokens.clone());
36
37 if let Ok(nv) = parsed
38 && nv.path.is_ident("umi")
39 && let syn::Expr::Lit(expr_lit) = &nv.value
40 && let Lit::Str(lit_str) = &expr_lit.lit
41 {
42 return lit_str.value();
43 }
44 }
45 }
46
47 "umi".to_string()
49}