use std::borrow::Cow;
const RAW_BASE: u32 = 0x0010_FF00;
pub fn decode(bytes: &[u8]) -> String {
let mut out = String::with_capacity(bytes.len());
let mut rest = bytes;
loop {
match std::str::from_utf8(rest) {
Ok(s) => {
out.push_str(s);
return out;
}
Err(e) => {
let (good, bad) = rest.split_at(e.valid_up_to());
out.push_str(std::str::from_utf8(good).unwrap_or_default());
let n = e.error_len().unwrap_or(bad.len());
out.extend(bad[..n].iter().map(|&b| placeholder(b)));
rest = &bad[n..];
}
}
}
}
pub fn encode(name: &str) -> Cow<'_, [u8]> {
if !name.chars().any(|c| raw_byte(c).is_some()) {
return Cow::Borrowed(name.as_bytes());
}
let mut out = Vec::with_capacity(name.len());
let mut buf = [0u8; 4];
for c in name.chars() {
match raw_byte(c) {
Some(b) => out.push(b),
None => out.extend_from_slice(c.encode_utf8(&mut buf).as_bytes()),
}
}
Cow::Owned(out)
}
pub fn raw_byte(c: char) -> Option<u8> {
let v = u32::from(c);
(RAW_BASE..=RAW_BASE + 0xFF)
.contains(&v)
.then(|| (v - RAW_BASE) as u8)
}
fn placeholder(b: u8) -> char {
char::from_u32(RAW_BASE + u32::from(b)).expect("Private Use Area-B is valid")
}
pub fn display(name: &str) -> Cow<'_, str> {
if !name.chars().any(|c| raw_byte(c).is_some()) {
return Cow::Borrowed(name);
}
let mut out = String::with_capacity(name.len());
for c in name.chars() {
match raw_byte(c) {
Some(b) => out.push_str(&format!("\\x{b:02X}")),
None => out.push(c),
}
}
Cow::Owned(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn utf8_names_pass_through_untouched() {
assert_eq!(decode(b"NeXTSTEP_3.3"), "NeXTSTEP_3.3");
assert_eq!(decode("caf\u{e9}".as_bytes()), "caf\u{e9}");
assert!(matches!(encode("plain"), Cow::Borrowed(_)));
}
#[test]
fn nextstep_and_euc_bytes_round_trip() {
for raw in [
&b"Steroidgrundger\xf6st.lookMol"[..],
&b"Eject\xbcE.tiff"[..],
&b"01OmniWeb \xa4\xd8\xa4\xe8\xa4\xa6\xa4\xb3\xa4\xbd.wdesk"[..],
&b"\xff\xfe"[..],
] {
let name = decode(raw);
assert_eq!(&*encode(&name), raw, "{name:?}");
}
}
}