use std::error::Error as StdError;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::io::Read;
use std::io::Seek;
use std::io::Write;
use std::path;
use std::path::Path;
use std::path::PathBuf;
use std::pin::Pin;
use std::result::Result as StdResult;
use crate::error::Error;
use crate::error::Result;
use crate::error::VfsResult;
use crate::error::WrapIoError as _;
use crate::prelude::*;
pub trait VfsCore {
type Path: PathType + ?Sized;
}
pub trait Vfs<'vfs>: VfsCore + 'vfs {
type DirWalk<'a>: DirWalker<'a, P = Self::Path>
where
'vfs: 'a,
Self: 'a;
type RFile: Read + 'vfs;
fn open_read(self: Pin<&Self>, path: &Self::Path) -> VfsResult<Self::RFile, Self>;
fn read(self: Pin<&Self>, path: &Self::Path) -> VfsResult<Vec<u8>, Self>;
fn read_string(self: Pin<&Self>, path: &Self::Path) -> VfsResult<String, Self> {
self.read(path).and_then(|bytes| {
String::from_utf8(bytes).map_err(|e| Error::Parse(path.owned(), Box::new(e)))
})
}
fn exists(self: Pin<&Self>, path: &Self::Path) -> VfsResult<bool, Self>;
fn is_dir(self: Pin<&Self>, path: &Self::Path) -> VfsResult<bool, Self>;
fn walk_dir<'b>(self: Pin<&'b Self>, path: &Self::Path) -> VfsResult<Self::DirWalk<'b>, Self>
where
'vfs: 'b;
}
pub trait VfsExt<'vfs>: Vfs<'vfs> {
fn read_typed_pinned<T: ReadFrom<'vfs, Self>>(
self: Pin<&'vfs Self>,
path: impl AsRef<Self::Path>,
) -> VfsResult<T, Self> {
T::read_from(path.as_ref(), self)
}
fn read_typed<T: ReadFrom<'vfs, Self>>(
&'vfs self,
path: impl AsRef<Self::Path>,
) -> VfsResult<T, Self>
where
Self: Unpin,
{
Pin::new(self).read_typed_pinned(path)
}
}
impl<'vfs, V: Vfs<'vfs> + ?Sized> VfsExt<'vfs> for V {}
pub trait VfsWithSeekRead<'vfs>: Vfs<'vfs>
where
Self::RFile: Seek,
{
}
impl<'vfs, T: Vfs<'vfs>> VfsWithSeekRead<'vfs> for T where T::RFile: Seek {}
pub trait WriteSupportingVfs<'vfs>: Vfs<'vfs> {
type WFile: Write + 'vfs;
fn open_write(self: Pin<&Self>, path: &Self::Path) -> VfsResult<Self::WFile, Self>;
fn write(self: Pin<&Self>, path: &Self::Path, data: &[u8]) -> VfsResult<(), Self> {
self.open_write(path)?
.write_all(data)
.wrap_io_error_with(path)
}
fn remove_dir_all(self: Pin<&Self>, path: &Self::Path) -> VfsResult<(), Self>;
fn create_dir(self: Pin<&Self>, path: &Self::Path) -> VfsResult<(), Self>;
fn create_dir_all(self: Pin<&Self>, path: &Self::Path) -> VfsResult<(), Self>;
fn create_parent_dir(self: Pin<&Self>, path: &Self::Path) -> VfsResult<(), Self> {
if let Some(parent) = path.parent()
&& !self.exists(parent)?
{
self.create_dir_all(parent)?;
}
Ok(())
}
}
pub trait WriteSupportingVfsExt<'vfs>: WriteSupportingVfs<'vfs> {
fn write_typed_pinned<T: WriteTo<'vfs, Self>>(
self: Pin<&'vfs Self>,
path: impl AsRef<Self::Path>,
value: &T,
) -> VfsResult<(), Self> {
value.write_to(path.as_ref(), self)
}
fn write_typed<T: WriteTo<'vfs, Self>>(
&'vfs self,
path: impl AsRef<Self::Path>,
value: &T,
) -> VfsResult<(), Self>
where
Self: Unpin,
{
Pin::new(self).write_typed_pinned(path, value)
}
}
impl<'vfs, Vfs: WriteSupportingVfs<'vfs> + ?Sized> WriteSupportingVfsExt<'vfs> for Vfs {}
pub trait VfsWithSeekWrite<'vfs>: WriteSupportingVfs<'vfs>
where
Self::WFile: Seek,
{
}
impl<'vfs, T: WriteSupportingVfs<'vfs>> VfsWithSeekWrite<'vfs> for T where T::WFile: Seek {}
pub trait PathType: PartialEq + Send + Sync {
type OwnedPath: OwnedPathType<RefType = Self>;
type PathSegmentRef: ToOwned<Owned = Self::PathSegmentOwned> + PartialEq + ?Sized;
type PathSegmentOwned: Send + Sync + Clone + Eq + AsRef<Self::PathSegmentRef>;
fn parent(&self) -> Option<&Self>;
fn join(&self, new_fragment: impl AsRef<Self>) -> Self::OwnedPath;
fn join_segment(&self, new_fragment: impl AsRef<Self::PathSegmentRef>) -> Self::OwnedPath;
fn join_segment_str(&self, new_fragment: &str) -> Self::OwnedPath;
type StripPrefixError: StdError + Send + Sync + 'static;
fn strip_prefix(&self, base: &Self) -> StdResult<&Self, Self::StripPrefixError>;
fn owned(&self) -> Self::OwnedPath;
}
impl PathType for Path {
type OwnedPath = PathBuf;
type PathSegmentRef = OsStr;
type PathSegmentOwned = OsString;
fn parent(&self) -> Option<&Self> {
self.parent()
}
fn join(&self, new_fragment: impl AsRef<Self>) -> Self::OwnedPath {
Self::join(self, new_fragment.as_ref())
}
fn join_segment(&self, new_fragment: impl AsRef<Self::PathSegmentRef>) -> Self::OwnedPath {
Self::join(self, new_fragment.as_ref())
}
fn join_segment_str(&self, new_fragment: &str) -> Self::OwnedPath {
Self::join(self, new_fragment)
}
type StripPrefixError = path::StripPrefixError;
fn strip_prefix(&self, base: &Self) -> StdResult<&Self, Self::StripPrefixError> {
self.strip_prefix(base)
}
fn owned(&self) -> Self::OwnedPath {
self.to_path_buf()
}
}
pub trait OwnedPathType: Clone + AsRef<Self::RefType> + PartialEq + Send + Sync {
type RefType: PathType<OwnedPath = Self> + ?Sized;
fn parent(&self) -> Option<&Self::RefType> {
self.as_ref().parent()
}
fn insert_in_front(&mut self, new_fragment: &<Self::RefType as PathType>::PathSegmentRef);
fn push_segment_str(&mut self, new_fragment: &str);
}
impl OwnedPathType for PathBuf {
type RefType = Path;
fn parent(&self) -> Option<&Self::RefType> {
self.as_path().parent()
}
fn insert_in_front(&mut self, new_fragment: &<Self::RefType as PathType>::PathSegmentRef) {
let mut new_path = PathBuf::from(new_fragment);
new_path.push(&self);
*self = new_path;
}
fn push_segment_str(&mut self, new_fragment: &str) {
self.push(new_fragment);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "assert_eq", derive(assert_eq::AssertEq))]
pub enum DirEntryKind {
File,
Directory,
}
impl DirEntryKind {
pub fn is_file(self) -> bool {
matches!(self, DirEntryKind::File)
}
pub fn is_dir(self) -> bool {
matches!(self, DirEntryKind::Directory)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "assert_eq", derive(assert_eq::AssertEq))]
pub struct DirEntryInfo<P: PathType + ?Sized> {
pub name: P::PathSegmentOwned,
pub path: P::OwnedPath,
pub kind: DirEntryKind,
}
pub trait DirWalker<'vfs>: 'vfs {
type P: PathType + ?Sized;
fn next(&mut self) -> Option<Result<DirEntryInfo<Self::P>, <Self::P as PathType>::OwnedPath>>;
}