diff options
Diffstat (limited to 'systems/sheet/src/mapping.rs')
| -rw-r--r-- | systems/sheet/src/mapping.rs | 160 |
1 files changed, 3 insertions, 157 deletions
diff --git a/systems/sheet/src/mapping.rs b/systems/sheet/src/mapping.rs index 3dfb67e..2e6645e 100644 --- a/systems/sheet/src/mapping.rs +++ b/systems/sheet/src/mapping.rs @@ -3,7 +3,9 @@ use std::cmp::Ordering; use just_fmt::fmt_path::{PathFormatConfig, fmt_path_str, fmt_path_str_custom}; use serde::{Deserialize, Serialize}; -use crate::{index_source::IndexSource, mapping::error::ParseMappingError}; +use crate::{ + compare::compare_vec_string, index_source::IndexSource, mapping::error::ParseMappingError, +}; pub mod error; pub mod parse; @@ -651,159 +653,3 @@ impl Ord for LocalMapping { compare_vec_string(&self.val, &other.val) } } - -/// 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) - compared numerically -/// 4. All other Unicode characters (in their natural order) -/// -/// The comparison is lexicographic: the first differing element determines the order. -fn compare_vec_string(a: &Vec<String>, b: &Vec<String>) -> std::cmp::Ordering { - use std::cmp::Ordering; - - for (left, right) in a.iter().zip(b.iter()) { - match compare_string(left, right) { - Ordering::Equal => continue, - ord => return ord, - } - } - // If all compared elements are equal, the shorter vector comes first. - a.len().cmp(&b.len()) -} - -/// 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().peekable(); - let mut b_chars = b.chars().peekable(); - - loop { - 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, - (Some(_), None) => return Ordering::Greater, - (None, None) => return Ordering::Equal, - } - } -} - -/// 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) - 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); - let group_b = char_group(b); - - if group_a != group_b { - return group_a.cmp(&group_b); - } - - // Same group: compare within the group. - match group_a { - CharGroup::AsciiSymbol => a.cmp(&b), // ASCII symbols in natural order - CharGroup::Letter => { - // Uppercase letters (A‑Z) come before lowercase (a‑z) - if a.is_ascii_uppercase() && b.is_ascii_lowercase() { - Ordering::Less - } else if a.is_ascii_lowercase() && b.is_ascii_uppercase() { - Ordering::Greater - } else { - a.cmp(&b) - } - } - 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) - } -} - -/// Classification of a character for ordering. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] -enum CharGroup { - AsciiSymbol = 0, // !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ - Letter = 1, // A‑Z, a‑z - Digit = 2, // 0‑9 - Other = 3, // Other -} - -fn char_group(c: char) -> CharGroup { - if c.is_ascii_punctuation() { - CharGroup::AsciiSymbol - } else if c.is_ascii_alphabetic() { - CharGroup::Letter - } else if c.is_ascii_digit() { - CharGroup::Digit - } else { - CharGroup::Other - } -} - -#[test] -fn test_compare_char_groups() { - assert!(compare_string("!", "A") == Ordering::Less); - assert!(compare_string("A", "a") == Ordering::Less); - 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 -} |
