use alloc::string::String;
use crate::upcase::Upcase;
pub(crate) fn name_eq(query: &[u8], ondisk: &[u16], up: &Upcase) -> bool {
let Ok(query) = core::str::from_utf8(query) else {
return false;
};
let mut on = ondisk.iter().map(|&c| up.up(c));
let mut buf = [0u16; 2];
for ch in query.chars() {
for &unit in ch.encode_utf16(&mut buf).iter() {
match on.next() {
Some(o) if o == up.up(unit) => {}
_ => return false,
}
}
}
on.next().is_none()
}
pub(crate) fn decode_lossy(ondisk: &[u16]) -> String {
char::decode_utf16(ondisk.iter().copied())
.map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER))
.collect()
}
#[cfg(test)]
mod tests {
extern crate alloc;
use alloc::vec::Vec;
use super::*;
fn utf16(s: &str) -> Vec<u16> {
s.encode_utf16().collect()
}
#[test]
fn ascii_compare_is_case_insensitive() {
let up = Upcase::ascii();
let on = utf16("README.TXT");
assert!(name_eq(b"readme.txt", &on, &up));
assert!(name_eq(b"ReAdMe.TxT", &on, &up));
assert!(name_eq(b"README.TXT", &on, &up));
assert!(!name_eq(b"readme.tx", &on, &up));
assert!(!name_eq(b"readme.txtx", &on, &up));
assert!(!name_eq(b"other", &on, &up));
}
#[test]
fn non_ascii_matches_exact_case() {
let up = Upcase::ascii();
let on = utf16("café");
assert!(name_eq("café".as_bytes(), &on, &up));
}
#[test]
fn invalid_utf8_query_never_matches() {
let up = Upcase::ascii();
let on = utf16("x");
assert!(!name_eq(&[0xFF, 0xFE], &on, &up));
}
#[test]
fn decode_replaces_lone_surrogate() {
assert_eq!(decode_lossy(&utf16("grub.cfg")), "grub.cfg");
assert_eq!(decode_lossy(&[0xD800]), "\u{FFFD}");
}
}