use std::fmt;
use std::io;
use std::path::{Path, PathBuf};
use super::LayerResolver;
#[derive(Debug, Clone, Default)]
pub struct FsResolver {
prefixes: Vec<(String, PathBuf)>,
}
impl FsResolver {
pub fn new() -> Self {
Self::default()
}
pub fn map_prefix(mut self, prefix: impl Into<String>, dir: impl Into<PathBuf>) -> Self {
self.prefixes.push((prefix.into(), dir.into()));
self
}
fn path(&self, uri: &str, importer: Option<&str>) -> Result<PathBuf, FsError> {
for (prefix, dir) in &self.prefixes {
if let Some(rest) = uri.strip_prefix(prefix.as_str()) {
return Ok(dir.join(rest.trim_start_matches('/')));
}
}
if uri.contains("://") {
return Err(FsError::UnsupportedUri(uri.to_owned()));
}
let path = Path::new(uri);
Ok(match importer.and_then(|key| Path::new(key).parent()) {
Some(dir) if path.is_relative() => dir.join(path),
_ => path.to_path_buf(),
})
}
}
impl LayerResolver for FsResolver {
type Error = FsError;
fn key(&mut self, uri: &str, importer: Option<&str>) -> Result<String, FsError> {
let path = self.path(uri, importer)?;
let path = std::fs::canonicalize(&path).unwrap_or(path);
path.into_os_string()
.into_string()
.map_err(|path| FsError::NonUtf8Path(path.into()))
}
fn load(&mut self, key: &str) -> Result<Option<Vec<u8>>, FsError> {
match std::fs::read(key) {
Ok(bytes) => Ok(Some(bytes)),
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(None),
Err(source) => Err(FsError::Io {
path: key.into(),
source,
}),
}
}
}
#[derive(Debug)]
pub enum FsError {
UnsupportedUri(String),
NonUtf8Path(PathBuf),
Io {
path: PathBuf,
source: io::Error,
},
}
impl fmt::Display for FsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnsupportedUri(uri) => write!(f, "no local mapping for URI {uri:?}"),
Self::NonUtf8Path(path) => write!(f, "path {} is not valid UTF-8", path.display()),
Self::Io { path, source } => write!(f, "reading {}: {source}", path.display()),
}
}
}
impl std::error::Error for FsError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io { source, .. } => Some(source),
Self::UnsupportedUri(_) | Self::NonUtf8Path(_) => None,
}
}
}