use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use crate::{raw_assert::r#trait, token_store::TokenStore};
use super::{
context::Context,
generatable_set::GeneratableSet,
ident_generator::{self, CountingIdentGenerator},
};
pub struct Store<'a, IdentGenerator = CountingIdentGenerator>
where
IdentGenerator: ident_generator::IdentGenerator,
{
pub(crate) extra_items: TokenStream,
pub(crate) generatables: GeneratableSet<'a>,
pub(crate) ident_gen: IdentGenerator,
}
impl<'a, IdentGenerator> Store<'a, IdentGenerator>
where
IdentGenerator: ident_generator::IdentGenerator,
{
pub fn add_extra_items(&mut self, item: impl ToTokens) {
item.to_tokens(&mut self.extra_items);
}
}
#[allow(clippy::module_name_repetitions)]
pub type DefaultStore<'a> = Store<'a, CountingIdentGenerator>;
impl<'a, IdentGenerator> Store<'a, IdentGenerator>
where
IdentGenerator: ident_generator::IdentGenerator,
{
pub fn assert(&mut self, assert: impl r#trait::RawAssertable<'a>) {
assert.do_raw_assert(self);
}
#[must_use]
pub fn new() -> Self
where
IdentGenerator: Default,
{
Self::default()
}
}
impl<'a, IdentGenerator> Default for Store<'a, IdentGenerator>
where
IdentGenerator: ident_generator::IdentGenerator + Default,
{
fn default() -> Self {
Self {
extra_items: TokenStream::new(),
generatables: GeneratableSet::new(),
ident_gen: IdentGenerator::default(),
}
}
}
impl<'a, IdentGenerator> ToTokens for Store<'a, IdentGenerator>
where
IdentGenerator: ident_generator::IdentGenerator + Clone,
{
fn to_tokens(&self, tokens: &mut TokenStream) {
let mut ident_gen = self.ident_gen.clone();
let mut context = Context::new(&mut ident_gen);
let mut token_store = TokenStore::new();
for generatable in self.generatables.iter() {
generatable.generate_into(&mut context, &mut token_store);
}
let asserted_tokens = token_store.into_tokens(&mut context);
let extra_items = &self.extra_items;
let closure_contents = quote! {
#extra_items
#asserted_tokens
};
let closure = if context.requires_nonconstant_code() {
quote! {
let _ = || {
#closure_contents
};
}
} else {
quote! {
const _: fn() = || {
#closure_contents
};
}
};
tokens.extend(quote! {
#[doc(hidden)]
#[allow(warnings)]
#closure
});
}
}
#[cfg(test)]
mod test {
use quote::ToTokens;
use syn::parse_quote;
use crate::assert_into;
use super::DefaultStore;
#[test]
fn test() {
let generics = syn::Generics::default();
let test_ident: syn::Type = parse_quote!(Test1);
let test_type: syn::Type = syn::Type::Path(parse_quote!(Test2));
let mut store = DefaultStore::new();
assert_into!(store | &test_type with &generics == Test);
assert_into!(store | &test_ident with &generics impl Debug);
println!("{}", store.to_token_stream());
}
}