summaryrefslogtreecommitdiff
path: root/systems/sheet/src
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-02-25 10:26:57 +0800
committer魏曹先生 <1992414357@qq.com>2026-02-25 10:26:57 +0800
commit05bf408a7f08c2f38479366307bd1e3c038b7b40 (patch)
tree4e17272fff33e01b24923b299a5c5843a909c9b4 /systems/sheet/src
parentc171285118026e2339570d55914f749a5d0e47af (diff)
Add ordering for LocalMapping based on custom character groups
Diffstat (limited to 'systems/sheet/src')
-rw-r--r--systems/sheet/src/mapping.rs116
1 files changed, 116 insertions, 0 deletions
diff --git a/systems/sheet/src/mapping.rs b/systems/sheet/src/mapping.rs
index b2acaa7..469e083 100644
--- a/systems/sheet/src/mapping.rs
+++ b/systems/sheet/src/mapping.rs
@@ -1,3 +1,5 @@
+use std::cmp::Ordering;
+
use just_fmt::fmt_path::{PathFormatConfig, fmt_path_str, fmt_path_str_custom};
use serde::{Deserialize, Serialize};
@@ -625,3 +627,117 @@ pub fn node(str: impl Into<String>) -> Vec<String> {
let str = just_fmt::fmt_path::fmt_path_str(str).unwrap_or_default();
str.split("/").into_iter().map(|f| f.to_string()).collect()
}
+
+// Implement comparison for LocalMapping
+
+impl PartialOrd for LocalMapping {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for LocalMapping {
+ fn cmp(&self, other: &Self) -> Ordering {
+ 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)
+/// 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();
+ let mut b_chars = b.chars();
+
+ loop {
+ match (a_chars.next(), b_chars.next()) {
+ (Some(ca), Some(cb)) => {
+ let ord = compare_char(ca, cb);
+ if ord != Ordering::Equal {
+ return ord;
+ }
+ }
+ (None, Some(_)) => return Ordering::Less,
+ (Some(_), None) => return Ordering::Greater,
+ (None, None) => return Ordering::Equal,
+ }
+ }
+}
+
+/// 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)
+/// 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 => a.cmp(&b), // Digits 0‑9
+ 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);
+}