use alloc::borrow::Cow;
use core::cmp::Ordering;
use icu_casemap::CaseMapperBorrowed;
static CASE_MAPPER: CaseMapperBorrowed<'static> = CaseMapperBorrowed::new();
pub fn fold(s: &str) -> Cow<'_, str> {
CASE_MAPPER.fold_string(s)
}
pub fn eq(a: &str, b: &str) -> bool {
if a.is_ascii() && b.is_ascii() {
return a.eq_ignore_ascii_case(b);
}
CASE_MAPPER.fold_string(a) == CASE_MAPPER.fold_string(b)
}
pub fn cmp(a: &str, second: &str) -> Ordering {
if a.is_ascii() && second.is_ascii() {
let ord = a
.bytes()
.map(|byte| byte.to_ascii_lowercase())
.cmp(second.bytes().map(|byte| byte.to_ascii_lowercase()));
return ord;
}
let fa = CASE_MAPPER.fold_string(a);
let fb = CASE_MAPPER.fold_string(second);
fa.cmp(&fb)
}
pub fn contains(haystack: &str, needle: &str) -> bool {
if haystack.is_ascii() && needle.is_ascii() {
let h = haystack.as_bytes();
let n = needle.as_bytes();
if n.is_empty() {
return true;
}
if n.len() > h.len() {
return false;
}
return h.windows(n.len()).any(|w| w.eq_ignore_ascii_case(n));
}
let fh = CASE_MAPPER.fold_string(haystack);
let fn_ = CASE_MAPPER.fold_string(needle);
fh.contains(&*fn_)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn eq_ascii_same_case() {
assert!(eq("hello", "hello"));
}
#[test]
fn eq_ascii_diff_case() {
assert!(eq("Hello", "hELLO"));
}
#[test]
fn eq_sharp_s() {
assert!(eq("Straße", "STRASSE"));
assert!(eq("straße", "strasse"));
}
#[test]
fn eq_greek_sigma() {
assert!(eq("ΣΕΛΑΣ", "σελας"));
assert!(eq("σελας", "σελας"));
}
#[test]
fn eq_ligature() {
assert!(eq("ffi", "ffi"));
assert!(eq("ffi", "FFI"));
}
#[test]
fn eq_not_equal() {
assert!(!eq("abc", "abd"));
assert!(!eq("abc", "ab"));
}
#[test]
fn eq_empty() {
assert!(eq("", ""));
assert!(!eq("", "a"));
}
#[test]
fn cmp_equal() {
assert_eq!(cmp("hello", "HELLO"), Ordering::Equal);
}
#[test]
fn cmp_less() {
assert_eq!(cmp("alpha", "BETA"), Ordering::Less);
}
#[test]
fn cmp_greater() {
assert_eq!(cmp("beta", "ALPHA"), Ordering::Greater);
}
#[test]
fn contains_ascii() {
assert!(contains("Hello World", "lo wo"));
}
#[test]
fn contains_empty_needle() {
assert!(contains("anything", ""));
}
#[test]
fn contains_sharp_s() {
assert!(contains("Die Straße ist lang", "STRASSE"));
}
#[test]
fn contains_no_match() {
assert!(!contains("hello", "xyz"));
}
#[test]
fn fold_ascii_lowercase_borrows() {
let result = fold("hello");
assert!(matches!(result, Cow::Borrowed(_)));
assert_eq!(&*result, "hello");
}
#[test]
fn fold_sharp_s() {
let result = fold("Straße");
assert_eq!(&*result, "strasse");
}
}