pmhelp_internal/
util.rs

1use alloc::{
2    format,
3    string::{String, ToString},
4    vec,
5    vec::Vec,
6};
7use core::{convert::TryFrom, iter::Cycle, ops::RangeInclusive};
8use proc_macro2::Span;
9use syn::{parse_quote, GenericParam, Generics, Ident, Lifetime, LifetimeDef, TypeParam};
10
11pub struct LetterGenerator(Vec<(char, Cycle<RangeInclusive<char>>)>);
12
13impl LetterGenerator {
14    fn new() -> Self {
15        Self(vec![Self::new_item()])
16    }
17
18    fn new_item() -> (char, Cycle<RangeInclusive<char>>) {
19        let mut cycle = ('a'..='z').cycle();
20        cycle.next();
21        ('a', cycle)
22    }
23
24    fn get_current_string(&self) -> String {
25        self.0.iter().map(|(c, _)| *c).collect()
26    }
27
28    fn inc_combo(&mut self, idx: usize) {
29        let value = self.0.get_mut(idx).unwrap();
30        let next = value.1.next().unwrap();
31        value.0 = next;
32        if next == 'a' {
33            if idx == 0 {
34                self.0.push(Self::new_item());
35            } else {
36                self.inc_combo(idx - 1);
37            }
38        }
39    }
40}
41
42impl Iterator for LetterGenerator {
43    type Item = String;
44
45    fn next(&mut self) -> Option<Self::Item> {
46        let rval = self.get_current_string();
47        self.inc_combo(self.0.len() - 1);
48        Some(rval)
49    }
50}
51
52impl Default for LetterGenerator {
53    fn default() -> Self {
54        Self::new()
55    }
56}
57
58pub fn create_new_type_generics<const N: usize>(base: &Generics) -> ([Ident; N], Generics) {
59    let mut seen = Vec::new();
60    let mut insert_at = None;
61    for (i, param) in base.params.iter().enumerate() {
62        match &param {
63            GenericParam::Lifetime(_) => continue,
64            GenericParam::Type(TypeParam { ident, .. }) => {
65                if insert_at.is_none() {
66                    insert_at = Some(i)
67                }
68                seen.push(ident.to_string())
69            }
70            _ => {
71                if insert_at.is_none() {
72                    insert_at = Some(i)
73                }
74                break;
75            }
76        }
77    }
78    let mut new_types = Vec::with_capacity(N);
79    let mut type_gen = LetterGenerator::new().map(|l| format!("'{}", l));
80    while new_types.len() < N {
81        let name = type_gen.next().unwrap();
82        if !seen.contains(&name) {
83            let ty: TypeParam = parse_quote! { #name };
84            new_types.push(ty);
85        }
86    }
87    let insert_at = insert_at.unwrap_or(0);
88    let mut generics = base.clone();
89    for ty in new_types.iter() {
90        generics
91            .params
92            .insert(insert_at, GenericParam::Type(ty.clone()));
93    }
94    let type_idents = new_types
95        .into_iter()
96        .map(|tp| tp.ident)
97        .collect::<Vec<Ident>>();
98    if let Ok(ty) = <[Ident; N]>::try_from(type_idents) {
99        (ty, generics)
100    } else {
101        unreachable!()
102    }
103}
104
105/// Creates the desired number of new lifetimes that won't conflict with the provided `Generics`
106/// by continuously iterating through different possibilities of single letters (e.g. `'a`, `'b',
107/// `'c', etcetera), adding and additional letter if all other lifetimes are exhausted. This then
108/// returns an array of the requested lifetimes, as well as a clone of the `Generics` instance with
109/// the lifetimes appended to them.
110pub fn create_new_lifetimes<const N: usize>(base: &Generics) -> ([Lifetime; N], Generics) {
111    let mut seen_lifetimes = Vec::new();
112    let mut insert_at = 0;
113    for (i, param) in base.params.iter().enumerate() {
114        if let GenericParam::Lifetime(lifetime) = &param {
115            seen_lifetimes.push(lifetime.lifetime.ident.to_string());
116        } else {
117            insert_at = i;
118            break;
119        }
120    }
121    let mut new_lifetimes = Vec::with_capacity(N);
122    let mut lifetime_gen = LetterGenerator::new().map(|l| format!("'{}", l));
123    while new_lifetimes.len() < N {
124        let lf_name = lifetime_gen.next().unwrap();
125        if !seen_lifetimes.contains(&lf_name) {
126            let lifetime = Lifetime::new(&lf_name, Span::call_site());
127            new_lifetimes.push(lifetime);
128        }
129    }
130    let mut generics = base.clone();
131    for lf in new_lifetimes.iter() {
132        let param = GenericParam::Lifetime(LifetimeDef::new(lf.clone()));
133        generics.params.insert(insert_at, param);
134    }
135    if let Ok(lifetimes) = <[Lifetime; N]>::try_from(new_lifetimes) {
136        (lifetimes, generics)
137    } else {
138        unreachable!()
139    }
140}
141
142#[cfg(test)]
143mod test {
144    use super::*;
145
146    #[test]
147    fn test_lifetime_generator() {
148        let mut gen = LetterGenerator::new();
149        for c in 'a'..='z' {
150            assert_eq!(gen.next().unwrap(), format!("'{}", c));
151        }
152        for c in 'a'..='z' {
153            assert_eq!(gen.next().unwrap(), format!("'a{}", c));
154        }
155        for c in 'a'..='z' {
156            assert_eq!(gen.next().unwrap(), format!("'b{}", c));
157        }
158    }
159}