Skip to main content

tokel_std/
string.rs

1//! String and text-manipulation Tokel [`Transformer`]s.
2
3use proc_macro2::{Ident, Span, TokenStream};
4
5use syn::parse::Nothing;
6
7use tokel_engine::prelude::{Registry, Transformer};
8
9/// A transformer that concatenates all input tokens into a single identifier.
10///
11/// It ignores standard spacing and simply glues the string representations
12/// of the tokens together.
13///
14/// # Example
15/// `[< hello _ world >]:concatenate` -> `hello_world`
16#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct Concatenate;
18
19impl Transformer for Concatenate {
20    fn transform(
21        &mut self,
22        input: TokenStream,
23        argument: TokenStream,
24    ) -> Result<TokenStream, syn::Error> {
25        // Concatenate takes no arguments, so we enforce that the `[[...]]` is empty.
26        let _: Nothing = syn::parse2(argument)?;
27
28        // If the input is completely empty, just return empty.
29        if input.is_empty() {
30            return Ok(input);
31        }
32
33        let mut concatenated_string = String::new();
34        let mut first_span = Span::call_site();
35        let mut is_first = true;
36
37        for tree in input {
38            if is_first {
39                first_span = tree.span();
40
41                is_first = false;
42            }
43
44            // `to_string()` on a TokenTree strips `r#` from idents and handles raw strings nicely.
45            concatenated_string.push_str(&tree.to_string());
46        }
47
48        // We must ensure the resulting string is a valid Rust identifier.
49        // `syn::Ident::new` will panic if the string is not a valid ident (e.g. if it starts with a number).
50        // To be safe, we try to parse it. If it fails, we return a syn::Error.
51        let parsed_ident = syn::parse_str::<Ident>(&concatenated_string).map_err(|_| {
52            syn::Error::new(
53                first_span,
54                format!("concatenated string `{concatenated_string}` is not a valid identifier"),
55            )
56        })?;
57
58        Ok(quote::quote_spanned!(first_span=> #parsed_ident))
59    }
60}
61
62/// Inserts all `string`-related [`Transformer`]s into the specified [`Registry`].
63///
64/// # Errors
65///
66/// This will fail if at least one standard `string`-related [`Transformer`] is already present by-name in the [`Registry`].
67///
68/// On failure, there is no guarantee that other non-colliding transformers have not been registered.
69#[inline]
70pub fn register(registry: &mut Registry) -> Result<(), Box<dyn Transformer>> {
71    registry
72        .try_insert("concatenate", Concatenate)
73        .map_err(Box::new)
74        .map_err(|target_value| target_value as Box<dyn Transformer>)?;
75
76    Ok(())
77}