use proc_macro2::TokenStream;
use proc_macro_error2::abort;
use quote::quote;
use syn::File;
use syn::visit_mut::VisitMut;
use crate::args::{ArgType, CondParams, TraitGen};
use crate::subst::{to_subst_types, Subst};
use crate::utils::pathname;
pub(crate) const VERBOSE: bool = false;
pub(crate) const VERBOSE_TF: bool = false;
fn substitute(item: TokenStream, mut types: Subst) -> TokenStream {
if VERBOSE || VERBOSE_TF {
println!("{}\ntrait_gen for {} -> {}: {}",
"=".repeat(80),
pathname(&types.generic_arg),
if types.is_path { "PATH" } else { "TYPE" },
&types.types.iter().map(pathname).collect::<Vec<_>>().join(", ")
)
}
if VERBOSE || VERBOSE_TF { println!("\n{}\n{}", item, "-".repeat(80)); }
let mut output = TokenStream::new();
let ast: File = syn::parse2(item).unwrap();
while !types.types.is_empty() {
let mut modified_ast = ast.clone();
types.visit_file_mut(&mut modified_ast);
output.extend(quote!(#modified_ast));
assert!(types.can_subst_path.is_empty(), "self.enabled has {} entries after type {}",
types.can_subst_path.len(), pathname(types.types.first().unwrap()));
types.types.remove(0);
}
if VERBOSE { println!("end trait_gen for {}\n{}", pathname(&types.generic_arg), "-".repeat(80)); }
output
}
pub(crate) fn macro_trait_gen(args: TokenStream, item: TokenStream) -> TokenStream {
let mut attribute = match syn::parse2::<TraitGen>(args) {
Ok(types) => types,
Err(err) => abort!(err.span(), err;
help = "The expected format is: #[trait_gen(T -> Type1, Type2, Type3)]"),
};
let mut output = TokenStream::new();
let args = std::mem::replace(&mut attribute.args, ArgType::None);
match &args {
ArgType::Tuple(paths) => {
let mut subst = Subst::from_trait_gen(attribute.clone(), paths[0].clone());
let types = std::mem::take(&mut subst.types);
subst.type_helper = Some(&types);
let new_iterators = (0..paths.len()).map(|_| types.iter()).collect::<Vec<_>>();
let mut values = vec![];
let mut iterators = vec![];
loop {
for mut new_iter in new_iterators.iter().skip(iterators.len()).cloned() {
values.push(new_iter.next().unwrap());
iterators.push(new_iter);
}
let mut stream = item.clone();
for (arg, &ty) in paths.iter().zip(values.iter()) {
subst.generic_arg = arg.clone();
subst.types = vec![ty.clone()];
stream = substitute(stream, subst.clone());
}
output.extend(stream);
while let Some(mut it) = iterators.pop() {
values.pop();
if let Some(v) = it.next() {
values.push(v);
iterators.push(it);
break;
}
}
if values.is_empty() { break }
}
}
ArgType::Permutation(path1, path2) | ArgType::StrictOrder(path1, path2) | ArgType::NonStrictOrder(path1, path2) => {
let (_, types) = to_subst_types(attribute.types.clone());
let mut subst = Subst::from_trait_gen(attribute.clone(), path1.clone());
for (i1, p1) in types.iter().enumerate() {
for (i2, p2) in types.iter().enumerate() {
let cond = match &args {
ArgType::Permutation(_, _) => i1 != i2,
ArgType::StrictOrder(_, _) => i1 < i2,
ArgType::NonStrictOrder(_, _) => i1 <= i2,
_ => panic!("can't happen")
};
if cond {
subst.types = vec![p1.clone()];
subst.generic_arg = path1.clone();
let stream = substitute(item.clone(), subst.clone());
subst.types = vec![p2.clone()];
subst.generic_arg = path2.clone();
output.extend(substitute(stream, subst.clone()));
}
}
}
}
_ => panic!("can't happen"),
}
if VERBOSE { println!("{}\n{}", output, "=".repeat(80)); }
output
}
pub(crate) fn macro_trait_gen_if(name: &str, args: TokenStream, item: TokenStream) -> TokenStream {
if VERBOSE { println!("process_conditional_attribute({}, {})", args.to_string(), item.to_string()); }
let new_code = match syn::parse2::<CondParams>(args) {
Ok(attr) => {
if attr.types.contains(&attr.generic_arg) ^ attr.is_negated {
item } else {
TokenStream::new() }
}
Err(err) => {
abort!(err.span(), err;
help = "The expected format is: #[{}(T in Type1, Type2, Type3)]", name);
}
};
new_code
}