Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overlay FileSystem #3677

Merged
merged 12 commits into from
Mar 16, 2023
Prev Previous commit
Next Next commit
Rewrote the FileSystems trait to use external iterators
Michael-F-Bryan committed Mar 16, 2023

Unverified

No user is associated with the committer email.
commit 10e0d600fc13ae1148c75489bb03eaf1555fa8ab
5 changes: 3 additions & 2 deletions lib/vfs/src/filesystem_ext.rs
Original file line number Diff line number Diff line change
@@ -40,10 +40,11 @@ pub trait FileSystemExt {
/// This is analogous to [`std::fs::read_to_string()`].
async fn read_to_string(&self, path: impl AsRef<Path> + Send) -> Result<String, FsError>;

/// Make sure a file exists, creating an empty file if it doesn't.
/// Update a file's modification and access times, creating the file if it
/// doesn't already exist.
fn touch(&self, path: impl AsRef<Path> + Send) -> Result<(), FsError>;

/// Recursively iterate over all paths inside a directory, discarding any
/// Recursively iterate over all paths inside a directory, ignoring any
/// errors that may occur along the way.
fn walk(&self, path: impl AsRef<Path>) -> Box<dyn Iterator<Item = DirEntry> + '_>;
}
114 changes: 54 additions & 60 deletions lib/vfs/src/filesystems.rs
Original file line number Diff line number Diff line change
@@ -1,105 +1,99 @@
use crate::FileSystem;
use std::ops::ControlFlow;

/// A chain of one or more [`FileSystem`]s.
pub trait FileSystems {
pub trait FileSystems<'a>: 'a {
Michael-F-Bryan marked this conversation as resolved.
Show resolved Hide resolved
// FIXME(Michael-F-Bryan): Rewrite this to use GATs and an external iterator
// when we bump the MSRV to 1.65 or higher.
fn for_each_filesystems<F, Ret>(&self, func: F) -> Option<Ret>
where
F: FnMut(&dyn FileSystem) -> ControlFlow<Ret>;
// when we bump the MSRV to 1.65 or higher. That'll get rid of all the
// lifetimes and HRTBs.
type Iter: IntoIterator<Item = &'a dyn FileSystem> + 'a;
fn iter_filesystems(&'a self) -> Self::Iter;
}

impl<'b, S> FileSystems for &'b S
impl<'a, 'b, S> FileSystems<'a> for &'b S
where
S: FileSystems + 'b,
S: FileSystems<'a> + 'b,
'b: 'a,
{
fn for_each_filesystems<F, Ret>(&self, func: F) -> Option<Ret>
where
F: FnMut(&dyn FileSystem) -> ControlFlow<Ret>,
{
(**self).for_each_filesystems(func)
type Iter = S::Iter;

fn iter_filesystems(&'a self) -> Self::Iter {
(**self).iter_filesystems()
}
}

impl<T> FileSystems for Vec<T>
impl<'a, T> FileSystems<'a> for Vec<T>
where
T: FileSystem,
{
fn for_each_filesystems<F, Ret>(&self, func: F) -> Option<Ret>
where
F: FnMut(&dyn FileSystem) -> ControlFlow<Ret>,
{
self[..].for_each_filesystems(func)
type Iter = <[T] as FileSystems<'a>>::Iter;

fn iter_filesystems(&'a self) -> Self::Iter {
self[..].iter_filesystems()
}
}

impl<T, const N: usize> FileSystems for [T; N]
impl<'a, T, const N: usize> FileSystems<'a> for [T; N]
where
T: FileSystem,
{
fn for_each_filesystems<F, Ret>(&self, func: F) -> Option<Ret>
where
F: FnMut(&dyn FileSystem) -> ControlFlow<Ret>,
{
self[..].for_each_filesystems(func)
type Iter = [&'a dyn FileSystem; N];

fn iter_filesystems(&'a self) -> Self::Iter {
// TODO: rewrite this when array::each_ref() is stable
let mut i = 0;
[(); N].map(|_| {
let f = &self[i] as &dyn FileSystem;
i += 1;
f
})
}
}

impl<T> FileSystems for [T]
impl<'a, T> FileSystems<'a> for [T]
where
T: FileSystem,
{
fn for_each_filesystems<F, Ret>(&self, mut func: F) -> Option<Ret>
where
F: FnMut(&dyn FileSystem) -> ControlFlow<Ret>,
{
for fs in self.iter() {
match func(fs) {
ControlFlow::Continue(_) => continue,
ControlFlow::Break(result) => return Some(result),
}
}
type Iter = std::iter::Map<std::slice::Iter<'a, T>, fn(&T) -> &dyn FileSystem>;

None
fn iter_filesystems(&'a self) -> Self::Iter {
self.iter().map(|fs| fs as &dyn FileSystem)
}
}

impl FileSystems for () {
fn for_each_filesystems<F, Ret>(&self, _func: F) -> Option<Ret>
where
F: FnMut(&dyn FileSystem) -> ControlFlow<Ret>,
{
None
impl<'a> FileSystems<'a> for () {
type Iter = std::iter::Empty<&'a dyn FileSystem>;

fn iter_filesystems(&'a self) -> Self::Iter {
std::iter::empty()
}
}

macro_rules! count {
($first:tt $($rest:tt)*) => {
1 + count!($($rest)*)
};
() => { 0 };
}

macro_rules! tuple_filesystems {
($first:ident $(, $rest:ident)* $(,)?) => {
impl<$first, $( $rest ),*> FileSystems for ($first, $($rest),*)
impl<'a, $first, $( $rest ),*> FileSystems<'a> for ($first, $($rest),*)
where
$first: FileSystem,
$($rest: FileSystem),*
{
fn for_each_filesystems<F, Ret>(&self, mut func: F) -> Option<Ret>
where
F: FnMut(&dyn FileSystem) -> ControlFlow<Ret>,
{
#[allow(non_snake_case)]
let ($first, $($rest),*) = &self;
type Iter = [&'a dyn FileSystem; count!($first $($rest)*)];

if let ControlFlow::Break(result) = func($first) {
return Some(result);
}

$(
if let ControlFlow::Break(result) = func($rest) {
return Some(result);
}
)*
fn iter_filesystems(&'a self) -> Self::Iter {
#[allow(non_snake_case)]
let ($first, $($rest),*) = self;

None
[
$first as &dyn FileSystem,
$($rest),*
]
}

}

tuple_filesystems!($($rest),*);
Loading