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
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
use alloc::{format, string::String, vec, vec::Vec};
use core::{convert::TryFrom, iter::Cycle, ops::RangeInclusive};
use proc_macro2::Span;
use syn::{GenericParam, Generics, Lifetime, LifetimeDef};

pub struct LifetimeGenerator(Vec<(char, Cycle<RangeInclusive<char>>)>);

impl LifetimeGenerator {
    fn new() -> Self {
        Self(vec![Self::new_item()])
    }

    fn new_item() -> (char, Cycle<RangeInclusive<char>>) {
        let mut cycle = ('a'..='z').cycle();
        cycle.next();
        ('a', cycle)
    }

    fn get_current_string(&self) -> String {
        self.0.iter().map(|(c, _)| *c).collect()
    }

    fn inc_combo(&mut self, idx: usize) {
        let value = self.0.get_mut(idx).unwrap();
        let next = value.1.next().unwrap();
        value.0 = next;
        if next == 'a' {
            if idx == 0 {
                self.0.push(Self::new_item());
            } else {
                self.inc_combo(idx - 1);
            }
        }
    }
}

impl Iterator for LifetimeGenerator {
    type Item = String;

    fn next(&mut self) -> Option<Self::Item> {
        let rval = self.get_current_string();
        self.inc_combo(self.0.len() - 1);
        Some(format!("'{}", rval))
    }
}

impl Default for LifetimeGenerator {
    fn default() -> Self {
        Self::new()
    }
}

/// Creates the desired number of new lifetimes that won't conflict with the provided `Generics`
/// by continuously iterating through different possibilities of single letters (e.g. `'a`, `'b',
/// `'c', etcetera), adding and additional letter if all other lifetimes are exhausted. This then
/// returns an array of the requested lifetimes, as well as a clone of the `Generics` instance with
/// the lifetimes appended to them.
pub fn create_new_lifetimes<const N: usize>(base: &Generics) -> ([Lifetime; N], Generics) {
    let mut seen_lifetimes = Vec::new();
    let mut insert_at = 0;
    for (i, param) in base.params.iter().enumerate() {
        if let GenericParam::Lifetime(ref lifetime) = param {
            seen_lifetimes.push(format!("{}", lifetime.lifetime.ident));
        } else {
            insert_at = i;
            break;
        }
    }
    let mut new_lifetimes = Vec::with_capacity(N);
    let mut lifetime_gen = LifetimeGenerator::new();
    while new_lifetimes.len() < N {
        let lf_name = lifetime_gen.next().unwrap();
        if !seen_lifetimes.contains(&lf_name) {
            let lifetime = Lifetime::new(&lf_name, Span::call_site());
            new_lifetimes.push(lifetime);
        }
    }
    let mut generics = base.clone();
    for lf in new_lifetimes.iter() {
        let param = GenericParam::Lifetime(LifetimeDef::new(lf.clone()));
        generics.params.insert(insert_at, param);
    }
    let new_lifetimes = if let Ok(a) = <[Lifetime; N]>::try_from(new_lifetimes) {
        a
    } else {
        unreachable!();
    };
    (new_lifetimes, generics)
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_lifetime_generator() {
        let mut gen = LifetimeGenerator::new();
        for c in 'a'..='z' {
            assert_eq!(gen.next().unwrap(), format!("'{}", c));
        }
        for c in 'a'..='z' {
            assert_eq!(gen.next().unwrap(), format!("'a{}", c));
        }
        for c in 'a'..='z' {
            assert_eq!(gen.next().unwrap(), format!("'b{}", c));
        }
    }
}