use std::cmp;
use std::cmp::Ordering;
pub fn chap_cmp (string1: &str, string2: &str) -> Ordering {
let mut point = None;
for i in 0..cmp::min(string1.len(),string2.len()) {
let i = i as usize;
if string1[i..i+1] != string2[i..i+1] {
point = Some(i);
break;
}
}
match point {
Some(point) => {
if let Some(chap1) = gather_from(&string1, point) {
if let Some(chap2) = gather_from(&string2, point) {
if chap1.chars().next().unwrap().is_ascii_digit() {
if let Ok(num1) = chap1.parse::<u32>() {
if let Ok(num2) = chap2.parse::<u32>() {
return num1.cmp(&num2);
}
}
} if chap1.len() == chap2.len() {
return chap1.cmp(&chap2);
} else {
return chap1.len().cmp(&chap2.len());
}
}
}
return string1.cmp(&string2); },
None => { return string1.len().cmp(&string2.len());
},
}
}
pub fn chap_next (string: &str) -> String {
let mut result = string.to_string ();
if let Some((posn, chap)) = gather_backwards(&string) {
let mut new_val = "".to_string();
let mut letter_inc = |chap: &str| {
let chars: Vec<char> = chap.chars().collect();
let mut wrap = false;
let mut i = chars.len();
while i > 0 {
i -= 1;
new_val.insert(0, next_char(chars[i]));
if chars[i] == 'Z' && i == 0 {
wrap = true;
} else if chars[i] != 'Z' {
break;
}
}
if wrap {
new_val.push('A');
}
};
if chap.contains(|c:char| c.is_ascii_digit()) {
if let Ok(num) = chap.parse::<u32>() {
new_val = format!("{}", num+1);
} } else if chap.contains(|c:char| c.is_ascii_uppercase()) {
letter_inc(&chap);
} else { let chap = chap.to_uppercase();
letter_inc(&chap);
new_val = new_val.to_lowercase();
}
result.replace_range(posn..(posn+chap.len()), &new_val);
return result;
} else {
"".to_string()
}
}
fn next_char (c: char) -> char {
match c {
'A' => 'B',
'B' => 'C',
'C' => 'D',
'D' => 'E',
'E' => 'F',
'F' => 'G',
'G' => 'H',
'H' => 'I',
'I' => 'J',
'J' => 'K',
'K' => 'L',
'L' => 'M',
'M' => 'N',
'N' => 'O',
'O' => 'P',
'P' => 'Q',
'Q' => 'R',
'R' => 'S',
'S' => 'T',
'T' => 'U',
'U' => 'V',
'V' => 'W',
'W' => 'X',
'X' => 'Y',
'Y' => 'Z',
'Z' => 'A',
_ => c,
}
}
fn gather_from (string: &str, point: usize) -> Option<String> {
let chars: Vec<char> = string.chars().collect();
let mut result = "".to_string();
let mut i = point;
if chars[i].is_ascii_uppercase() {
while i < chars.len() && chars[i].is_ascii_uppercase() {
result.push(chars[i]);
i += 1;
}
} else if chars[i].is_ascii_lowercase() {
while i < chars.len() && chars[i].is_ascii_lowercase() {
result.push(chars[i]);
i += 1;
}
} else if chars[i].is_ascii_digit() {
while i < chars.len() && chars[i].is_ascii_digit() {
result.push(chars[i]);
i += 1;
}
} else {
return None;
}
Some(result)
}
fn gather_backwards (string: &str) -> Option<(usize,String)> {
let chars: Vec<char> = string.chars().collect();
let mut result = "".to_string();
let mut i = chars.len()-1;
while i > 0 && !chars[i].is_ascii_uppercase() && !chars[i].is_ascii_lowercase() && !chars[i].is_ascii_digit() {
i -= 1;
}
if chars[i].is_ascii_uppercase() {
while i > 0 && chars[i].is_ascii_uppercase() {
result.insert(0, chars[i]);
i -= 1;
}
} else if chars[i].is_ascii_lowercase() {
while i > 0 && chars[i].is_ascii_lowercase() {
result.insert(0, chars[i]);
i -= 1;
}
} else if chars[i].is_ascii_digit() {
while i > 0 && chars[i].is_ascii_digit() {
result.insert(0, chars[i]);
i -= 1;
}
} else {
return None;
}
Some((i+1, result))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gather_from () {
assert_eq!(gather_from("ABC", 0), Some("ABC".to_string()));
assert_eq!(gather_from("Section ABc", 8), Some("AB".to_string()));
assert_eq!(gather_from("Section ABc", 4), Some("ion".to_string()));
assert_eq!(gather_from("Sect_19 ", 4), None);
assert_eq!(gather_from("Sect_19 ", 5), Some("19".to_string()));
}
#[test]
fn test_chap_cmp () {
assert_eq!(chap_cmp("a_4", "a_4"), Ordering::Equal);
assert_eq!(chap_cmp("a_4", "a_10"), Ordering::Less);
assert_eq!(chap_cmp("Section 1.2.1", "Section 1.1.2"), Ordering::Greater);
assert_eq!(chap_cmp("Appendix D", "Appendix A"), Ordering::Greater);
assert_eq!(chap_cmp("Appendix W", "Appendix AA"), Ordering::Less);
assert_eq!(chap_cmp("Result^{3.99}", "Result^{4}"), Ordering::Less);
assert_eq!(chap_cmp("a 4", "a_10"), Ordering::Less); }
#[test]
fn test_chap_next () {
assert_eq!(chap_next("a_9"), "a_10");
assert_eq!(chap_next("Section 1.2.1"), "Section 1.2.2");
assert_eq!(chap_next("Appendix B"), "Appendix C");
assert_eq!(chap_next("Appendix b"), "Appendix c");
assert_eq!(chap_next("Appendix(Z)"), "Appendix(AA)");
assert_eq!(chap_next("Appendix(AZ)"), "Appendix(BA)");
assert_eq!(chap_next("Appendix(zz)"), "Appendix(aaa)");
assert_eq!(chap_next("Appendix(ZZ)"), "Appendix(AAA)");
}
}