use std::borrow::Cow;
use std::cmp::Ordering;
pub fn metafied_key(s: &str) -> Cow<'_, [u8]> {
if s.is_ascii() && !s.as_bytes().contains(&0) {
return Cow::Borrowed(s.as_bytes());
}
let raw = crate::ported::utils::unmetafy_str(s);
let mut out = Vec::with_capacity(raw.len() + raw.len() / 2);
for b in raw {
if crate::ported::utils::imeta_byte(b) {
out.push(crate::ported::zsh_h::Meta);
out.push(b ^ 32);
} else {
out.push(b);
}
}
Cow::Owned(out)
}
pub struct MetafiedOperand {
bytes: Vec<u8>,
backslash: bool,
}
impl MetafiedOperand {
pub fn new(s: &str) -> Self {
let mut bytes = metafied_key(s).into_owned();
let backslash = bytes.contains(&b'\\');
bytes.push(0);
MetafiedOperand { bytes, backslash }
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..self.bytes.len() - 1]
}
}
pub fn zstrcmp_operands(a: &MetafiedOperand, b: &MetafiedOperand, flags: u32) -> Ordering {
#[cfg(unix)]
{
let numeric = (crate::ported::zsh_h::SORTIT_NUMERICALLY
| crate::ported::zsh_h::SORTIT_NUMERICALLY_SIGNED) as u32;
if flags & numeric == 0 && !a.backslash && !b.backslash {
let c = unsafe {
libc::strcoll(
a.bytes.as_ptr() as *const libc::c_char,
b.bytes.as_ptr() as *const libc::c_char,
)
};
return c.cmp(&0);
}
}
crate::ported::sort::zstrcmp(a.as_bytes(), b.as_bytes(), flags)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn operands_agree_with_zstrcmp_on_every_matchcmp_flag_set() {
use crate::ported::zsh_h::{SORTIT_IGNORING_BACKSLASHES, SORTIT_NUMERICALLY};
let words = [
"", "a", "A", "alpha.txt", "README.md", "file10", "file9", "file09",
"x\\ y", "x y", "\\EurydiceCM\\", "Zotero", "dot", "日本語", "中文字",
"ascii", "a\\\\b", "a\\b", "-5", "5", "αβ", "zz",
];
let base = SORTIT_IGNORING_BACKSLASHES as u32;
for flags in [base, base | SORTIT_NUMERICALLY as u32] {
for a in words {
for b in words {
let want = crate::ported::sort::zstrcmp(
metafied_key(a),
metafied_key(b),
flags,
);
let got = zstrcmp_operands(
&MetafiedOperand::new(a),
&MetafiedOperand::new(b),
flags,
);
assert_eq!(got, want, "{a:?} vs {b:?} flags={flags}");
}
}
}
}
#[test]
fn ascii_is_borrowed_unchanged() {
assert!(matches!(metafied_key("alpha.txt"), Cow::Borrowed(b"alpha.txt")));
}
#[test]
fn continuation_bytes_in_the_imeta_range_are_escaped() {
assert_eq!(&*metafied_key("日"), &[0xe6, 0x83, 0x97 ^ 32, 0xa5][..]);
assert_eq!(&*metafied_key("α"), &[0xce, 0xb1][..]);
}
#[test]
fn a_meta_encoded_raw_byte_is_metafied_as_that_byte() {
let s: String = ['a', '\u{83}', char::from(0x90u8 ^ 32)].iter().collect();
assert_eq!(&*metafied_key(&s), &[b'a', 0x83, 0x90 ^ 32][..]);
}
}