1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
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},
};

/// A store for assertions with some custom [`IdentGenerator`](ident_generator::IdentGenerator) to generate
/// unique identifiers to be used within asserts. [`DefaultStore`] is recommended for most
/// purposes, which uses a [`CountingIdentGenerator`].
///
///
/// This is the central type of this crate. It provides a target to generate assertions into.
/// All asserts are stored here before beeing turned into tokens (using [`quote::ToTokens`]).
///
/// Usually you want to use the [`assert_into!`](macro@crate::prelude::assert_into) macro to actually add the tokens.
///
/// # Example
/// ```
/// # use proc_macro_assertions::prelude::DefaultStore
/// let store = DefaultStore::new();
/// let token_to_assert_something_on = todo!();
/// assert_into!(store | token_to_assert_something_on impl std::default::Default);
/// let tokens = quote!{ #store };
/// ```
pub struct Store<'a, IdentGenerator = CountingIdentGenerator>
where
    IdentGenerator: ident_generator::IdentGenerator,
{
    pub(crate) generatables: GeneratableSet<'a>,
    pub(crate) ident_gen: IdentGenerator,
}

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);
    }

    pub fn new() -> Self
    where
        IdentGenerator: Default,
    {
        Default::default()
    }
}

impl<'a, IdentGenerator> Default for Store<'a, IdentGenerator>
where
    IdentGenerator: ident_generator::IdentGenerator + Default,
{
    fn default() -> Self {
        Self {
            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 {
            ident_generator: &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);
        tokens.extend(quote! {
            // const fn to ensure that this is only called at compile time, that is then ignored
            // This ensures that this is a zero cost abstraction and that the assertions are only
            // checked at compile time
            #[doc(hidden)]
            const _: fn() = ||  {
                #asserted_tokens
            };
        });
    }
}

#[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::Ident = parse_quote!(Test1);
        let mut store = DefaultStore::new();
        assert_into!(store | &test_ident with &generics == Test);
        assert_into!(store | &test_ident with &generics impl Debug);
        println!("{}", store.to_token_stream().to_string());
    }
}