use std::ffi::CString;
use std::io::Error;
use std::io::Read;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use crate::io::*;
use crate::Format;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct CpioPath(CString);
impl CpioPath {
pub(crate) fn write<W: Write>(&self, mut writer: W, format: Format) -> Result<(), Error> {
let bytes = self.0.as_bytes_with_nul();
writer.write_all(bytes)?;
write_path_padding(writer, bytes.len(), format)?;
Ok(())
}
pub(crate) fn read<R: Read>(mut reader: R, len: usize, format: Format) -> Result<Self, Error> {
let mut buf = vec![0_u8; len];
reader.read_exact(&mut buf[..])?;
read_path_padding(reader, len, format)?;
Self::from_vec_with_nul(buf)
}
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
pub fn as_bytes_with_nul(&self) -> &[u8] {
self.0.as_bytes_with_nul()
}
pub fn to_path(&self) -> Result<PathBuf, Error> {
PathBuf::try_from(self.clone())
}
pub fn into_bytes_with_nul(self) -> Vec<u8> {
self.0.into_bytes_with_nul()
}
pub fn into_bytes(self) -> Vec<u8> {
self.0.into_bytes()
}
pub fn from_vec_with_nul(bytes: Vec<u8>) -> Result<Self, Error> {
let c_string = CString::from_vec_with_nul(bytes).map_err(Error::other)?;
Ok(Self(c_string))
}
}
impl std::fmt::Display for CpioPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0.to_str() {
Ok(s) => write!(f, "{s}"),
Err(_) => write!(f, "{}", self.0.to_string_lossy()),
}
}
}
impl TryFrom<&Path> for CpioPath {
type Error = <Self as TryFrom<PathBuf>>::Error;
fn try_from(other: &Path) -> Result<Self, Self::Error> {
other.to_path_buf().try_into()
}
}
impl TryFrom<&str> for CpioPath {
type Error = <Self as TryFrom<PathBuf>>::Error;
fn try_from(other: &str) -> Result<Self, Self::Error> {
Path::new(other).to_path_buf().try_into()
}
}
#[cfg(unix)]
impl TryFrom<PathBuf> for CpioPath {
type Error = std::io::Error;
fn try_from(other: PathBuf) -> Result<Self, Self::Error> {
use std::os::unix::ffi::OsStringExt;
let mut bytes = other.into_os_string().into_vec();
bytes.push(0_u8);
Self::from_vec_with_nul(bytes)
}
}
#[cfg(not(unix))]
impl TryFrom<PathBuf> for CpioPath {
type Error = std::io::Error;
fn try_from(other: PathBuf) -> Result<Self, Self::Error> {
let mut bytes = other
.to_str()
.ok_or_else(|| Error::other("Non-UTF-8 path"))?
.as_bytes()
.to_vec();
#[cfg(windows)]
for b in bytes.iter_mut() {
if *b == b'\\' {
*b = b'/';
}
}
bytes.push(0_u8);
Self::from_vec_with_nul(bytes)
}
}
#[cfg(unix)]
impl TryFrom<CpioPath> for PathBuf {
type Error = std::io::Error;
fn try_from(other: CpioPath) -> Result<Self, Self::Error> {
use std::os::unix::ffi::OsStringExt;
Ok(PathBuf::from(std::ffi::OsString::from_vec(
other.0.into_bytes(),
)))
}
}
#[cfg(not(unix))]
impl TryFrom<CpioPath> for PathBuf {
type Error = std::io::Error;
fn try_from(other: CpioPath) -> Result<Self, Self::Error> {
let string = other
.0
.into_string()
.map_err(|_| Error::other("Non-UTF-8 path"))?;
Ok(PathBuf::from(string))
}
}