fc_dioxus/behavior/
sort.rs1use std::cmp::Ordering;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub enum SortDirection {
14 Ascending,
16 Descending,
18}
19impl SortDirection {
20 #[must_use]
22 pub const fn as_str(self) -> &'static str {
23 match self {
24 Self::Ascending => "ascending",
25 Self::Descending => "descending",
26 }
27 }
28}
29
30#[must_use]
33pub fn next_direction(current: Option<(usize, SortDirection)>, column: usize) -> SortDirection {
34 match current {
35 Some((sorted, SortDirection::Ascending)) if sorted == column => SortDirection::Descending,
36 _ => SortDirection::Ascending,
37 }
38}
39
40#[must_use]
42pub fn as_number(value: &str) -> Option<f64> {
43 let cleaned: String = value
44 .chars()
45 .filter(|c| !matches!(c, ',' | ' ' | '\u{a0}' | '\u{202f}') && !c.is_whitespace())
46 .collect();
47 if cleaned.is_empty() {
48 return None;
49 }
50 cleaned.parse::<f64>().ok().filter(|n| n.is_finite())
51}
52
53#[must_use]
64pub fn compare(a: &str, b: &str) -> Ordering {
65 match (as_number(a), as_number(b)) {
66 (Some(x), Some(y)) => x.partial_cmp(&y).unwrap_or(Ordering::Equal),
67 _ => natural(a, b),
68 }
69}
70
71fn natural(a: &str, b: &str) -> Ordering {
72 let mut left = a.chars().peekable();
73 let mut right = b.chars().peekable();
74 loop {
75 match (left.peek().copied(), right.peek().copied()) {
76 (None, None) => return Ordering::Equal,
77 (None, Some(_)) => return Ordering::Less,
78 (Some(_), None) => return Ordering::Greater,
79 (Some(x), Some(y)) if x.is_ascii_digit() && y.is_ascii_digit() => {
80 let take = |it: &mut std::iter::Peekable<std::str::Chars<'_>>| {
81 let mut digits = String::new();
82 while let Some(c) = it.peek().copied().filter(char::is_ascii_digit) {
83 digits.push(c);
84 it.next();
85 }
86 digits
87 };
88 let (x, y) = (take(&mut left), take(&mut right));
89 let (xs, ys) = (x.trim_start_matches('0'), y.trim_start_matches('0'));
90 let order = xs.len().cmp(&ys.len()).then_with(|| xs.cmp(ys));
91 if order != Ordering::Equal {
92 return order;
93 }
94 }
95 (Some(x), Some(y)) => {
96 let order = x.to_lowercase().cmp(y.to_lowercase());
97 if order != Ordering::Equal {
98 return order;
99 }
100 left.next();
101 right.next();
102 }
103 }
104 }
105}
106
107#[must_use]
110pub fn order(keys: &[&str], direction: SortDirection) -> Vec<usize> {
111 let mut indices: Vec<usize> = (0..keys.len()).collect();
112 indices.sort_by(|&a, &b| {
113 let ordering = compare(
114 keys.get(a).copied().unwrap_or(""),
115 keys.get(b).copied().unwrap_or(""),
116 );
117 match direction {
118 SortDirection::Ascending => ordering,
119 SortDirection::Descending => ordering.reverse(),
120 }
121 });
122 indices
123}