1use crate::transform::Registry;
12
13#[derive(Debug, Default)]
19pub struct Session(Registry);
20
21impl Session {
22 #[inline]
27 #[must_use]
28 pub fn new() -> Self {
29 Self::new_with(Registry::default())
30 }
31
32 #[inline]
37 #[must_use]
38 pub const fn new_with(registry: Registry) -> Self {
39 Self(registry)
40 }
41
42 #[inline]
48 #[must_use]
49 pub fn empty() -> Self {
50 Self(Registry::empty())
51 }
52
53 #[inline]
55 #[must_use]
56 pub const fn registry(&self) -> &Registry {
57 let Self(target_value) = self;
58
59 target_value
60 }
61
62 #[inline]
67 #[must_use]
68 pub const fn registry_mut(&mut self) -> &mut Registry {
69 let Self(target_value) = self;
70
71 target_value
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use crate::prelude::*;
78 use proc_macro2::TokenStream;
79 use quote::quote;
80
81 fn assert_streams_eq(actual: TokenStream, expected: TokenStream) {
84 let actual_str = actual.to_string().replace(" ", "");
85 let expected_str = expected.to_string().replace(" ", "");
86 assert_eq!(
87 actual_str,
88 expected_str,
89 "\nExpected: {}\nGot: {}",
90 expected.to_string(),
91 actual.to_string()
92 );
93 }
94
95 #[derive(Debug)]
97 struct DummyReverse;
98
99 impl Transformer for DummyReverse {
100 fn transform(
101 &mut self,
102 input: TokenStream,
103 _args: TokenStream,
104 ) -> Result<TokenStream, syn::Error> {
105 let mut tokens: Vec<_> = input.into_iter().collect();
106 tokens.reverse();
107
108 Ok(tokens.into_iter().collect())
109 }
110 }
111
112 #[derive(Debug)]
114 struct DummyAppend;
115 impl Transformer for DummyAppend {
116 fn transform(
117 &mut self,
118 mut input: TokenStream,
119 args: TokenStream,
120 ) -> Result<TokenStream, syn::Error> {
121 input.extend(args);
122 Ok(input)
123 }
124 }
125
126 #[derive(Default, Debug)]
128 struct DummyCount(usize);
129 impl Transformer for DummyCount {
130 fn transform(
131 &mut self,
132 _input: TokenStream,
133 _args: TokenStream,
134 ) -> Result<TokenStream, syn::Error> {
135 let count = self.0;
136 self.0 += 1;
137 let count_str = count.to_string();
139 let lit: proc_macro2::Literal = syn::parse_str(&count_str).unwrap();
140 Ok(quote!(#lit))
141 }
142 }
143
144 #[derive(Debug)]
146 struct DummyError;
147 impl Transformer for DummyError {
148 fn transform(
149 &mut self,
150 input: TokenStream,
151 _args: TokenStream,
152 ) -> Result<TokenStream, syn::Error> {
153 let span = input
155 .into_iter()
156 .next()
157 .map(|t| t.span())
158 .unwrap_or_else(proc_macro2::Span::call_site);
159 Err(syn::Error::new(span, "Deliberate test error"))
160 }
161 }
162
163 #[test]
164 fn test_standard_rust_passthrough() {
165 let mut session = Session::empty();
166
167 let input = quote! {
168 pub fn process_data(data: &[u8]) -> Result<(), Error> {
169 let x = 10 + 20;
170 Ok(())
171 }
172 };
173
174 let ast: TokelStream = syn::parse2(input.clone()).unwrap();
175 let output = ast.expand(&mut session).expect("Failed to expand");
176
177 assert_streams_eq(output, input);
178 }
179
180 #[test]
181 fn test_identity_block_resolution() {
182 let mut session = Session::empty();
183
184 let input = quote! {
186 let val = [< 10 + 20 >];
187 };
188
189 let ast: TokelStream = syn::parse2(input).unwrap();
190 let output = ast.expand(&mut session).unwrap();
191
192 let expected = quote! {
193 let val = 10 + 20;
194 };
195
196 assert_streams_eq(output, expected);
197 }
198
199 #[test]
200 fn test_single_transformer_with_args() {
201 let mut session = Session::empty();
202 session
203 .registry_mut()
204 .try_insert("append", DummyAppend)
205 .unwrap();
206
207 let input = quote! {
208 let word = [< hello >]:append[[ _world ]];
209 };
210
211 let ast: TokelStream = syn::parse2(input).unwrap();
212 let output = ast.expand(&mut session).unwrap();
213
214 let expected = quote! {
215 let word = hello _world;
216 };
217
218 assert_streams_eq(output, expected);
219 }
220
221 #[test]
222 fn test_pipeline_chaining() {
223 let mut session = Session::empty();
224 session
225 .registry_mut()
226 .try_insert("append", DummyAppend)
227 .unwrap();
228 session
229 .registry_mut()
230 .try_insert("reverse", DummyReverse)
231 .unwrap();
232
233 let input = quote! { [< a b >]:append[[c]]:reverse };
238
239 let ast: TokelStream = syn::parse2(input).unwrap();
240 let output = ast.expand(&mut session).unwrap();
241
242 let expected = quote! { c b a };
243 assert_streams_eq(output, expected);
244 }
245
246 #[test]
247 fn test_deep_bottom_up_nesting() {
248 let mut session = Session::empty();
249 session
250 .registry_mut()
251 .try_insert("append", DummyAppend)
252 .unwrap();
253 session
254 .registry_mut()
255 .try_insert("reverse", DummyReverse)
256 .unwrap();
257
258 let input = quote! {
262 [< [< [< a b >]:reverse c >]:append[[d]] >]:reverse
263 };
264
265 let ast: TokelStream = syn::parse2(input).unwrap();
266 let output = ast.expand(&mut session).unwrap();
267
268 let expected = quote! { d c a b };
269 assert_streams_eq(output, expected);
270 }
271
272 #[test]
273 fn test_stateful_session_isolation() {
274 let mut session = Session::empty();
275 session
276 .registry_mut()
277 .try_insert("count", DummyCount::default())
278 .unwrap();
279
280 let input = quote! {
283 let first = [< >]:count;
284 let second = [< >]:count;
285 let inner_wins = [< [< >]:count >]:count;
286 };
287
288 let ast: TokelStream = syn::parse2(input).unwrap();
289 let output = ast.expand(&mut session).unwrap();
290
291 let expected = quote! {
295 let first = 0;
296 let second = 1;
297 let inner_wins = 3;
298 };
299
300 assert_streams_eq(output, expected);
301 }
302
303 #[test]
304 fn test_transformer_error_propagation() {
305 let mut session = Session::empty();
306 session
307 .registry_mut()
308 .try_insert("error_out", DummyError)
309 .unwrap();
310
311 let input = quote! {
312 fn good() {}
313 [< bad_tokens >]:error_out
314 };
315
316 let ast: TokelStream = syn::parse2(input).unwrap();
317 let result = ast.expand(&mut session);
318
319 assert!(
320 result.is_err(),
321 "Engine failed to bubble up the transformer error"
322 );
323 assert_eq!(result.unwrap_err().to_string(), "Deliberate test error");
324 }
325
326 #[test]
327 fn test_unknown_transformer_error() {
328 let mut session = Session::empty();
329 let input = quote! { [< x >]:non_existent_transformer };
332
333 let ast: TokelStream = syn::parse2(input).unwrap();
334 let result = ast.expand(&mut session);
335
336 assert!(result.is_err());
337 let err_msg = result.unwrap_err().to_string();
338 assert!(
339 err_msg.contains("non_existent_transformer"),
340 "Error message should mention the missing transformer"
341 );
342 }
343
344 #[test]
345 fn test_nested_arguments() {
346 let mut session = Session::empty();
347 session
348 .registry_mut()
349 .try_insert("append", DummyAppend)
350 .unwrap();
351 session
352 .registry_mut()
353 .try_insert("reverse", DummyReverse)
354 .unwrap();
355
356 let input = quote! {
360 [< A B >]:append[[ [< 1 2 >]:reverse ]]
361 };
362
363 let ast: TokelStream = syn::parse2(input).unwrap();
364 let output = ast.expand(&mut session).unwrap();
365
366 let expected = quote! { A B 2 1 };
367 assert_streams_eq(output, expected);
368 }
369}