use std::ffi::OsString;
use std::path::PathBuf;
pub fn os_from_bytes(bytes: &[u8]) -> OsString {
#[cfg(unix)]
{
use std::os::unix::ffi::OsStringExt;
OsString::from_vec(bytes.to_vec())
}
#[cfg(not(unix))]
{
OsString::from(String::from_utf8_lossy(bytes).into_owned())
}
}
pub fn path_from_bytes(bytes: &[u8]) -> PathBuf {
PathBuf::from(os_from_bytes(bytes))
}
pub fn unquote_c_style_path(s: &str) -> Vec<u8> {
let bytes = s.as_bytes();
if bytes.first() != Some(&b'"') {
return bytes.to_vec();
}
let mut out: Vec<u8> = Vec::with_capacity(bytes.len());
let mut i = 1;
while i < bytes.len() {
match bytes[i] {
b'"' => break,
b'\\' if i + 1 < bytes.len() => {
i += 1;
match bytes[i] {
b'a' => out.push(0x07),
b'b' => out.push(0x08),
b't' => out.push(b'\t'),
b'n' => out.push(b'\n'),
b'v' => out.push(0x0b),
b'f' => out.push(0x0c),
b'r' => out.push(b'\r'),
b'"' => out.push(b'"'),
b'\\' => out.push(b'\\'),
d @ b'0'..=b'7' => {
let mut val = u32::from(d - b'0');
let mut taken = 0;
while taken < 2
&& i + 1 < bytes.len()
&& (b'0'..=b'7').contains(&bytes[i + 1])
{
i += 1;
val = val * 8 + u32::from(bytes[i] - b'0');
taken += 1;
}
out.push(val as u8);
}
other => out.push(other),
}
i += 1;
}
b => {
out.push(b);
i += 1;
}
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ascii_and_utf8_round_trip_on_every_platform() {
assert_eq!(path_from_bytes(b"src/lib.rs"), PathBuf::from("src/lib.rs"));
assert_eq!(
path_from_bytes("café.txt".as_bytes()),
PathBuf::from("café.txt")
);
}
#[test]
fn unquotes_c_style_paths_and_passes_through_plain_paths() {
assert_eq!(unquote_c_style_path("b/plain.txt"), b"b/plain.txt");
assert_eq!(
unquote_c_style_path("\"b/caf\\303\\251.txt\""),
"b/café.txt".as_bytes()
);
assert_eq!(unquote_c_style_path("\"a\\tb\""), b"a\tb");
assert_eq!(unquote_c_style_path("\"a\\\\b\""), b"a\\b");
assert_eq!(unquote_c_style_path("\"a\\\"b\""), b"a\"b");
assert_eq!(unquote_c_style_path("\"\\377.bin\""), b"\xff.bin");
}
#[cfg(unix)]
#[test]
fn non_utf8_bytes_survive_on_unix() {
use std::os::unix::ffi::OsStrExt;
let raw = b"caf\xff.txt"; let os = os_from_bytes(raw);
assert_eq!(os.as_bytes(), raw, "the exact bytes must survive");
}
}