use super::*;
use quote::*;
use syn::*;
pub fn manifestable(_attr: TokenStream, input: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(input as Item);
let trait_item = match &mut input {
Item::Trait(trait_item) => trait_item,
_ => return TokenStream::from(quote! {
::core::compile_error!("The #[manifestable] attribute can only be used on trait declarations");
}),
};
let ident = trait_item.ident.clone();
insert_any_supertrait(trait_item);
let core_path = utils::core_path_attr(&trait_item.attrs);
let default_type_path = match default_attr(&mut trait_item.attrs) {
Some(path) => path,
None => return TokenStream::from(quote! {
::core::compile_error!("The #[manifestable] attribute must be followed by #[default(<TypePath>)]");
}),
};
let custom_traits = custom_attr(&mut trait_item.attrs);
let mut custom_qualify = false;
let mut custom_default = false;
let mut custom_encode = false;
let mut custom_encompass = false;
let mut custom_inspect = false;
let mut custom_debug = false;
for path in custom_traits {
match path.get_ident() {
Some(ident) => match ident.to_string().as_str() {
"Qualify" => custom_qualify = true,
"Default" => custom_default = true,
"Encode" => custom_encode = true,
"Encompass" => custom_encompass = true,
"Inspect" => custom_inspect = true,
"Debug" => custom_debug = true,
other => return TokenStream::from(quote ! {
::core::compile_error!(::core::concat!("Unrecognized identifier ", ::core::stringify!(#other), " in #[custom] attribute"));
})
}
None => return TokenStream::from(quote! {
::core::compile_error!("Failed to read identifier in #[custom] attribute");
}),
}
}
let derived_module_ident = format_ident!("{}_manifestable", ident);
let registry_implementation = quote! {
#[allow(non_snake_case)]
mod #derived_module_ident {
use super::*;
struct Registry {
meta_by_type_id: ::std::collections::HashMap<::std::any::TypeId, #core_path::manifest::ManifestableMeta<Box<dyn #ident>>>,
meta_by_qualifier: ::std::collections::HashMap<&'static str, #core_path::manifest::ManifestableMeta<Box<dyn #ident>>>,
}
static REGISTRY: #core_path::once_cell::sync::Lazy<Registry> = #core_path::once_cell::sync::Lazy::new(|| {
let mut meta_by_type_id = ::std::collections::HashMap::new();
let mut meta_by_qualifier = ::std::collections::HashMap::new();
for registration in #core_path::inventory::iter::<#core_path::manifest::ManifestableRegistration<Box<dyn #ident>>> {
let type_id = (registration.type_id_fn)();
let qualifier = (registration.qualifier_fn)();
let meta = #core_path::manifest::ManifestableMeta {
type_id,
qualifier,
default_instance: Box::leak(Box::new((registration.default_fn)())),
default_fn: registration.default_fn,
encode_fn: registration.encode_fn,
decode_fn: registration.decode_fn,
encompass_fn: registration.encompass_fn,
inspect_fn: registration.inspect_fn,
debug_fn: registration.debug_fn,
};
let meta_clone = meta.clone();
meta_by_type_id.insert(type_id, meta);
meta_by_qualifier.insert(qualifier, meta_clone);
}
Registry { meta_by_type_id, meta_by_qualifier }
});
impl dyn #ident {
pub fn iter_meta() -> #core_path::manifest::ManifestableMetaIterator<'static, Box<dyn #ident>> {
#core_path::manifest::ManifestableMetaIterator::<'static, Box<dyn #ident>>::new(
REGISTRY.meta_by_type_id.values()
)
}
pub fn get_meta_by_type_id(type_id: ::std::any::TypeId) -> Option<#core_path::manifest::ManifestableMeta<Box<dyn #ident>>> {
let meta = REGISTRY.meta_by_type_id.get(&type_id)?;
Some(meta.clone())
}
pub fn get_meta_by_qualifier(qualifier: &str) -> Option<#core_path::manifest::ManifestableMeta<Box<dyn #ident>>> {
let meta = REGISTRY.meta_by_qualifier.get(qualifier)?;
Some(meta.clone())
}
}
}
};
let qualify_implementation = if custom_qualify { quote! {} } else {
quote! {
impl #core_path::manifest::Qualify for Box<dyn #ident> {
fn qualifier() -> &'static str {
::core::concat!(::core::module_path!(), "::", ::core::stringify!(#ident))
}
}
}
};
let default_implementation = if custom_default { quote! {} } else {
quote! {
impl ::std::default::Default for Box<dyn #ident> {
fn default() -> Self {
Box::new(#default_type_path::default())
}
}
}
};
let encode_implementation = if custom_encode { quote! {} } else {
quote! {
impl #core_path::encode::Encode for Box<dyn #ident> {
fn encode(&self, encoder: &mut #core_path::encode::Encoder) {
use ::std::any::Any;
let type_id = (**self).type_id();
let qualifier = <dyn #ident>::get_meta_by_type_id(type_id).map(|meta| meta.qualifier).unwrap_or_default();
encoder.output_dyn_start();
encoder.output_dyn_qualifier(qualifier);
match <dyn #ident>::get_meta_by_type_id(type_id) {
None => encoder.output_null(),
Some(meta) => (meta.encode_fn)(self, encoder),
}
}
fn decode(&mut self, decoder: &mut #core_path::encode::Decoder) -> Result<(), Box<dyn ::std::error::Error>> {
match decoder.maybe_dyn_start()? {
None => decoder.skip_value()?,
Some(_) => {
let qualifier = decoder.expect_dyn_qualifier()?;
match <dyn #ident>::get_meta_by_qualifier(&qualifier) {
None => decoder.skip_value()?,
Some(meta) => {
(meta.decode_fn)(self, decoder)?;
}
}
}
}
Ok(())
}
}
}
};
let encompass_implementation = if custom_encompass { quote!{} } else {
quote! {
impl #core_path::encompass::Encompass for Box<dyn #ident> {
fn encompass(&self, v: &mut #core_path::encompass::EncompassVisitor) -> Result<(), Box<dyn std::error::Error>> {
let type_id = (**self).type_id();
match <dyn #ident>::get_meta_by_type_id(type_id) {
None => (),
Some(meta) => (meta.encompass_fn)(self, v)?,
}
Ok(())
}
}
}
};
let inspect_implementation = if custom_inspect { quote!{} } else {
quote! {
impl #core_path::inspect::Inspect for Box<dyn #ident> {
fn inspect(&mut self, ctx: &mut #core_path::inspect::InspectContext, ui: &mut #core_path::egui::Ui) {
let type_id = (**self).type_id();
match <dyn #ident>::get_meta_by_type_id(type_id) {
None => (),
Some(meta) => (meta.inspect_fn)(self, ctx, ui),
}
}
}
}
};
let debug_implementation = if custom_debug { quote!{} } else {
quote! {
impl ::std::fmt::Debug for Box<dyn #ident> {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
use std::any::Any;
let type_id = (**self).type_id();
let qualifier = <dyn #ident>::get_meta_by_type_id(type_id).map(|meta| meta.qualifier).unwrap_or_default();
let mut debug_tuple = f.debug_tuple(::core::concat!("dyn ", ::core::stringify!(#ident)));
debug_tuple.field(&qualifier);
match <dyn #ident>::get_meta_by_type_id(type_id) {
Some(meta) => {
struct DebugWrapper<'a>(&'a Box<dyn #ident>, fn(&Box<dyn #ident>, &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result);
impl<'a> std::fmt::Debug for DebugWrapper<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
(self.1)(self.0, f)
}
}
debug_tuple.field(&DebugWrapper(self, meta.debug_fn));
}
None => return Err(std::fmt::Error),
}
debug_tuple.finish()
}
}
}
};
let manifest_implementation = quote! {
impl #core_path::manifest::Manifest for Box<dyn #ident> {}
#core_path::inventory::submit! {
#core_path::manifest::ManifestRegistration {
type_id_fn: || ::std::any::TypeId::of::<Box<dyn #ident>>(),
qualifier_fn: || {
use #core_path::manifest::Qualify;
Box::<dyn #ident>::qualifier()
},
default_fn: || {
use ::std::default::Default;
Box::new(Box::<dyn #ident>::default())
},
encode_fn: |v, encoder| {
let force_downcasted: &Box<dyn #ident> = unsafe {
let (data, _vtable): (*const (), *const ()) = ::std::mem::transmute(v);
::std::mem::transmute(data)
};
encoder.delegate_encoding(force_downcasted)
},
decode_fn: |v, decoder| -> Result<(), Box<dyn ::std::error::Error>> {
let force_downcasted: &mut Box<dyn #ident> = unsafe {
let (data, _vtable): (*const (), *const ()) = ::std::mem::transmute(v);
::std::mem::transmute(data)
};
decoder.delegate_decoding(force_downcasted)?;
Ok(())
},
encompass_fn: |v, visitor| {
let force_downcasted: &Box<dyn #ident> = unsafe {
let (data, _vtable): (*const (), *const ()) = ::std::mem::transmute(v);
::std::mem::transmute(data)
};
use #core_path::encompass::Encompass;
force_downcasted.encompass(visitor)
},
inspect_fn: |v, ui_context, ui| {
let force_downcasted: &mut Box<dyn #ident> = unsafe {
let (data, _vtable): (*const (), *const ()) = ::std::mem::transmute(v);
::std::mem::transmute(data)
};
use #core_path::inspect::Inspect;
force_downcasted.inspect(ui_context, ui);
},
debug_fn: |v, formatter| {
let force_downcasted: &Box<dyn #ident> = unsafe {
let (data, _vtable): (*const (), *const ()) = ::std::mem::transmute(v);
::std::mem::transmute(data)
};
use ::std::fmt::Debug;
force_downcasted.fmt(formatter)
},
}
}
};
let full_implementation = quote! {
#trait_item
#registry_implementation
#qualify_implementation
#default_implementation
#encode_implementation
#encompass_implementation
#inspect_implementation
#debug_implementation
#manifest_implementation
};
full_implementation.into()
}
pub fn default_attr(attrs: &mut Vec<Attribute>) -> Option<Path> {
let mut default_type_path = None;
attrs.retain(|attr| {
let remove_attribute: bool;
if attr.path().is_ident("default") {
match attr.parse_args::<Path>() {
Ok(path) => {
default_type_path = Some(path);
remove_attribute = true;
}
Err(_) => remove_attribute = false,
}
} else {
remove_attribute = false
}
!remove_attribute
});
default_type_path
}
pub fn custom_attr(attrs: &mut Vec<Attribute>) -> Vec<Path> {
let mut custom_traits = Vec::new();
attrs.retain(|attr| {
let remove_attribute: bool;
if attr.path().is_ident("custom") {
match attr.parse_args_with(syn::punctuated::Punctuated::<Path, Token![,]>::parse_terminated) {
Ok(punctuated) => {
custom_traits = punctuated.into_iter().collect();
remove_attribute = true;
}
Err(_) => remove_attribute = false,
}
} else {
remove_attribute = false
}
!remove_attribute
});
return custom_traits;
}
pub fn insert_any_supertrait(trait_item: &mut ItemTrait) {
let any_path: Path = parse2(quote!(::std::any::Any)).unwrap();
let trait_bound = TraitBound {
paren_token: None,
modifier: TraitBoundModifier::None,
lifetimes: None,
path: any_path,
};
let any_bound = TypeParamBound::Trait(trait_bound);
trait_item.supertraits.push(any_bound);
}