ident_concat/
lib.rs

1//! [`ident!`], [`replace!`]
2extern crate proc_macro;
3use proc_macro::{token_stream::IntoIter, *};
4
5fn concat(ts: &mut IntoIter, end: Option<char>) -> Ident {
6    let mut ident = String::new();
7    for x in ts {
8        if let TokenTree::Ident(x) = x {
9            ident = ident + &x.to_string();
10        } else if matches!(end, Some(e) if matches!(x, TokenTree::Punct(x) if x == e)) {
11            break;
12        }
13    }
14    Ident::new(&ident, Span::call_site())
15}
16
17#[proc_macro]
18/// Concatenates identifiers.
19/// ```
20/// use ident_concat::ident;
21/// let ident!(a b) = 4;
22/// assert_eq!(ab, ident!(a b));
23/// ```
24pub fn ident(ts: TokenStream) -> TokenStream {
25    TokenStream::from(TokenTree::Ident(concat(&mut ts.into_iter(), None)))
26}
27
28fn convert_replace(tt: TokenTree, to_replace: &TokenTree, ident: &Ident) -> TokenTree {
29    match (&tt, to_replace) {
30        (TokenTree::Ident(x), TokenTree::Ident(to_replace))
31            if x.to_string() == to_replace.to_string() =>
32        {
33            TokenTree::Ident(ident.clone())
34        }
35        (TokenTree::Group(x), _) => TokenTree::Group(Group::new(
36            x.delimiter(),
37            TokenStream::from_iter(
38                x.stream()
39                    .into_iter()
40                    .map(|x| convert_replace(x, to_replace, ident)),
41            ),
42        )),
43        _ => tt,
44    }
45}
46
47#[proc_macro]
48/// Replaces identifiers with concatenated ones.
49/// ```
50/// use ident_concat::replace;
51/// replace!{ placeholder te st:
52///     fn placeholder() -> u32 { 4 }
53/// }
54/// assert_eq!(test(), 4);
55/// ```
56pub fn replace(ts: TokenStream) -> TokenStream {
57    let mut iter = ts.into_iter();
58    let to_replace = iter
59        .next()
60        .expect("syntax: replace!(to_replace with _this: to_replace) => with_this");
61    let ident = concat(&mut iter, Some(':'));
62    TokenStream::from_iter(iter.map(|x| convert_replace(x, &to_replace, &ident)))
63}