use better_any::{Tid, TidAble};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{Ident, TraitBound};
use crate::{
context::Context,
into_template::IntoTemplate,
maybe_borrowed::MaybeBorrowed,
passed_data::{PassedData, WithData},
};
use super::Generatable;
#[derive(Tid)]
pub struct Trait<'a> {
trait_bound: MaybeBorrowed<'a, TraitBound>,
}
impl<'a> Trait<'a> {
pub fn new<T>(trait_bound: T) -> Self
where
T: Into<MaybeBorrowed<'a, TraitBound>>,
{
Self {
trait_bound: trait_bound.into(),
}
}
}
impl<'a> IntoTemplate<'a> for TraitBound {
type Template = Trait<'a>;
fn into_template<T: crate::into_template::TypeEq<This = Self::Template>>(
self,
) -> Self::Template {
Trait::new(self)
}
}
impl<'a> IntoTemplate<'a> for &'a TraitBound {
type Template = Trait<'a>;
fn into_template<T: crate::into_template::TypeEq<This = Self::Template>>(
self,
) -> Self::Template {
Trait::new(self)
}
}
impl<'a> PartialOrd for Trait<'a> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(Ord::cmp(self, other))
}
}
impl<'a> Ord for Trait<'a> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
Ord::cmp(
&self.trait_bound.to_token_stream().to_string(),
&other.trait_bound.to_token_stream().to_string(),
)
}
}
impl<'a> PartialEq for Trait<'a> {
fn eq(&self, other: &Self) -> bool {
PartialEq::eq(
&self.trait_bound.to_token_stream().to_string(),
&other.trait_bound.to_token_stream().to_string(),
)
}
}
impl<'a> Eq for Trait<'a> {}
impl<'a> Generatable<'a> for Trait<'a> {
type GeneratableData = ();
type TemplateData = Ident;
type Assert = Ident;
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()
}
}
#[cfg(test)]
mod test {
use quote::quote;
use syn::parse_quote;
use crate::{
context::Context,
generatable::Generatable,
ident_generator::MockIdentGenerator,
maybe_borrowed::MaybeBorrowed,
passed_data::{PassedData, WithData},
};
use super::Trait;
#[test]
fn generate_generatable() {
let mut mock_ident_gen = MockIdentGenerator::new();
let mut context = Context {
ident_generator: &mut mock_ident_gen,
};
assert_eq!(Trait::generatable(&mut context), PassedData::default());
assert!(mock_ident_gen.was_not_called())
}
#[test]
fn generate_template() {
let mut mock_ident_gen = MockIdentGenerator::new();
mock_ident_gen.push_ident("mock_1");
let mut context = Context {
ident_generator: &mut mock_ident_gen,
};
assert_eq!(
Trait {
trait_bound: MaybeBorrowed::Owned(parse_quote!(::test_mod::Test))
}
.template(&mut context, &()),
quote! {
fn assert_trait_bound_mock_1<T: ::test_mod::Test>() {}
}
.with_data(parse_quote!(assert_trait_bound_mock_1))
);
assert!(mock_ident_gen.has_no_idents_remaining());
}
#[test]
fn generate_assert() {
let mut mock_ident_gen = MockIdentGenerator::new();
let mut context = Context {
ident_generator: &mut mock_ident_gen,
};
assert_eq!(
Trait {
trait_bound: MaybeBorrowed::Owned(parse_quote!(::test_mod::Test))
}
.assert(
&mut context,
(&(), &parse_quote!(assert_trait_bound_mock_1)),
&parse_quote!(TestType)
)
.as_ref()
.map(ToString::to_string),
Some(quote! {
assert_trait_bound_mock_1::<TestType>();
})
.as_ref()
.map(ToString::to_string)
);
assert!(mock_ident_gen.was_not_called());
}
}