use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{ItemImpl, parse_macro_input, spanned::Spanned};
use crate::sassi_path;
pub fn trait_impl(args: TokenStream, input: TokenStream) -> TokenStream {
let args = TokenStream2::from(args);
let item = parse_macro_input!(input as ItemImpl);
if !args.is_empty() {
return syn::Error::new(args.span(), "sassi::trait_impl takes no arguments")
.to_compile_error()
.into();
}
if !item.generics.params.is_empty() || item.generics.where_clause.is_some() {
return syn::Error::new(
item.generics.span(),
"#[sassi::trait_impl] currently supports concrete, non-generic impl blocks",
)
.to_compile_error()
.into();
}
let trait_path = match &item.trait_ {
Some((None, path, _for_token)) => path.clone(),
Some((Some(not_token), _path, _for_token)) => {
return syn::Error::new(
not_token.span(),
"#[sassi::trait_impl] cannot register negative impl blocks",
)
.to_compile_error()
.into();
}
None => {
return syn::Error::new(
item.impl_token.span(),
"sassi::trait_impl must be applied to `impl Trait for Type`",
)
.to_compile_error()
.into();
}
};
let sassi_path = match sassi_path() {
Ok(path) => path,
Err(e) => return e.to_compile_error().into(),
};
let model_ty = &item.self_ty;
let expanded = quote! {
#item
const _: () = {
fn __sassi_collect_trait_impl(
sassi: &#sassi_path::Sassi,
) -> ::std::boxed::Box<dyn ::std::any::Any + ::std::marker::Send + ::std::marker::Sync> {
let values: ::std::vec::Vec<::std::sync::Arc<dyn #trait_path>> =
match sassi.pool::<#model_ty>() {
Some(pool) => pool
.scope(::std::vec::Vec::new())
.collect()
.into_iter()
.map(|value| value as ::std::sync::Arc<dyn #trait_path>)
.collect(),
None => ::std::vec::Vec::new(),
};
::std::boxed::Box::new(values)
}
#sassi_path::__private::inventory::submit! {
#sassi_path::__private::TraitImplEntry {
trait_type_id: ::std::any::TypeId::of::<dyn #trait_path>(),
model_type_id: ::std::any::TypeId::of::<#model_ty>(),
collect_fn: __sassi_collect_trait_impl,
}
}
};
};
expanded.into()
}