use super::*;
pub trait FileSystemIsolated : FileSystemProvider
{
type HostFileSystem : FileSystem;
fn isolated_root(&mut self) -> PathBuf;
fn non_isolated_file_system() -> Self::HostFileSystem;
fn validate_path<P: AsRef<Path>>(&mut self, path: P) -> IoResult<PathBuf> {
let path = path.as_ref();
let mut fs = Self::non_isolated_file_system();
let mut full_path = fs.canonicalize(&self.isolated_root())?;
full_path.reserve(path.as_os_str().len() + 1);
let mut depth = 0;
for (idx, c) in path.components().enumerate()
{
match c
{
std::path::Component::Prefix(prefix) =>
{
return Err(IoError::new_with_path(IoErrorKind::InvalidFilename, format!("found prefix {:?} in the middle of the path", prefix), path));
},
std::path::Component::RootDir =>
{
if idx != 0 { return Err(IoError::new_with_path(IoErrorKind::InvalidFilename, format!("found root dir in the middle of the path"), path))}
},
std::path::Component::CurDir => { continue; } std::path::Component::ParentDir =>
{
if depth == 0 {
return Err(IoError::new_with_path(
IoErrorKind::InvalidFilename,
"Path attempts to escape root with '..'".to_string(),
path
));
}
depth -= 1;
full_path.pop();
},
std::path::Component::Normal(os_str) =>
{
depth += 1;
full_path.push(os_str);
},
}
}
Ok(full_path)
}
}
impl<T> FileSystemDynRead for T where T: FileSystemIsolated
{
fn dyn_read_bytes_at(&mut self, path: &Path) -> IoResult<Cow<'static, [u8]>> {
Self::non_isolated_file_system().dyn_read_bytes_at(&self.validate_path(path)?)
}
fn dyn_read_dir_at(&mut self, path: &Path) -> IoResult<Vec<IoResult<PathBuf>>> {
Self::non_isolated_file_system().dyn_read_dir_at(&self.validate_path(path)?)
}
fn dyn_read_link_at(&mut self, path: &Path) -> IoResult<PathBuf> {
Self::non_isolated_file_system().dyn_read_link_at(&self.validate_path(path)?)
}
fn dyn_file_type_at(&mut self, path: &Path) -> IoResult<FileType> {
Self::non_isolated_file_system().dyn_file_type_at(&self.validate_path(path)?)
}
fn dyn_resolve_paths(&mut self, path: &Path) -> IoResult<Vec<PathBuf>>
{
let root = self.isolated_root();
let mut resolved = Self::non_isolated_file_system().dyn_resolve_paths(&self.validate_path(path)?)?;
resolved.retain_mut(|p|
{
match strip_prefix(&p, &root)
{
Ok(stripped) => { *p = stripped; true },
Err(_) => false,
}
});
Ok(resolved)
}
fn dyn_canonicalize(&mut self, path: &Path) -> IoResult<PathBuf>
{
let path = self.validate_path(path)?;
let root = self.isolated_root();
strip_prefix(&path, &root)
}
}
fn strip_prefix(path: &Path, root: &Path) -> IoResult<PathBuf>
{
match path.strip_prefix(&root)
{
Ok(p) => Ok(p.to_path_buf()),
Err(_err) => Err(IoError::new(
IoErrorKind::InvalidInput,
format!("Path '{:?}' is not within the isolated root '{:?}'", path, root))),
}
}
impl<T> FileSystemDynWrite for T where T: FileSystemIsolated
{
fn dyn_write_bytes_at(&mut self, path: &Path, value: &[u8]) -> IoResult {
Self::non_isolated_file_system().dyn_write_bytes_at(&self.validate_path(path)?, value)
}
fn dyn_create_dir(&mut self, path: &Path) -> IoResult {
Self::non_isolated_file_system().dyn_create_dir(&self.validate_path(path)?)
}
fn dyn_remove_at(&mut self, path: &Path) -> IoResult {
Self::non_isolated_file_system().dyn_remove_at(&self.validate_path(path)?)
}
fn dyn_rename_at(&mut self, from: &Path, to: &Path) -> IoResult {
Self::non_isolated_file_system().dyn_rename_at(&self.validate_path(from)?, &self.validate_path(to)?)
}
}
pub trait FileSystemProvider
{
type FileSystem: FileSystem;
fn file_system() -> Self::FileSystem;
}
#[doc(hidden)]
pub trait FileSystemDynRead
{
#[doc(hidden)]
#[must_use]
fn dyn_try_exist_at(&mut self, path: &Path) -> IoResult<bool>
{
self.dyn_file_type_at(path)?;
Ok(true)
}
#[doc(hidden)]
#[must_use]
fn dyn_read_bytes_at(&mut self, path: &Path) -> IoResult<Cow<'static, [u8]>>;
#[doc(hidden)]
#[must_use]
fn dyn_read_dir_at(&mut self, path: &Path) -> IoResult<Vec<IoResult<PathBuf>>>;
#[doc(hidden)]
#[must_use]
fn dyn_read_link_at(&mut self, path: &Path) -> IoResult<PathBuf>;
#[doc(hidden)]
#[must_use]
fn dyn_file_type_at(&mut self, path: &Path) -> IoResult<FileType>;
#[doc(hidden)]
#[must_use]
fn dyn_resolve_paths(&mut self, path: &Path) -> IoResult<Vec<PathBuf>>;
#[doc(hidden)]
#[must_use]
fn dyn_resolve_path(&mut self, path: &Path) -> IoResult<PathBuf>
{
let mut paths = self.dyn_resolve_paths(path)?;
if let Some(p) = paths.pop()
{
if !paths.is_empty()
{
return Err(IoError::new_with_path(IoErrorKind::InvalidInput, "Can't be resolved to multiple path", path));
}
return Ok(p);
}
Ok(path.to_owned())
}
#[doc(hidden)]
#[must_use]
fn dyn_canonicalize(&mut self, path: &Path) -> IoResult<PathBuf>;
}
#[doc(hidden)]
pub trait FileSystemDynWrite: FileSystemDynRead
{
#[doc(hidden)]
#[must_use]
fn dyn_write_bytes_at(&mut self, path: &Path, value: &[u8]) -> IoResult;
#[doc(hidden)]
#[must_use]
fn dyn_create_dir(&mut self, path: &Path) -> IoResult;
#[doc(hidden)]
#[must_use]
fn dyn_remove_at(&mut self, path: &Path) -> IoResult;
#[doc(hidden)]
#[must_use]
fn dyn_rename_at(&mut self, from: &Path, to: &Path) -> IoResult;
}
pub trait FileSystemRead: FileSystemDynRead
{
#[must_use]
fn exist<P: AsRef<Path>>(&mut self, path: P) -> bool { self.try_exist(path).is_ok_and(|exist| exist) }
#[must_use]
fn try_exist<P: AsRef<Path>>(&mut self, path: P) -> IoResult<bool> { let path = self.resolve_path(path)?; self.dyn_try_exist_at(&path) }
#[must_use]
fn exist_at<P: AsRef<Path>>(&mut self, path: P) -> bool { self.try_exist_at(path).is_ok_and(|exist| exist) }
#[must_use]
fn try_exist_at<P: AsRef<Path>>(&mut self, path: P) -> IoResult<bool> { self.dyn_try_exist_at(path.as_ref()) }
#[must_use]
fn read_bytes_at<P: AsRef<Path>>(&mut self, path: P) -> IoResult<Cow<'static, [u8]>> { self.dyn_read_bytes_at(path.as_ref()) }
#[must_use]
fn read_dir_at<P: AsRef<Path>>(&mut self, path: P) -> IoResult<Vec<IoResult<PathBuf>>> { self.dyn_read_dir_at(path.as_ref()) }
#[must_use]
fn read_link_at<P: AsRef<Path>>(&mut self, path: P) -> IoResult<Vec<IoResult<PathBuf>>> { self.dyn_read_dir_at(path.as_ref()) }
#[must_use]
fn read_bytes<P: AsRef<Path>>(&mut self, path: P) -> IoResult<Cow<'static, [u8]>> { let path = self.resolve_path(path)?; self.read_bytes_at(path) }
#[must_use]
fn read_dir<P: AsRef<Path>>(&mut self, path: P) -> IoResult<Vec<IoResult<PathBuf>>> { let path = self.resolve_path(path)?; self.read_dir_at(path) }
#[must_use]
fn read_link<P: AsRef<Path>>(&mut self, path: P) -> IoResult<Vec<IoResult<PathBuf>>> { let path = self.resolve_path(path)?; self.read_link_at(path) }
#[must_use]
fn file_type_at<P: AsRef<Path>>(&mut self, path: P) -> IoResult<FileType> { self.dyn_file_type_at(path.as_ref()) }
#[must_use]
fn file_type<P: AsRef<Path>>(&mut self, path: P) -> IoResult<FileType> { let path = self.resolve_path(path)?; self.file_type_at(path) }
#[must_use]
fn resolve_paths<P: AsRef<Path>>(&mut self, path: P) -> IoResult<Vec<PathBuf>> { self.dyn_resolve_paths(path.as_ref()) }
#[must_use]
fn resolve_path<P: AsRef<Path>>(&mut self, path: P) -> IoResult<PathBuf> { self.dyn_resolve_path(path.as_ref()) }
#[must_use]
fn canonicalize<P: AsRef<Path>>(&mut self, path: P) -> IoResult<PathBuf> { self.dyn_canonicalize(path.as_ref()) }
}
impl<T> FileSystemRead for T where T: FileSystemDynRead {}
impl FileSystemRead for dyn FileSystemDynRead {}
impl FileSystemRead for dyn FileSystem {}
pub trait FileSystem: FileSystemDynWrite + FileSystemDynRead {}
impl<F> FileSystem for F where F: FileSystemDynWrite + FileSystemDynRead {}
pub trait FileSystemWrite: FileSystemRead + FileSystemDynWrite
{
#[must_use]
fn write_bytes_at<P: AsRef<Path>>(&mut self, path: P, value: &[u8]) -> IoResult { self.dyn_write_bytes_at(path.as_ref(), value) }
#[must_use]
fn write_bytes<P: AsRef<Path>>(&mut self, path: P, value: &[u8]) -> IoResult<PathBuf>
{
let path = self.resolve_path(path)?;
self.write_bytes_at(&path, value)?;
Ok(path)
}
#[must_use]
fn create_dir<P: AsRef<Path>>(&mut self, path: P) -> IoResult { self.dyn_create_dir(path.as_ref()) }
#[must_use]
fn remove_at<P: AsRef<Path>>(&mut self, path: P) -> IoResult { self.dyn_remove_at(path.as_ref()) }
#[must_use]
fn remove<P: AsRef<Path>>(&mut self, path: P) -> IoResult<PathBuf>
{
let path = self.resolve_path(path)?;
self.remove_at(&path)?;
Ok(path)
}
#[must_use]
fn rename_at<P: AsRef<Path>, Q: AsRef<Path>>(&mut self, from: P, to: Q) -> IoResult { self.dyn_rename_at(from.as_ref(), to.as_ref()) }
#[must_use]
fn rename<P: AsRef<Path>, Q: AsRef<Path>>(&mut self, from: P, to: Q) -> IoResult<PathBuf>
{
let from = self.resolve_path(from)?;
let to = self.resolve_path(to)?;
self.rename_at(from, &to)?;
Ok(to)
}
}
impl<T> FileSystemWrite for T where T: FileSystem {}
impl FileSystemWrite for dyn FileSystem {}