use std::convert::TryInto;
use std::ffi::OsString;
use std::fmt::Write;
use std::path::PathBuf;
#[derive(Clone, PartialEq, Eq)]
pub struct ByteString(Vec<u8>);
impl std::fmt::Debug for ByteString {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
let escaped = self.as_escaped_string();
write!(fmt, "\"{}\"", escaped.escape_debug())
}
}
impl std::fmt::Display for ByteString {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
let escaped = self.as_escaped_string();
write!(fmt, "\"{}\"", escaped.escape_default())
}
}
impl std::ops::Deref for ByteString {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.0
}
}
impl std::ops::DerefMut for ByteString {
fn deref_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl ByteString {
pub fn as_bytes(&self) -> &[u8] {
self.0.as_slice()
}
pub fn as_escaped_string(&self) -> String {
let mut input = self.0.as_slice();
let mut output = String::new();
loop {
match ::std::str::from_utf8(input) {
Ok(valid) => {
output.push_str(valid);
break;
}
Err(error) => {
let (valid, after_valid) = input.split_at(error.valid_up_to());
unsafe {
output.push_str(::std::str::from_utf8_unchecked(valid))
}
if let Some(invalid_sequence_length) = error.error_len() {
for b in &after_valid[..invalid_sequence_length] {
write!(output, "\\x{:x}", b).unwrap();
}
input = &after_valid[invalid_sequence_length..];
} else {
break;
}
}
}
}
output
}
}
impl From<Vec<u8>> for ByteString {
fn from(vec: Vec<u8>) -> Self {
Self(vec)
}
}
impl From<String> for ByteString {
fn from(s: String) -> Self {
Self(s.into_bytes())
}
}
impl From<&str> for ByteString {
fn from(s: &str) -> Self {
Self(s.as_bytes().to_vec())
}
}
impl TryInto<String> for ByteString {
type Error = std::string::FromUtf8Error;
fn try_into(self) -> Result<String, Self::Error> {
String::from_utf8(self.0)
}
}
impl TryInto<OsString> for ByteString {
type Error = std::string::FromUtf8Error;
#[cfg(unix)]
fn try_into(self) -> Result<OsString, Self::Error> {
Ok(std::os::unix::ffi::OsStringExt::from_vec(self.0))
}
#[cfg(windows)]
fn try_into(self) -> Result<OsString, Self::Error> {
let s = String::from_utf8(self.0)?;
Ok(s.into())
}
}
impl TryInto<PathBuf> for ByteString {
type Error = std::string::FromUtf8Error;
fn try_into(self) -> Result<PathBuf, Self::Error> {
let os: OsString = self.try_into()?;
Ok(os.into())
}
}
impl TryInto<ByteString> for OsString {
type Error = &'static str;
#[cfg(unix)]
fn try_into(self) -> Result<ByteString, Self::Error> {
Ok(ByteString(std::os::unix::ffi::OsStringExt::into_vec(self)))
}
#[cfg(windows)]
fn try_into(self) -> Result<ByteString, Self::Error> {
let s = self
.into_string()
.map_err(|_| "OsString is not representible as UTF-8")?;
Ok(ByteString(s.into_bytes()))
}
}
impl TryInto<ByteString> for PathBuf {
type Error = &'static str;
fn try_into(self) -> Result<ByteString, Self::Error> {
Ok(self.into_os_string().try_into()?)
}
}