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, Parse};
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 trait that represents a singular pass over an input [token stream].
50///
51/// This is a stronger-typed alternative to the [`Transformer`] trait, which is obligated to be dyn-safe.
52///
53/// # Blanket Implementation
54///
55/// A blanket implementation for [`Transformer`] for all implementors of [`Pass`] is automatically performed.
56///
57/// It is advisable to use generic bounds of this type in respect to [`Transformer`].
58///
59/// [token stream]: TokenStream
60pub trait Pass: Transformer {
61 /// The argument type used by this pass.
62 ///
63 /// If no argument is required, it is advisable to use the [`Nothing`] type.
64 type Argument: Parse;
65
66 /// Pass-through an input [token stream] with an appropiate source-parsable argument.
67 fn through(&mut self, input: TokenStream, argument: Self::Argument)
68 -> syn::Result<TokenStream>;
69}
70
71/// A blanket implementation for [`Transformer`] for all [`Pass`] implementors.
72impl<T> Transformer for T
73where
74 T: Pass + Any,
75{
76 fn transform(&mut self, input: TokenStream, argument: TokenStream) -> syn::Result<TokenStream> {
77 <T as Pass>::through(self, input, syn::parse2::<T::Argument>(argument)?)
78 }
79}
80
81/// A centralized registry of [`Transformer`] of distinct types.
82///
83/// This maps an unique name to a boxed [`Transformer`] trait object.
84///
85/// # Identifier Resolution
86///
87/// When the Tokel engine encounters a transformer in a pipeline (e.g., `:case`),
88/// it converts the parsed `syn::Ident` to a string using exactly its written case.
89/// Raw identifiers (e.g., `r#case`) have their `r#` prefix stripped before lookup.
90///
91/// Therefore, transformer names registered here should typically be exact,
92/// `snake_case` string literals matching the intended syntax.
93pub struct Registry(AHashMap<Cow<'static, str>, Box<dyn Transformer>>);
94
95impl Registry {
96 /// Creates an empty [`Registry`].
97 #[must_use]
98 pub fn empty() -> Self {
99 Self(AHashMap::new())
100 }
101
102 /// Inserts a [`Transformer`] into the [`Registry`].
103 ///
104 /// If a [`Transformer`] was already registered with an identical name, it is yielded back.
105 #[inline]
106 pub fn insert<S, T>(&mut self, name: S, transformer: T) -> Option<Box<dyn Transformer>>
107 where
108 S: Into<Cow<'static, str>>,
109 T: Transformer,
110 {
111 let &mut Self(ref mut map) = self;
112
113 map.insert(S::into(name), Box::new(transformer))
114 }
115
116 /// Attempts to insert a [`Transformer`] into the [`Registry`].
117 ///
118 /// # Errors
119 ///
120 /// This will fail if another [`Transformer`] with the same name has already been registered.
121 #[inline]
122 pub fn try_insert<S, T>(
123 &mut self,
124 name: S,
125 transformer: T,
126 ) -> Result<&mut Box<dyn Transformer>, T>
127 where
128 S: Into<Cow<'static, str>>,
129 T: Transformer,
130 {
131 let &mut Self(ref mut map) = self;
132
133 let key = S::into(name);
134
135 match map.entry(key) {
136 Entry::Occupied(..) => Err(transformer),
137 Entry::Vacant(vacant_entry) => Ok(vacant_entry.insert(Box::new(transformer))),
138 }
139 }
140}
141
142impl Deref for Registry {
143 type Target = AHashMap<Cow<'static, str>, Box<dyn Transformer>>;
144
145 #[inline]
146 fn deref(&self) -> &Self::Target {
147 let Self(target_value) = self;
148
149 target_value
150 }
151}
152
153impl DerefMut for Registry {
154 #[inline]
155 fn deref_mut(&mut self) -> &mut Self::Target {
156 let Self(target_value) = self;
157
158 target_value
159 }
160}
161
162impl Default for Registry {
163 fn default() -> Self {
164 let mut target_value = Self::empty();
165
166 let _ = target_value.insert("identity", Identity);
167
168 target_value
169 }
170}
171
172impl fmt::Debug for Registry {
173 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174 let Self(target_value) = self;
175
176 let mut target_state = f.debug_map();
177
178 for (entry_key, entry_transformer) in target_value {
179 // NOTE: We don't know anything about the Transformer but its address, so let's mention it.
180 let target_value = &format_args!(
181 "<transformer at {:#x}>",
182 ptr::from_ref(entry_transformer).addr()
183 );
184
185 target_state.entry(entry_key, target_value);
186 }
187
188 target_state.finish()
189 }
190}
191
192/// An identity [`Transformer`].
193///
194/// This transformer acts as a no-op, passing the input token stream through
195/// without any mutations. It takes no arguments.
196#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
197pub struct Identity;
198
199impl Pass for Identity {
200 type Argument = Nothing;
201
202 fn through(&mut self, input: TokenStream, _: Self::Argument) -> syn::Result<TokenStream> {
203 Ok(input)
204 }
205}
206
207impl fmt::Display for Identity {
208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209 f.write_str("Identity")
210 }
211}