catchr_core/
scope.rs

1use proc_macro2::TokenStream;
2use quote::quote;
3
4#[derive(Clone, Debug)]
5pub struct Scope {
6    before: Vec<syn::Stmt>,
7    inner: Option<Box<Scope>>,
8    after: Vec<syn::Stmt>,
9}
10
11impl Scope {
12    pub fn empty() -> Self {
13        Self {
14            before: vec![],
15            inner: None,
16            after: vec![],
17        }
18    }
19
20    pub fn new(before: &[syn::Stmt], after: &[syn::Stmt]) -> Self {
21        Self {
22            before: Vec::from(before),
23            inner: None,
24            after: Vec::from(after),
25        }
26    }
27
28    pub fn push_mut(&mut self, before: &[syn::Stmt], after: &[syn::Stmt]) {
29        if let Some(inner) = self.inner.as_mut() {
30            inner.push_mut(before, after);
31        } else {
32            self.inner = Some(Box::new(Scope::new(before, after)));
33        }
34    }
35
36    pub fn quote_with(&self, stmts: &[syn::Stmt]) -> TokenStream {
37        let Scope {
38            before,
39            inner,
40            after,
41        } = &self;
42
43        if let Some(inner) = inner.as_ref() {
44            let inner = inner.quote_with(stmts);
45
46            quote! {
47                #(#before)*
48                {
49                    #inner
50                }
51                #(#after)*
52            }
53        } else {
54            quote! {
55                #(#before)*
56                {
57                    #(#stmts)*
58                }
59                #(#after)*
60            }
61        }
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use quote::quote;
68    use syn::parse_quote;
69
70    use super::*;
71
72    fn assert_eq_string(exp: impl ToString, act: impl ToString) {
73        assert_eq!(exp.to_string(), act.to_string());
74    }
75
76    #[test]
77    fn quote_empty_scope() {
78        let scope = Scope::empty();
79
80        let act = scope.quote_with(&[]);
81
82        assert_eq_string(quote!({}), act);
83    }
84
85    #[test]
86    fn quote_empty_scope_with_items() {
87        let scope = Scope::empty();
88
89        let act = scope.quote_with(&[
90            parse_quote!(let x = 1;),
91            parse_quote!(assert_eq!(x, 1);),
92        ]);
93
94        assert_eq_string(
95            quote!({
96                let x = 1;
97                assert_eq!(x, 1);
98            }),
99            act,
100        );
101    }
102
103    #[test]
104    fn quote_non_empty_scope() {
105        let scope = Scope::new(
106            &[parse_quote!(let x = 1;)],
107            &[parse_quote!(assert_eq!(x, 1);)],
108        );
109
110        let act = scope.quote_with(&[]);
111
112        assert_eq_string(
113            quote!(
114                let x = 1;
115                {
116
117                }
118                assert_eq!(x, 1);
119            ),
120            act,
121        );
122    }
123
124    #[test]
125    fn quote_non_empty_scope_with_items() {
126        let scope = Scope::new(
127            &[parse_quote!(let x = 1;)],
128            &[parse_quote!(assert_eq!(x, 1);)],
129        );
130
131        let act = scope.quote_with(&[parse_quote!(assert!(true);)]);
132
133        assert_eq_string(
134            quote!(
135                let x = 1;
136                {
137                    assert!(true);
138                }
139                assert_eq!(x, 1);
140            ),
141            act,
142        );
143    }
144
145    #[test]
146    fn push_empty_scope() {
147        let mut scope = Scope::empty();
148        scope.push_mut(&[], &[]);
149
150        let act = scope.quote_with(&[]);
151
152        assert_eq_string(quote!({ {} }), act);
153    }
154
155    #[test]
156    fn push_non_empty_scope() {
157        let mut scope = Scope::new(
158            &[parse_quote!(let x = 1;)],
159            &[parse_quote!(assert_eq!(x, 1);)],
160        );
161
162        scope.push_mut(
163            &[parse_quote!(assert!(true);)],
164            &[parse_quote!(assert!(false);)],
165        );
166
167        let act = scope.quote_with(&[parse_quote!(assert!(true);)]);
168
169        assert_eq_string(
170            quote!(
171                let x = 1;
172                {
173                    assert!(true);
174                    {
175                        assert!(true);
176                    }
177                    assert!(false);
178                }
179                assert_eq!(x, 1);
180            ),
181            act,
182        );
183    }
184}