use std::collections::HashSet;
use std::fs::read_dir;
use std::io;
use std::path::Path;
use std::path::PathBuf;
pub trait AbstractFilesystem {
fn file_names_in(&self, rel_path: &str) -> io::Result<HashSet<Box<str>>>;
fn read_root_workspace(&self, _rel_path_hint: Option<&str>) -> io::Result<(Vec<u8>, PathBuf)> {
Err(io::Error::new(io::ErrorKind::Unsupported, "AbstractFilesystem::read_root_workspace unimplemented"))
}
}
impl<T> AbstractFilesystem for &T
where
T: AbstractFilesystem + ?Sized,
{
fn file_names_in(&self, rel_path: &str) -> io::Result<HashSet<Box<str>>> {
<T as AbstractFilesystem>::file_names_in(*self, rel_path)
}
}
pub struct Filesystem<'a> {
path: &'a Path,
}
impl<'a> Filesystem<'a> {
#[must_use]
pub fn new(path: &'a Path) -> Self {
Self { path }
}
}
impl<'a> AbstractFilesystem for Filesystem<'a> {
fn file_names_in(&self, rel_path: &str) -> io::Result<HashSet<Box<str>>> {
Ok(read_dir(self.path.join(rel_path))?.filter_map(|entry| {
entry.ok().map(|e| {
e.file_name().to_string_lossy().into_owned().into()
})
})
.collect())
}
fn read_root_workspace(&self, path: Option<&str>) -> io::Result<(Vec<u8>, PathBuf)> {
match path {
Some(path) => {
let ws = self.path.join(path);
Ok((std::fs::read(ws.join("Cargo.toml"))?, ws))
},
None => {
match find_in(self.path) {
Ok(found) => Ok(found),
Err(err) if self.path.is_absolute() => Err(err),
Err(_) => find_in(&self.path.ancestors().last().unwrap().canonicalize()?),
}
}
}
}
}
fn find_in(path: &Path) -> io::Result<(Vec<u8>, PathBuf)> {
path.ancestors().skip(1)
.map(|parent| parent.join("Cargo.toml"))
.find_map(|p| {
Some((std::fs::read(&p).ok()?, p))
})
.ok_or(io::ErrorKind::NotFound.into())
}