use std::fmt::Debug;
use vfs::FileSystem;
use vfs::VfsError;
use vfs::VfsMetadata;
use vfs::error::VfsErrorKind;
use crate::Metadata;
use crate::VfsEntry;
use crate::VfsTree;
pub trait FileOpener<F>: Debug + Send + Sync + 'static {
fn open(&self, meta: &F) -> vfs::VfsResult<Box<dyn vfs::SeekAndRead + Send>>;
}
impl<F, Func> FileOpener<F> for Func
where
Func:
Fn(&F) -> vfs::VfsResult<Box<dyn vfs::SeekAndRead + Send>> + Debug + Send + Sync + 'static,
{
fn open(&self, meta: &F) -> vfs::VfsResult<Box<dyn vfs::SeekAndRead + Send>> {
(self)(meta)
}
}
#[derive(Debug, Clone)]
pub struct ReadOnlyVfs<F, O> {
tree: VfsTree<F>,
opener: O,
}
impl<F, O> ReadOnlyVfs<F, O> {
pub fn new(tree: VfsTree<F>, opener: O) -> Self {
Self { tree, opener }
}
pub fn tree(&self) -> &VfsTree<F> {
&self.tree
}
pub fn opener(&self) -> &O {
&self.opener
}
}
impl<F: Metadata + Debug + Send + Sync + 'static, O: FileOpener<F>> FileSystem
for ReadOnlyVfs<F, O>
{
fn read_dir(&self, path: &str) -> vfs::VfsResult<Box<dyn Iterator<Item = String> + Send>> {
self.tree.vfs_read_dir(path)
}
fn open_file(&self, path: &str) -> vfs::VfsResult<Box<dyn vfs::SeekAndRead + Send>> {
let entry = self.tree.vfs_lookup(path)?;
let VfsEntry::File(meta) = entry else {
return Err(VfsErrorKind::Other("not a file".into()).into());
};
self.opener.open(meta)
}
fn metadata(&self, path: &str) -> vfs::VfsResult<VfsMetadata> {
self.tree.vfs_metadata(path)
}
fn exists(&self, path: &str) -> vfs::VfsResult<bool> {
self.tree.vfs_exists(path)
}
crate::read_only_fs_stubs!();
}
impl<F> VfsTree<F> {
pub fn vfs_lookup(&self, path: &str) -> vfs::VfsResult<&VfsEntry<F>> {
self.lookup(path)
.ok_or_else(|| VfsError::from(VfsErrorKind::FileNotFound))
}
pub fn vfs_read_dir(
&self,
path: &str,
) -> vfs::VfsResult<Box<dyn Iterator<Item = String> + Send>> {
let entry = self.vfs_lookup(path)?;
match entry {
VfsEntry::Directory { children, .. } => Ok(Box::new(children.clone().into_iter())),
VfsEntry::File(_) => Err(VfsError::from(VfsErrorKind::Other(
"not a directory".into(),
))),
}
}
pub fn vfs_exists(&self, path: &str) -> vfs::VfsResult<bool> {
Ok(self.exists(path))
}
}
impl<F: Metadata> VfsTree<F> {
pub fn vfs_metadata(&self, path: &str) -> vfs::VfsResult<VfsMetadata> {
let entry = self.vfs_lookup(path)?;
let meta = match entry {
VfsEntry::Directory { meta, .. } => VfsMetadata {
file_type: vfs::VfsFileType::Directory,
len: meta.as_ref().map_or(0, |m| m.len()),
created: meta.as_ref().and_then(|m| m.created()),
modified: meta.as_ref().and_then(|m| m.modified()),
accessed: meta.as_ref().and_then(|m| m.accessed()),
},
VfsEntry::File(f) => VfsMetadata {
file_type: vfs::VfsFileType::File,
len: f.len(),
created: f.created(),
modified: f.modified(),
accessed: f.accessed(),
},
};
Ok(meta)
}
}
#[cfg(feature = "async-vfs")]
pub trait AsyncFileOpener<F>: Debug + Send + Sync + 'static {
fn open_async(
&self,
meta: &F,
) -> vfs::VfsResult<Box<dyn vfs::async_vfs::SeekAndRead + Send + Unpin>>;
}
#[cfg(feature = "async-vfs")]
impl<F, Func> AsyncFileOpener<F> for Func
where
Func: Fn(&F) -> vfs::VfsResult<Box<dyn vfs::async_vfs::SeekAndRead + Send + Unpin>>
+ Debug
+ Send
+ Sync
+ 'static,
{
fn open_async(
&self,
meta: &F,
) -> vfs::VfsResult<Box<dyn vfs::async_vfs::SeekAndRead + Send + Unpin>> {
(self)(meta)
}
}
#[cfg(feature = "async-vfs")]
const _: () = {
use async_trait::async_trait;
use vfs::async_vfs::AsyncFileSystem;
#[async_trait]
impl<F, O> AsyncFileSystem for ReadOnlyVfs<F, O>
where
F: Metadata + Debug + Send + Sync + 'static,
O: FileOpener<F> + AsyncFileOpener<F>,
{
async fn read_dir(
&self,
path: &str,
) -> vfs::VfsResult<Box<dyn Unpin + futures::Stream<Item = String> + Send>> {
self.tree.async_vfs_read_dir(path)
}
async fn open_file(
&self,
path: &str,
) -> vfs::VfsResult<Box<dyn vfs::async_vfs::SeekAndRead + Send + Unpin>> {
let entry = self.tree.vfs_lookup(path)?;
let VfsEntry::File(meta) = entry else {
return Err(vfs::error::VfsErrorKind::Other("not a file".into()).into());
};
self.opener.open_async(meta)
}
async fn metadata(&self, path: &str) -> vfs::VfsResult<VfsMetadata> {
self.tree.vfs_metadata(path)
}
async fn exists(&self, path: &str) -> vfs::VfsResult<bool> {
self.tree.vfs_exists(path)
}
async fn create_dir(&self, _path: &str) -> vfs::VfsResult<()> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
async fn create_file(
&self,
_path: &str,
) -> vfs::VfsResult<Box<dyn futures::io::AsyncWrite + Send + Unpin>> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
async fn append_file(
&self,
_path: &str,
) -> vfs::VfsResult<Box<dyn futures::io::AsyncWrite + Send + Unpin>> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
async fn remove_file(&self, _path: &str) -> vfs::VfsResult<()> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
async fn remove_dir(&self, _path: &str) -> vfs::VfsResult<()> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
async fn copy_file(&self, _src: &str, _dest: &str) -> vfs::VfsResult<()> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
async fn move_file(&self, _src: &str, _dest: &str) -> vfs::VfsResult<()> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
async fn move_dir(&self, _src: &str, _dest: &str) -> vfs::VfsResult<()> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
}
};
#[cfg(feature = "async-vfs")]
impl<F> VfsTree<F> {
pub fn async_vfs_read_dir(
&self,
path: &str,
) -> vfs::VfsResult<Box<dyn Unpin + futures::Stream<Item = String> + Send>> {
let entry = self.vfs_lookup(path)?;
match entry {
VfsEntry::Directory { children, .. } => {
Ok(Box::new(futures::stream::iter(children.clone())))
}
VfsEntry::File(_) => Err(VfsError::from(VfsErrorKind::Other(
"not a directory".into(),
))),
}
}
}
#[macro_export]
macro_rules! read_only_fs_stubs {
() => {
fn create_dir(&self, _path: &str) -> vfs::VfsResult<()> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
fn create_file(&self, _path: &str) -> vfs::VfsResult<Box<dyn vfs::SeekAndWrite + Send>> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
fn append_file(&self, _path: &str) -> vfs::VfsResult<Box<dyn vfs::SeekAndWrite + Send>> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
fn remove_file(&self, _path: &str) -> vfs::VfsResult<()> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
fn remove_dir(&self, _path: &str) -> vfs::VfsResult<()> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
fn set_creation_time(
&self,
_path: &str,
_time: std::time::SystemTime,
) -> vfs::VfsResult<()> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
fn set_modification_time(
&self,
_path: &str,
_time: std::time::SystemTime,
) -> vfs::VfsResult<()> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
fn set_access_time(&self, _path: &str, _time: std::time::SystemTime) -> vfs::VfsResult<()> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
fn copy_file(&self, _src: &str, _dest: &str) -> vfs::VfsResult<()> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
fn move_file(&self, _src: &str, _dest: &str) -> vfs::VfsResult<()> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
fn move_dir(&self, _src: &str, _dest: &str) -> vfs::VfsResult<()> {
Err(vfs::error::VfsErrorKind::NotSupported.into())
}
};
}