tokel_engine/transform.rs
1//! The transformer pipeline and transformation traits.
2//!
3//! In Tokel, a transformer is a component that maps an input token stream
4//! to an output token stream. Transformers are applied sequentially to
5//! the fully resolved output of an expansion block (`[< ... >]`).
6//!
7//! This module defines the core [`Transformer`] trait that all built-in (and potentially
8//! custom) token manipulators must implement, along with standard utility transformers
9//! like [`Identity`].
10
11use std::{
12 any::Any,
13 borrow::Cow,
14 collections::hash_map::Entry,
15 fmt,
16 ops::{Deref, DerefMut},
17 ptr,
18};
19
20use ahash::AHashMap;
21
22use proc_macro2::TokenStream;
23
24use syn::parse::Nothing;
25
26/// A transformer in a pipeline.
27///
28/// A transformer takes a fully resolved `TokenStream` from the preceding step in an
29/// expansion block and mutates it into a new `TokenStream`.
30///
31/// Implementors of this trait define the behavior of individual operations (e.g.,
32/// `:case`, `:concatenate`, `:reverse`) in a `Pipeline`. Transformers can maintain
33/// internal state, allowing for complex, stateful token generation across a session.
34pub trait Transformer: Any {
35 /// Transforms an input [token stream] into an output one.
36 ///
37 /// It receives the `input` tokens and the raw `argument` token stream,
38 /// returning the modified tokens or a `syn::Error` if the transformation
39 /// is invalid.
40 ///
41 /// [token stream]: TokenStream
42 ///
43 /// # Errors
44 ///
45 /// The failure mode of this associated function is implementation-dependent.
46 fn transform(&mut self, input: TokenStream, argument: TokenStream) -> syn::Result<TokenStream>;
47}
48
49/// A centralized registry of [`Transformer`] of distinct types.
50///
51/// This maps an unique name to a boxed [`Transformer`] trait object.
52///
53/// # Identifier Resolution
54///
55/// When the Tokel engine encounters a transformer in a pipeline (e.g., `:case`),
56/// it converts the parsed `syn::Ident` to a string using exactly its written case.
57/// Raw identifiers (e.g., `r#case`) have their `r#` prefix stripped before lookup.
58///
59/// Therefore, transformer names registered here should typically be exact,
60/// `snake_case` string literals matching the intended syntax.
61pub struct Registry(AHashMap<Cow<'static, str>, Box<dyn Transformer>>);
62
63impl Registry {
64 /// Creates an empty [`Registry`].
65 #[must_use]
66 pub fn empty() -> Self {
67 Self(AHashMap::new())
68 }
69
70 /// Inserts a [`Transformer`] into the [`Registry`].
71 ///
72 /// If a [`Transformer`] was already registered with an identical name, it is yielded back.
73 #[inline]
74 pub fn insert<S, T>(&mut self, name: S, transformer: T) -> Option<Box<dyn Transformer>>
75 where
76 S: Into<Cow<'static, str>>,
77 T: Transformer,
78 {
79 let &mut Self(ref mut map) = self;
80
81 map.insert(S::into(name), Box::new(transformer))
82 }
83
84 /// Attempts to insert a [`Transformer`] into the [`Registry`].
85 ///
86 /// # Errors
87 ///
88 /// This will fail if another [`Transformer`] with the same name has already been registered.
89 #[inline]
90 pub fn try_insert<S, T>(
91 &mut self,
92 name: S,
93 transformer: T,
94 ) -> Result<&mut Box<dyn Transformer>, T>
95 where
96 S: Into<Cow<'static, str>>,
97 T: Transformer,
98 {
99 let &mut Self(ref mut map) = self;
100
101 let key = S::into(name);
102
103 match map.entry(key) {
104 Entry::Occupied(..) => Err(transformer),
105 Entry::Vacant(vacant_entry) => Ok(vacant_entry.insert(Box::new(transformer))),
106 }
107 }
108}
109
110impl Deref for Registry {
111 type Target = AHashMap<Cow<'static, str>, Box<dyn Transformer>>;
112
113 #[inline]
114 fn deref(&self) -> &Self::Target {
115 let Self(target_value) = self;
116
117 target_value
118 }
119}
120
121impl DerefMut for Registry {
122 #[inline]
123 fn deref_mut(&mut self) -> &mut Self::Target {
124 let Self(target_value) = self;
125
126 target_value
127 }
128}
129
130impl Default for Registry {
131 fn default() -> Self {
132 let mut target_value = Self::empty();
133
134 let _ = target_value.insert("identity", Identity);
135
136 target_value
137 }
138}
139
140impl fmt::Debug for Registry {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 let Self(target_value) = self;
143
144 let mut target_state = f.debug_map();
145
146 for (entry_key, entry_transformer) in target_value {
147 // NOTE: We don't know anything about the Transformer but its address, so let's mention it.
148 let target_value = &format_args!(
149 "<transformer at {:#x}>",
150 ptr::from_ref(entry_transformer).addr()
151 );
152
153 target_state.entry(entry_key, target_value);
154 }
155
156 target_state.finish()
157 }
158}
159
160/// An identity [`Transformer`].
161///
162/// This transformer acts as a no-op, passing the input token stream through
163/// without any mutations. It takes no arguments.
164#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
165pub struct Identity;
166
167impl Transformer for Identity {
168 #[inline]
169 fn transform(&mut self, input: TokenStream, argument: TokenStream) -> syn::Result<TokenStream> {
170 let _: Nothing = syn::parse2(argument)?;
171
172 Ok(input)
173 }
174}
175
176impl fmt::Display for Identity {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 f.write_str("Identity")
179 }
180}