diff options
Diffstat (limited to 'systems/sheet/src/mapping.rs')
| -rw-r--r-- | systems/sheet/src/mapping.rs | 76 |
1 files changed, 66 insertions, 10 deletions
diff --git a/systems/sheet/src/mapping.rs b/systems/sheet/src/mapping.rs index 469e083..f509c0b 100644 --- a/systems/sheet/src/mapping.rs +++ b/systems/sheet/src/mapping.rs @@ -645,7 +645,7 @@ impl Ord for LocalMapping { /// Compare two `Vec<String>` according to a specific ordering: /// 1. ASCII symbols (excluding letters and digits) /// 2. Letters (Aa-Zz, case‑sensitive, uppercase before lowercase) -/// 3. Digits (0‑9) +/// 3. Digits (0‑9) - compared numerically /// 4. All other Unicode characters (in their natural order) /// /// The comparison is lexicographic: the first differing element determines the order. @@ -664,15 +664,28 @@ fn compare_vec_string(a: &Vec<String>, b: &Vec<String>) -> std::cmp::Ordering { /// Compare two individual strings with the same ordering rules. fn compare_string(a: &str, b: &str) -> std::cmp::Ordering { - let mut a_chars = a.chars(); - let mut b_chars = b.chars(); + let mut a_chars = a.chars().peekable(); + let mut b_chars = b.chars().peekable(); loop { - match (a_chars.next(), b_chars.next()) { - (Some(ca), Some(cb)) => { - let ord = compare_char(ca, cb); - if ord != Ordering::Equal { - return ord; + match (a_chars.peek(), b_chars.peek()) { + (Some(&ca), Some(&cb)) => { + if ca.is_ascii_digit() && cb.is_ascii_digit() { + // Parse both numbers and compare numerically + let a_num = parse_number(&mut a_chars); + let b_num = parse_number(&mut b_chars); + match a_num.cmp(&b_num) { + Ordering::Equal => continue, + ord => return ord, + } + } else { + // Non-digit comparison + let ord = compare_char(ca, cb); + if ord != Ordering::Equal { + return ord; + } + a_chars.next(); + b_chars.next(); } } (None, Some(_)) => return Ordering::Less, @@ -682,10 +695,26 @@ fn compare_string(a: &str, b: &str) -> std::cmp::Ordering { } } +/// Parse a number from the character iterator. +/// Consumes consecutive ASCII digits and returns the parsed u64. +/// Assumes the first character is already verified to be a digit. +fn parse_number<I: Iterator<Item = char>>(chars: &mut std::iter::Peekable<I>) -> u64 { + let mut num = 0; + while let Some(&c) = chars.peek() { + if c.is_ascii_digit() { + num = num * 10 + (c as u64 - '0' as u64); + chars.next(); + } else { + break; + } + } + num +} + /// Compare two characters according to the ordering: /// 1. ASCII symbols (non‑letter, non‑digit) /// 2. Letters (A‑Z then a‑z) -/// 3. Digits (0‑9) +/// 3. Digits (0‑9) - note: digits are handled specially in compare_string /// 4. Other Unicode fn compare_char(a: char, b: char) -> std::cmp::Ordering { let group_a = char_group(a); @@ -708,7 +737,11 @@ fn compare_char(a: char, b: char) -> std::cmp::Ordering { a.cmp(&b) } } - CharGroup::Digit => a.cmp(&b), // Digits 0‑9 + CharGroup::Digit => { + // Digits should be compared numerically, but this is only reached + // when comparing single digits (not part of a longer number). + a.cmp(&b) + } CharGroup::Other => a.cmp(&b), // Other Unicode (natural order) } } @@ -741,3 +774,26 @@ fn test_compare_char_groups() { assert!(compare_string("a", "0") == Ordering::Less); assert!(compare_string("9", "你") == Ordering::Less); } + +#[test] +fn test_numeric_ordering() { + // Test numeric ordering + assert!(compare_string("0", "1") == Ordering::Less); + assert!(compare_string("1", "0") == Ordering::Greater); + assert!(compare_string("9", "10") == Ordering::Less); + assert!(compare_string("10", "9") == Ordering::Greater); + assert!(compare_string("99", "100") == Ordering::Less); + assert!(compare_string("100", "99") == Ordering::Greater); + assert!(compare_string("001", "1") == Ordering::Equal); // "001" numerically equals "1" + assert!(compare_string("01", "1") == Ordering::Equal); // "01" numerically equals "1" + + // Test mixed strings + assert!(compare_string("Frame-9", "Frame-10") == Ordering::Less); + assert!(compare_string("Frame-10", "Frame-9") == Ordering::Greater); + assert!(compare_string("Frame-99", "Frame-100") == Ordering::Less); + assert!(compare_string("Frame-100", "Frame-99") == Ordering::Greater); + + // Test that numbers are compared as whole numbers, not digit-by-digit + assert!(compare_string("123", "23") == Ordering::Greater); // 123 > 23 + assert!(compare_string("23", "123") == Ordering::Less); // 23 < 123 +} |
