use std::iter;
use better_any::{Tid, TidAble};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::Ident;
use crate::{
context::Context,
maybe_borrowed::MaybeBorrowed,
passed_data::{PassedData, WithData},
};
use super::Generatable;
#[derive(Tid, PartialEq, Eq, PartialOrd, Ord)]
pub struct Type<'a> {
pub r#type: MaybeBorrowed<'a, Ident>,
pub generics_count: usize,
}
impl<'a> Type<'a> {
pub fn new<T>(r#type: T, generics_count: usize) -> Self
where
T: Into<MaybeBorrowed<'a, Ident>>,
{
Self {
r#type: r#type.into(),
generics_count,
}
}
}
impl<'a> Generatable<'a> for Type<'a> {
type GeneratableData = Ident;
type TemplateData = ();
type Assert = syn::Ident;
fn generatable(
Context {
ident_generator, ..
}: &mut Context,
) -> PassedData<Self::GeneratableData> {
let ident = ident_generator.prefixed("assert_type_eq");
let type_eq_ident = ident_generator.prefixed("TypeEq");
quote! {
trait #type_eq_ident {
type This: ?Sized;
}
impl<T: ?Sized> #type_eq_ident 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()
}
}
#[cfg(test)]
mod test {
use crate::ident_generator::MockIdentGenerator;
use quote::quote;
use syn::parse_quote;
use super::*;
#[test]
fn generate_generatable() {
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!(
Type::generatable(&mut context),
quote! {
trait TypeEq {
type This: ?Sized;
}
impl<T: ?Sized> TypeEq for T {
type This = Self;
}
fn assert_type_eq_mock_1<T, U>()
where
T: ?Sized + TypeEq<This = U>,
U: ?Sized,
{
}
}
.with_data(parse_quote!(assert_type_eq_mock_1)),
);
assert!(mock_ident_gen.has_no_idents_remaining());
}
#[test]
fn generate_template() {
let mut mock_ident_gen = MockIdentGenerator::new();
let mut context = Context {
ident_generator: &mut mock_ident_gen,
};
assert_eq!(
Type {
r#type: MaybeBorrowed::Owned(parse_quote!(Type)),
generics_count: 2,
}
.template(&mut context, &parse_quote!(assert_type_eq_mock_1)),
PassedData::<()>::default(),
);
assert!(mock_ident_gen.was_not_called());
}
#[test]
fn generate_assert() {
let mut mock_ident_gen = MockIdentGenerator::new();
let mut context = Context {
ident_generator: &mut mock_ident_gen,
};
assert_eq!(
Type {
r#type: MaybeBorrowed::Owned(parse_quote!(Type)),
generics_count: 2,
}
.assert(
&mut context,
(&parse_quote!(assert_type_eq_mock_1), &()),
&syn::Ident::new("Test", Span::call_site())
)
.map(|x| x.to_string()),
Some(quote! {
assert_type_eq_mock_1::<Test,Type<_,_>>();
})
.map(|x| x.to_string())
);
assert!(mock_ident_gen.was_not_called());
}
}