use std::cmp::Ordering;
const PUNCTUATION_ORDER: &str = " _-,;:!?.'\"()[]{}@*/\\&#%`^+<=>|~$";
fn primary(c: char) -> (u8, u32) {
if let Some(index) = PUNCTUATION_ORDER.find(c) {
return (0, u32::try_from(index).unwrap_or(u32::MAX));
}
if c.is_ascii_digit() {
return (1, u32::from(c));
}
if c.is_alphabetic() {
let lower = c.to_lowercase().next().unwrap_or(c);
return (2, u32::from(lower));
}
if c.is_ascii() {
return (0, 1000 + u32::from(c));
}
(3, u32::from(c))
}
fn tertiary(c: char) -> u8 {
u8::from(c.is_uppercase())
}
#[must_use]
pub(crate) fn locale_compare(a: &str, b: &str) -> Ordering {
a.chars()
.map(primary)
.cmp(b.chars().map(primary))
.then_with(|| a.chars().map(tertiary).cmp(b.chars().map(tertiary)))
.then_with(|| a.cmp(b))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn matches_v8_order() {
let mut ids = vec![
"a-b", "a_b", "ab", "a.b", "aB", "Ab", "a1", "a-1", "a10", "a9", "A", "a",
];
ids.sort_by(|a, b| locale_compare(a, b));
assert_eq!(
ids,
vec![
"a", "A", "a_b", "a-1", "a-b", "a.b", "a1", "a10", "a9", "ab", "aB", "Ab"
]
);
}
#[test]
fn matches_pi_fixture() {
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/resolver.json");
let data: serde_json::Value =
serde_json::from_str(&std::fs::read_to_string(path).unwrap()).unwrap();
let expected: Vec<String> = serde_json::from_value(data["collation"].clone()).unwrap();
let mut actual = expected.clone();
actual.reverse();
actual.sort_by(|a, b| locale_compare(a, b));
assert_eq!(actual, expected);
}
}