use proc_macro2::{TokenStream, TokenTree};
use quote::{ToTokens, quote};
use syn::ItemImpl;
use crate::ast::{Op, Ty};
use crate::codegen::{Mapping, apply_mapping, sync_trait_application};
use crate::entry::driver::collect_spec_leaves;
use crate::util::{Cursor, is_single_colon};
#[allow(clippy::too_many_arguments)]
pub(crate) fn assemble_impl(
item: &ItemImpl, trait_path: &syn::Path, new_gen: Option<&TokenStream>,
where_preds: &[TokenTree], m: &Mapping, for_ty: TokenStream,
) -> Result<TokenStream, TokenStream> {
let entries = m.entries();
let item_params = item.generics.params.iter().map(|p| p.to_token_stream()).collect::<Vec<_>>();
let gen_tokens = match new_gen {
Some(ng) => {
let ng_empty = ng.clone().into_iter().next().is_none();
match (ng_empty, item_params.is_empty()) {
(true, true) => quote!(),
(true, false) => quote!(<#(#item_params),*>),
(false, true) => quote!(<#ng>),
(false, false) => quote!(<#ng, #(#item_params),*>),
}
}
None => {
if item_params.is_empty() {
quote!()
} else {
quote!(<#(#item_params),*>)
}
}
};
let trait_args = trait_path
.segments
.last()
.map(|seg| match &seg.arguments {
syn::PathArguments::AngleBracketed(ab) => {
ab.args.iter().map(|a| a.to_token_stream()).collect::<Vec<_>>()
}
_ => vec![],
})
.unwrap_or_default();
let mut preds = vec![];
if !where_preds.is_empty() {
let p = sync_trait_application(where_preds.iter().cloned().collect(), &trait_args)?;
preds.push(apply_mapping(p, entries));
}
if let Some(wc) = &item.generics.where_clause {
let p = sync_trait_application(wc.predicates.to_token_stream(), &trait_args)?;
preds.push(apply_mapping(p, entries));
}
let where_clause = if preds.is_empty() { quote!() } else { quote!(where #(#preds),*) };
let items = item
.items
.iter()
.map(|it| apply_mapping(it.to_token_stream(), entries))
.collect::<Vec<_>>();
let unsafe_kw = if item.unsafety.is_some() { quote!(unsafe) } else { quote!() };
Ok(quote! {
#unsafe_kw impl #gen_tokens #trait_path for #for_ty #where_clause {
#(#items)*
}
})
}
pub(crate) fn parse_matrix_leaves(matrix: &[TokenTree]) -> Result<Vec<Ty>, TokenStream> {
let mut cursor = Cursor::new(matrix);
let (leaves, errors) = collect_spec_leaves(&mut cursor, Op::Comma, None);
if !errors.is_empty() {
return Err(errors.into_iter().collect());
}
Ok(leaves)
}
pub(crate) fn peel_where(spec: &[TokenTree]) -> (&[TokenTree], Vec<TokenTree>) {
if spec.len() >= 2
&& let Some(TokenTree::Group(g)) = spec.last()
&& g.delimiter() == proc_macro2::Delimiter::Brace
&& let Some(TokenTree::Ident(w)) = spec.get(spec.len() - 2)
&& *w == "where"
{
(&spec[..spec.len() - 2], g.stream().into_iter().collect())
} else {
(spec, vec![])
}
}
pub(crate) fn find_shape_colon(spec: &[TokenTree]) -> Option<usize> {
spec.iter().enumerate().find_map(|(i, tt)| {
matches!(tt, TokenTree::Punct(_) if is_single_colon(spec, i)).then_some(i)
})
}
pub(crate) fn split_new_gen(tokens: &[TokenTree]) -> (Option<TokenStream>, Vec<TokenTree>) {
match tokens.first() {
Some(TokenTree::Group(g)) if g.delimiter() == delimiter![<>] => {
(Some(g.stream()), tokens[1..].to_vec())
}
_ => (None, tokens.to_vec()),
}
}