|
use crate::helper_structs::AttrInnerKeyStringMap; |
|
use crate::helpers::{fold_error_iter, two_segment_path}; |
|
use proc_macro2::{Span, TokenStream as TokenStream2}; |
|
use syn::{Attribute, Data, DeriveInput, LitStr, Variant}; |
|
|
|
fn parse_hint_helper_attrs(attrs: &[Attribute]) -> syn::Result<(Vec<LitStr>, Vec<LitStr>)> { |
|
fold_error_iter( |
|
attrs |
|
.iter() |
|
.filter(|a| a.path().get_ident().is_some_and(|i| i == "hint")) |
|
.map(|attr| attr.parse_args::<AttrInnerKeyStringMap>()), |
|
) |
|
.and_then(|v: Vec<AttrInnerKeyStringMap>| { |
|
fold_error_iter(AttrInnerKeyStringMap::multi_into_iter(v).map(|(k, mut v)| match v.len() { |
|
0 => panic!("internal error: a key without values was somehow inserted into the hashmap"), |
|
1 => { |
|
let single_val = v.pop().unwrap(); |
|
Ok((LitStr::new(&k.to_string(), Span::call_site()), single_val)) |
|
} |
|
_ => { |
|
|
|
let after_first = v.into_iter().skip(1); |
|
|
|
fold_error_iter(after_first.map(|lit| Err(syn::Error::new(lit.span(), format!("value for key {k} was already given"))))).map(|_: Vec<()>| unreachable!()) |
|
} |
|
})) |
|
}) |
|
.map(|v| v.into_iter().unzip()) |
|
} |
|
|
|
pub fn derive_hint_impl(input_item: TokenStream2) -> syn::Result<TokenStream2> { |
|
let input = syn::parse2::<DeriveInput>(input_item)?; |
|
|
|
let ident = input.ident; |
|
|
|
match input.data { |
|
Data::Enum(data) => { |
|
let variants = data.variants.iter().map(|var: &Variant| two_segment_path(ident.clone(), var.ident.clone())).collect::<Vec<_>>(); |
|
|
|
let hint_result = fold_error_iter(data.variants.into_iter().map(|var: Variant| parse_hint_helper_attrs(&var.attrs))); |
|
|
|
hint_result.map(|hints: Vec<(Vec<LitStr>, Vec<LitStr>)>| { |
|
let (keys, values): (Vec<Vec<LitStr>>, Vec<Vec<LitStr>>) = hints.into_iter().unzip(); |
|
let cap: Vec<usize> = keys.iter().map(|v| v.len()).collect(); |
|
|
|
quote::quote! { |
|
impl Hint for #ident { |
|
fn hints(&self) -> ::std::collections::HashMap<String, String> { |
|
match self { |
|
#( |
|
#variants { .. } => { |
|
let mut hm = ::std::collections::HashMap::with_capacity(#cap); |
|
#( |
|
hm.insert(#keys.to_string(), #values.to_string()); |
|
)* |
|
hm |
|
} |
|
)* |
|
} |
|
} |
|
} |
|
} |
|
}) |
|
} |
|
Data::Struct(_) | Data::Union(_) => { |
|
let hint_result = parse_hint_helper_attrs(&input.attrs); |
|
|
|
hint_result.map(|(keys, values)| { |
|
let cap = keys.len(); |
|
|
|
quote::quote! { |
|
impl Hint for #ident { |
|
fn hints(&self) -> ::std::collections::HashMap<String, String> { |
|
let mut hm = ::std::collections::HashMap::with_capacity(#cap); |
|
#( |
|
hm.insert(#keys.to_string(), #values.to_string()); |
|
)* |
|
hm |
|
} |
|
} |
|
} |
|
}) |
|
} |
|
} |
|
} |
|
|