interface_rs/helper/
sort.rs

1use std::cmp::Ordering;
2
3/// Compares two strings using natural sort order.
4/// Natural sort order compares numbers in a way humans expect,
5/// so "swp10" comes after "swp2" instead of "swp10" being less than "swp2" as in ASCII sorting.
6///
7/// # Arguments
8/// - `a`: The first string to compare.
9/// - `b`: The second string to compare.
10///
11/// # Returns
12/// An `Ordering` indicating the result of the comparison.
13pub fn natural(a: &str, b: &str) -> Ordering {
14    let mut a_iter = a.chars().peekable();
15    let mut b_iter = b.chars().peekable();
16
17    loop {
18        match (a_iter.peek(), b_iter.peek()) {
19            (Some(a_c), Some(b_c)) => {
20                if a_c.is_digit(10) && b_c.is_digit(10) {
21                    // Extract full numbers
22                    let mut a_num = String::new();
23                    while let Some(c) = a_iter.peek() {
24                        if c.is_digit(10) {
25                            a_num.push(*c);
26                            a_iter.next();
27                        } else {
28                            break;
29                        }
30                    }
31
32                    let mut b_num = String::new();
33                    while let Some(c) = b_iter.peek() {
34                        if c.is_digit(10) {
35                            b_num.push(*c);
36                            b_iter.next();
37                        } else {
38                            break;
39                        }
40                    }
41
42                    let a_int = a_num.parse::<u64>().unwrap();
43                    let b_int = b_num.parse::<u64>().unwrap();
44
45                    match a_int.cmp(&b_int) {
46                        Ordering::Equal => continue,
47                        other => return other,
48                    }
49                } else {
50                    // Compare characters
51                    match a_c.cmp(b_c) {
52                        Ordering::Equal => {
53                            a_iter.next();
54                            b_iter.next();
55                            continue;
56                        }
57                        other => return other,
58                    }
59                }
60            }
61            (Some(_), None) => return Ordering::Greater,
62            (None, Some(_)) => return Ordering::Less,
63            (None, None) => return Ordering::Equal,
64        }
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn test_sort_natural() {
74        assert_eq!(natural("swp1", "swp2"), Ordering::Less);
75        assert_eq!(natural("swp10", "swp2"), Ordering::Greater);
76        assert_eq!(natural("swp10", "swp10"), Ordering::Equal);
77        assert_eq!(natural("swp100", "swp20"), Ordering::Greater);
78        assert_eq!(natural("swp2", "swp2"), Ordering::Equal);
79        assert_eq!(natural("swp10s1", "swp10s2"), Ordering::Less);
80        assert_eq!(natural("swp10", "swp9s1"), Ordering::Greater);
81    }
82}