use std::mem::take;
use std::path::PathBuf;
use std::vec::Vec;
use module::merge::Merge;
use serde::Deserialize;
use crate::evaluator::dfs;
use crate::evaluator::{Acyclic, Dfs, Evaluator, Imports, Trace};
pub mod format;
pub use self::format::{Format, Module};
pub mod error;
pub use self::error::Error;
#[derive(Debug)]
pub struct File<T, F>
where
T: Merge,
{
evaluator: Acyclic<Dfs<PathBuf, T>>,
format: F,
}
impl<T, F> File<T, F>
where
T: Merge,
{
pub fn new(format: F) -> Self {
Self {
evaluator: Default::default(),
format,
}
}
}
impl<T, F> Default for File<T, F>
where
T: Merge,
F: Default,
{
fn default() -> Self {
Self::new(Default::default())
}
}
impl<T, F> File<T, F>
where
T: Merge,
F: Format<T>,
F::Error: Send + Sync,
{
pub fn read(&mut self, path: PathBuf) -> Result<(), Error> {
self.drive_to_end()?;
self.eval_one(path)?;
self.drive_to_end()?;
Ok(())
}
pub fn finish(self) -> Option<T> {
self.evaluator.finish()
}
fn eval_one(&mut self, path: PathBuf) -> Result<(), Error> {
let Module { imports, body } = self.format.read(&path).map_err(|e| {
Error::new(dfs::Error::other(e)).with_trace({
let mut trace = self.take_trace();
trace.push(path.clone());
trace
})
})?;
let dirname = path
.parent()
.expect("read() succeeded so it should have a parent");
let imports = {
let mut tmp = Vec::from(imports);
for import in &mut tmp {
*import = dirname.join(&import);
}
Imports::from(tmp)
};
self.evaluator
.eval(path, imports, body)
.map_err(|e| Error::new(e).with_trace(self.take_trace()))?;
Ok(())
}
fn drive_to_end(&mut self) -> Result<(), Error> {
while let Some(path) = self.evaluator.next() {
self.eval_one(path)?;
}
Ok(())
}
fn take_trace(&mut self) -> Trace<PathBuf> {
let dfs = self.evaluator.get_mut();
take(&mut dfs.trace)
}
}
#[cfg(feature = "json")]
pub fn json<T, I>(paths: I) -> Result<Option<T>, Error>
where
T: for<'de> Deserialize<'de> + Merge,
I: IntoIterator<Item = PathBuf>,
{
let mut file = File::new(format::Json);
for path in paths {
file.read(path)?;
}
Ok(file.finish())
}
#[cfg(feature = "toml")]
pub fn toml<T, I>(paths: I) -> Result<Option<T>, Error>
where
T: for<'de> Deserialize<'de> + Merge,
I: IntoIterator<Item = PathBuf>,
{
let mut file = File::new(format::Toml);
for path in paths {
file.read(path)?;
}
Ok(file.finish())
}