Skip to main content

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}