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;
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}