use std::path::{Path, PathBuf};
use std::ffi::{OsString, OsStr};
#[derive(Default, Debug)]
pub struct DirBuf {
buf: PathBuf,
initial_len: usize,
}
impl DirBuf {
#[must_use]
pub fn new(path: impl Into<PathBuf>) -> Self {
let buf = path.into();
DirBuf {
initial_len: buf.as_os_str().len(),
buf,
}
}
pub(crate) unsafe fn reset(&mut self) -> &mut PathBuf {
let ilen = self.initial_len;
let mut entry = self.as_entry();
entry.initial_len = ilen;
unsafe { entry.reset() };
&mut self.buf
}
pub(crate) fn as_entry(&mut self) -> DirBufEntry<'_> {
DirBufEntry{
initial_len: self.buf.as_os_str().len(),
buf: &mut self.buf,
}
}
pub(crate) unsafe fn push(&mut self, path: &Path) {
self.as_entry().push(path);
}
}
#[derive(Debug)]
pub struct DirBufEntry<'buf> {
buf: &'buf mut PathBuf,
initial_len: usize,
}
impl DirBufEntry<'_> {
pub(crate) unsafe fn reset(&mut self) -> &mut PathBuf {
let os_path = self.buf.as_mut_os_string();
#[cfg_attr(feature = "nightly-safe-impl", expect(unused_unsafe))]
unsafe { truncate_os_string(os_path, self.initial_len) };
self.buf
}
pub(crate) fn as_entry(&mut self) -> DirBufEntry<'_> {
DirBufEntry{
initial_len: self.buf.as_os_str().len(),
buf: self.buf,
}
}
pub(crate) unsafe fn push(&mut self, path: &Path) {
unsafe {self.reset()}.push(path);
}
}
impl AsRef<Path> for DirBuf {
fn as_ref(&self) -> &Path {
unsafe { path_prefix_bytes(&self.buf, self.initial_len) }
}
}
impl AsRef<Path> for DirBufEntry<'_> {
fn as_ref(&self) -> &Path {
unsafe { path_prefix_bytes(self.buf, self.initial_len) }
}
}
unsafe fn path_prefix_bytes(path: &Path, len: usize) -> &Path {
let slice = &path.as_os_str().as_encoded_bytes()[..len];
Path::new(unsafe {OsStr::from_encoded_bytes_unchecked(slice)})
}
#[cfg(not(feature = "nightly-safe-impl"))]
unsafe fn truncate_os_string(s: &mut OsString, l: usize) {
let mut os_string_tmp = OsString::new();
std::mem::swap(s, &mut os_string_tmp);
let mut vec = os_string_tmp.into_encoded_bytes();
vec.truncate(l);
os_string_tmp = unsafe { OsString::from_encoded_bytes_unchecked(vec) };
std::mem::swap(s, &mut os_string_tmp);
}
#[cfg(feature = "nightly-safe-impl")]
fn truncate_os_string(s: &mut OsString, l: usize) {
s.truncate(l);
}