use super::{LoadError, Loader, SourceFile, SourceName};
use std::path::{Path, PathBuf};
#[derive(Debug)]
pub struct CargoLoader {
path: Vec<PathBuf>,
}
impl CargoLoader {
pub fn for_crate() -> Result<Self, LoadError> {
Ok(Self {
path: vec![get_pkg_base()?],
})
}
pub fn push_path(&mut self, path: &Path) -> Result<(), LoadError> {
self.path.push(if path.is_absolute() {
path.into()
} else {
get_pkg_base()?.join(path)
});
Ok(())
}
pub fn for_path(path: &Path) -> Result<(Self, SourceFile), LoadError> {
let path = if path.is_absolute() {
path.into()
} else {
get_pkg_base()?.join(path)
};
let mut f = std::fs::File::open(&path)
.map_err(|e| LoadError::Input(path.display().to_string(), e))?;
cargo_watch(&path);
let (path, name) = if let Some((base, path)) = path
.parent()
.and_then(|base| Some((base, path.strip_prefix(base).ok()?)))
{
(vec![base.to_path_buf()], path)
} else {
(vec![get_pkg_base()?], path.as_ref())
};
let loader = Self { path };
let source = SourceName::root(name.display().to_string());
let source = SourceFile::read(&mut f, source)?;
Ok((loader, source))
}
}
impl Loader for CargoLoader {
type File = std::fs::File;
fn find_file(&self, url: &str) -> Result<Option<Self::File>, LoadError> {
if !url.is_empty() {
for base in &self.path {
let full = base.join(url);
if full.is_file() {
tracing::debug!(?full, "opening file");
let file = Self::File::open(&full).map_err(|e| {
LoadError::Input(full.display().to_string(), e)
})?;
cargo_watch(&full);
return Ok(Some(file));
}
tracing::trace!(?full, "Not found");
}
}
Ok(None)
}
}
fn cargo_watch(path: &Path) {
println!("cargo:rerun-if-changed={}", path.display());
}
fn get_pkg_base() -> Result<PathBuf, LoadError> {
std::env::var_os("CARGO_MANIFEST_DIR")
.map(PathBuf::from)
.ok_or(LoadError::NotCalledFromCargo)
}