use std::io;
use std::path::{Path, PathBuf};
use crate::load::{DirEntry, FileKind, LoadedPlugin, Rejection};
use crate::path::PackagePath;
use crate::source::{self, Source};
#[derive(Debug, Clone)]
pub struct DirSource {
root: PathBuf,
canonical_root: PathBuf,
}
impl DirSource {
pub fn open(root: impl AsRef<Path>) -> io::Result<Self> {
let root = root.as_ref().to_path_buf();
let canonical_root = root.canonicalize()?;
Ok(Self { root, canonical_root })
}
pub fn root(&self) -> &Path {
&self.root
}
pub fn canonical_root(&self) -> &Path {
&self.canonical_root
}
fn native(&self, path: &PackagePath) -> PathBuf {
path.to_native(&self.root)
}
}
impl Source for DirSource {
type Error = io::Error;
fn kind(&mut self, path: &PackagePath) -> Result<Option<FileKind>, io::Error> {
match std::fs::metadata(self.native(path)) {
Ok(meta) => Ok(Some(if meta.is_file() {
FileKind::File
} else if meta.is_dir() {
FileKind::Directory
} else {
FileKind::Other
})),
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(None),
Err(e) => Err(e),
}
}
fn confined(&mut self, path: &PackagePath) -> Result<bool, io::Error> {
let resolved = self.native(path).canonicalize()?;
Ok(resolved.starts_with(&self.canonical_root))
}
fn read(&mut self, path: &PackagePath) -> Result<Vec<u8>, io::Error> {
std::fs::read(self.native(path))
}
fn list(&mut self, path: &PackagePath) -> Result<Vec<DirEntry>, io::Error> {
let mut entries = Vec::new();
for entry in std::fs::read_dir(self.native(path))? {
let entry = entry?;
let Ok(name) = entry.file_name().into_string() else {
continue; };
let kind = match std::fs::metadata(entry.path()) {
Ok(meta) if meta.is_file() => FileKind::File,
Ok(meta) if meta.is_dir() => FileKind::Directory,
_ => FileKind::Other,
};
entries.push(DirEntry::new(name, kind));
}
entries.sort_by(|a, b| a.name.cmp(&b.name));
Ok(entries)
}
}
pub fn load_dir(path: impl AsRef<Path>) -> Result<LoadedPlugin, Rejection> {
let mut source = DirSource::open(path)
.map_err(|e| Rejection::Source { detail: format!("cannot open plugin root: {e}") })?;
source::load(&mut source)
}