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
89
90
use std::collections::BTreeMap;

use proc_macro2::Span;
use syn::Ident;

use super::IdentGenerator;

#[derive(Debug, Default, Clone)]
pub struct CountingIdentGenerator {
    idents: BTreeMap<Option<&'static str>, usize>,
}

/// A [`IdentGenerator`] that works by counting up from 0 for each prefix
/// for each invocation.
impl CountingIdentGenerator {
    /// Create a new counting ident generator.
    pub fn new() -> Self {
        Default::default()
    }
}

impl IdentGenerator for CountingIdentGenerator {
    fn generate(&mut self, prefix: Option<&'static str>, span: Span) -> Ident {
        let counter = self.idents.entry(prefix).or_default();

        *counter += 1;

        if let Some(ident) = prefix {
            Ident::new(format!("C_{ident}_{counter}").as_str(), span)
        } else {
            Ident::new(format!("C_{counter}").as_str(), span)
        }
    }
}

#[cfg(test)]
mod test {
    use proc_macro2::Span;
    use syn::Ident;

    use crate::ident_generator::IdentGenerator;

    use super::CountingIdentGenerator;

    #[test]
    fn ident() {
        let mut ident_gen = CountingIdentGenerator::new();

        for i in 1..=100 {
            assert_eq!(
                ident_gen.ident(),
                Ident::new(format!("C_{i}").as_str(), Span::call_site())
            );
        }
    }

    #[test]
    fn prefixed() {
        let mut ident_gen = CountingIdentGenerator::new();

        for i in 1..=100 {
            assert_eq!(
                ident_gen.prefixed("prefix"),
                Ident::new(format!("C_prefix_{i}").as_str(), Span::call_site())
            );
        }
    }

    #[test]
    fn spanned() {
        let mut ident_gen = CountingIdentGenerator::new();

        assert_eq!(
            ident_gen.spanned(Span::mixed_site()),
            Ident::new("C_1", Span::mixed_site())
        );
    }

    #[test]
    fn prefixed_spanned() {
        let mut ident_gen = CountingIdentGenerator::new();

        for i in 1..=100 {
            assert_eq!(
                ident_gen.generate(Some("prefix"), Span::mixed_site()),
                Ident::new(format!("C_prefix_{i}").as_str(), Span::mixed_site())
            );
        }
    }
}