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
//! Various helper functions

use regex::Regex;

/// Copy a string while incrementing its contained number
///
/// - If the string contains one or more numbers, the last one gets incremented
/// - If it does not contain a number, a "1" is appended instead
///
/// This way, the returned number is guaranteed to be different from the input
/// string. That's useful for example to generate unique, incrementing pin
/// numbers like "X1", "X2", "X3" etc.
///
/// # Arguments
///
/// * `s` - The input string.
///
/// # Returns
///
/// Returns a new string with the incremented number.
pub fn increment_number_in_string(s: &str) -> String {
  let mut ret = s.to_owned();

  let re = Regex::new(r"[0-9]+").unwrap();
  if let Some(mat) = re.find_iter(s).last() {
    // String contains numbers -> increment the last one.
    if let Ok(num) = mat.as_str().parse::<i32>() {
      ret.replace_range(mat.range(), &(num + 1).to_string());
      return ret;
    }
  }

  // Fallback: just add a "1" at the end.
  ret + "1"
}

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

  create! {data, (input, expected), {
    assert_eq!(increment_number_in_string(input), expected);
  }}
  data! {
    d01: ("",                        "1"),
    d02: ("  ",                      "  1"),
    d03: ("0",                       "1"),
    d04: ("1",                       "2"),
    d05: (" 123 ",                   " 124 "),
    d06: ("X",                       "X1"),
    d07: ("X-1",                     "X-2"),
    d08: ("GND 41",                  "GND 42"),
    d09: ("FOO1.2",                  "FOO1.3"),
    d10: ("12 foo 34",               "12 foo 35"),
    d11: ("12 foo 34 bar 56 ",       "12 foo 34 bar 57 "),
    d12: ("99A",                     "100A"),
  }
}