1pub(crate) fn str_succ(s: &str) -> String {
14 let mut chars: Vec<char> = s.chars().collect();
15
16 let index: Option<usize> = chars
18 .iter()
19 .enumerate()
20 .rev()
21 .filter_map(|(i, c)| CharRange::from_char(*c).map(|_| i))
22 .next();
23 match index {
24 Some(index) => {
25 let mut carry: char = '1';
26 for i in (0..=index).rev() {
27 let ch = chars[i];
28 let range = match CharRange::from_char(ch) {
29 None => {
30 chars.insert(i + 1, carry);
31 break;
32 }
33 Some(range) => range,
34 };
35 carry = range.carry();
36 let (start, end) = range.bound();
37 if ch == end {
38 chars[i] = start;
39 if i == 0 {
40 chars.insert(0, range.carry());
41 }
42 } else {
43 chars[i] = ((ch as u8) + 1) as char;
44 break;
45 }
46 }
47 chars.into_iter().collect()
48 }
49 None => format!("{}1", s),
50 }
51}
52
53#[derive(Copy, Clone, PartialEq)]
55enum CharRange {
56 Digit,
57 LowerLetter,
58 UpperLetter,
59}
60
61impl CharRange {
62 const ALL: &'static [Self] = &[Self::Digit, Self::LowerLetter, Self::UpperLetter];
63
64 fn bound(self) -> (char, char) {
66 match self {
67 CharRange::Digit => ('0', '9'),
68 CharRange::LowerLetter => ('a', 'z'),
69 CharRange::UpperLetter => ('A', 'Z'),
70 }
71 }
72
73 fn carry(self) -> char {
75 match self {
76 CharRange::Digit => '1',
77 CharRange::LowerLetter => 'a',
78 CharRange::UpperLetter => 'A',
79 }
80 }
81
82 fn from_char(ch: char) -> Option<CharRange> {
84 Self::ALL.iter().copied().find(|range| {
85 let (start, end) = range.bound();
86 ch >= start && ch <= end
87 })
88 }
89}
90
91#[cfg(test)]
92#[test]
93fn test_str_succ() {
94 assert_eq!(str_succ("0"), "1");
96 assert_eq!(str_succ("1"), "2");
97 assert_eq!(str_succ("9"), "10");
98 assert_eq!(str_succ("233"), "234");
99 assert_eq!(str_succ("999"), "1000");
100 assert_eq!(str_succ("0099"), "0100");
101 assert_eq!(str_succ("1180591620717411303424"), "1180591620717411303425");
102
103 assert_eq!(str_succ("a-99"), "a-100");
104 assert_eq!(str_succ("aa99"), "ab00");
105 assert_eq!(str_succ("aa99.."), "ab00..");
106
107 assert_eq!(str_succ("a"), "b");
108 assert_eq!(str_succ("C"), "D");
109 assert_eq!(str_succ("ASDF"), "ASDG");
110 assert_eq!(str_succ("zz"), "aaa");
111
112 assert_eq!(str_succ("Zz"), "AAa");
113 assert_eq!(str_succ("9z9Z"), "10a0A");
114
115 assert_eq!(str_succ(""), "1");
117 assert_eq!(str_succ("."), ".1");
118}