1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//! Based on <https://crates.io/crates/macro_find_and_replace>
use proc_macro2::{Group, Literal, Punct, TokenStream, TokenTree};
use quote::{ToTokens, TokenStreamExt};
use syn::{parse::{Parse, ParseStream}, Ident};

pub enum SingleToken {
    Ident(Ident),
    Punct(Punct),
    Literal(Literal),
}

impl From<Ident> for SingleToken {
    fn from(value: Ident) -> Self {
        Self::Ident(value)
    }
}

impl From<Punct> for SingleToken {
    fn from(value: Punct) -> Self {
        Self::Punct(value)
    }
}

impl From<Literal> for SingleToken {
    fn from(value: Literal) -> Self {
        Self::Literal(value)
    }
}

impl Parse for SingleToken {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let tt: TokenTree = input.parse()?;
        match tt {
            TokenTree::Group(g) => Err(syn::Error::new(g.span(), "Only single tokens are allowed as needles")),
            TokenTree::Ident(i) => Ok(Self::Ident(i)),
            TokenTree::Punct(p) => Ok(Self::Punct(p)),
            TokenTree::Literal(l) => Ok(Self::Literal(l)),
        }
    }
}

impl ToTokens for SingleToken {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            SingleToken::Ident(i) => i.to_tokens(tokens),
            SingleToken::Punct(p) => p.to_tokens(tokens),
            SingleToken::Literal(l) => l.to_tokens(tokens),
        }
    }
}

impl PartialEq<TokenTree> for SingleToken {
    fn eq(&self, other: &TokenTree) -> bool {
        match (self, other) {
            (SingleToken::Ident(a), TokenTree::Ident(b)) if a == b => true,
            (SingleToken::Punct(a), TokenTree::Punct(b)) if a.as_char() == b.as_char() => true,
            (SingleToken::Literal(a), TokenTree::Literal(b)) if a.to_string() == b.to_string() => true,
            _ => false,
        }
    }
}



pub fn find_and_replace(needle: impl Into<SingleToken>, replacement: TokenStream, haystack: TokenStream) -> TokenStream {
    fn far(needle: &SingleToken, replacement: &TokenStream, haystack: TokenStream) -> TokenStream {
        let mut output = TokenStream::new();

        for tt in haystack {
            let tt = match tt {
                TokenTree::Group(g) => TokenTree::Group(Group::new(g.delimiter(), far(needle, replacement, g.stream()))),
                tt if needle == &tt => {
                    output.extend(replacement.clone());
                    continue;
                },
                tt => tt,
            };

            output.append(tt);
        }

        output
    }

    let needle = needle.into();

    far(&needle, &replacement, haystack)
}