use std::ffi::{OsStr, OsString};
#[must_use]
pub fn encode(value: &OsStr) -> Vec<u8> {
#[cfg(unix)]
{
std::os::unix::ffi::OsStrExt::as_bytes(value).to_vec()
}
#[cfg(windows)]
{
std::os::windows::ffi::OsStrExt::encode_wide(value)
.flat_map(u16::to_le_bytes)
.collect()
}
}
#[cfg_attr(
unix,
expect(clippy::unnecessary_wraps, reason = "fallible on Windows")
)]
pub fn decode(bytes: &[u8]) -> Result<OsString, String> {
#[cfg(unix)]
{
Ok(std::os::unix::ffi::OsStringExt::from_vec(bytes.to_vec()))
}
#[cfg(windows)]
{
if bytes.len() % 2 != 0 {
return Err(format!("{} bytes cannot be UTF-16 code units", bytes.len()));
}
let units: Vec<u16> = bytes
.chunks_exact(2)
.map(|pair| u16::from_le_bytes([pair[0], pair[1]]))
.collect();
Ok(std::os::windows::ffi::OsStringExt::from_wide(&units))
}
}
#[cfg(test)]
mod tests {
use std::ffi::OsString;
use super::{decode, encode};
#[test]
fn a_plain_path_round_trips() {
let value = OsString::from("/Users/one/.cargo/registry/src/serde-1.0.0/src/lib.rs");
assert_eq!(decode(&encode(&value)).expect("decode"), value);
}
#[cfg(unix)]
#[test]
fn a_non_unicode_path_round_trips() {
let value: OsString = std::os::unix::ffi::OsStringExt::from_vec(vec![b'/', 0xff, b'a']);
assert_eq!(decode(&encode(&value)).expect("decode"), value);
}
}