Yep, sure, here you go:
I have some Trait and struct as stated above
trait Trait<'a> where
Self: Any
{
// ...
}
struct OtherS<'a>(&'a mut dyn Trait<'a>);
and would then like to something like the following
impl<'a> std::cmp::PartialOrd for OtherS<'a> {
fn partial_cmp(&self, other: &Rhs) -> Option<Ordering> {
Some(std::cmp::Ord(self,other))
}
}
impl<'a> PartialEq for OtherS<'a> {
fn eq(&self, other: &Self) -> bool {
(*self.0).type_id() == (*other.0).type_id()
}
}
impl std::cmp::Ord for OtherS<'a> {
fn cmp(&self, other: &Rhs) -> Ordering {
std::cmp::Ord::cmp((*self.0).type_id() as usize, (*other.0).type_id() as usize)
}
}
impl std::cmp::Eq for OtherS<'a> {}
and use it in a BTreeSet
to have a set of "unique" types like so
#[test]
fn some_test() {
let set: BTreeSet<OtherS> = BTreeSet::new();
}
Or alternatively something like
#[test]
fn some_test() {
let set: BTreeMap<TypeId, OtherS> = BTreeMap::new();
}
which I can then encapsulate in my own struct for better ergonomics.
I then use this Type inside one of my proc macros where i have a kind of Generator
Trait which should generate code once per Struct that implements it (So that's what this question is about), once per instance (which I then call a Template) and once per usage of that template in a method on this trait, which is also easy enough. I am just kinda stuck on how to ensure that every "level" is only called once (and additionally every level may return data to which a reference is passed to each of the following levels)
Here is my actual trait if that helps
pub trait Generatable {
type GeneratableData;
type TemplateData;
type Assert;
fn generatable(context: &mut Context) -> PassedData<Self::GeneratableData>
where
Self: Sized;
fn template(
&self,
context: &mut Context,
passed: &Self::GeneratableData,
) -> PassedData<Self::TemplateData>;
fn assert(
&self,
_context: &mut Context,
_passed: (&Self::GeneratableData, &Self::TemplateData),
_to_assert: &Self::Assert,
) -> Option<TokenStream> {
None
}
}
The end goal is to have kind of "asserts" for proc _macros similar to static_assertions - Rust which generate code that asserts inputs to be of a specific type (see the static_assertions docs for more details).
Some specific Generatable
would then be used like so ( where TraitTemplate
implements Generatable
)
let r#type = todo!(); /* some syn::Type from the macro input */
let assertion_store = assertions::Store::new();
assertion_store.assert(TraitTemplate::new(syn::parse_quote!(::std::fmt::Debug)).test(r#type));
quote!{
#store
}
(test and so on will come from a Template Trait which is implemented for any T: Generatable
)
maybe I will also add a macro to simplify it a bit further, something like
assert_into!(store | r#type impl ::std::fmt::Debug);
Here the actual impl for Generatable for TraitTemplate that already exists (which checks for a type to implement some trait)
pub struct Trait<'a> {
trait_bound: &'a TraitBound,
}
impl<'a> Generatable for Trait<'a> {
type GeneratableData = ();
type TemplateData = Ident;
type Assert = syn::Type;
fn template(
&self,
Context {
ident_generator, ..
}: &mut Context,
_passed: &Self::GeneratableData,
) -> PassedData<Self::TemplateData> {
let fn_ident = ident_generator.prefixed("assert_trait_bound");
let trait_bound = self.trait_bound;
quote! {
fn #fn_ident<T: #trait_bound>() {}
}
.with_data(fn_ident)
}
fn assert(
&self,
_context: &mut Context,
(_, assert_trait_bound): (&Self::GeneratableData, &Self::TemplateData),
to_assert: &Self::Assert,
) -> Option<TokenStream> {
Some(quote! {
#assert_trait_bound::<#to_assert>();
})
}
fn generatable(_context: &mut Context) -> PassedData<Self::GeneratableData>
where
Self: Sized,
{
PassedData::default()
}
}
or here one for type equality
pub struct Type<'a> {
pub r#type: &'a Ident,
pub generics_count: usize,
}
impl<'a> Generatable for Type<'a> {
type GeneratableData = Ident;
type TemplateData = ();
type Assert = syn::Type;
fn generatable(
Context {
ident_generator, ..
}: &mut Context,
) -> PassedData<Self::GeneratableData> {
let ident = ident_generator.prefixed("assert_type_eq");
quote! {
trait TypeEq {
type This: ?Sized;
}
impl<T: ?Sized> TypeEq for T {
type This = Self;
}
fn #ident<T, U>()
where
T: ?Sized + TypeEq<This = U>,
U: ?Sized,
{
}
}
.with_data(ident)
}
fn assert(
&self,
_context: &mut Context,
(assert_type_eq, _): (&Self::GeneratableData, &Self::TemplateData),
to_assert: &Self::Assert,
) -> Option<TokenStream> {
let type_bound = self.r#type;
if self.generics_count > 0 {
let generic_ident = Ident::new("_", Span::call_site());
let generic_ident_iter = iter::repeat(generic_ident).take(self.generics_count);
Some(quote! {
#assert_type_eq::<#to_assert,#type_bound<#(#generic_ident_iter),*>>();
})
} else {
Some(quote! {
#assert_type_eq::<#to_assert,#type_bound>();
})
}
}
fn template(
&self,
_context: &mut Context,
_passed: &Self::GeneratableData,
) -> PassedData<Self::TemplateData>
where
Self::TemplateData: Default,
{
PassedData::default()
}
}