use std::collections::HashSet;
use std::fmt;
use std::path::{Path, PathBuf};
use module::{Error, Merge};
use serde::de::DeserializeOwned;
use crate::Result;
use crate::evaluator::Evaluator;
use crate::file::Module;
use super::Format;
#[derive(Debug)]
pub struct File<T, F> {
inner: Evaluator<DisplayPath, T>,
evaluated: HashSet<PathBuf>,
format: F,
}
impl<T, F> File<T, F> {
pub fn new(format: F) -> Self {
Self {
inner: Evaluator::new(),
evaluated: HashSet::new(),
format,
}
}
pub fn format(&self) -> &F {
&self.format
}
pub fn format_mut(&mut self) -> &mut F {
&mut self.format
}
pub fn finish(self) -> Option<T> {
self.inner.finish()
}
}
impl<T, F> File<T, F>
where
T: Merge + DeserializeOwned,
F: Format,
{
pub fn read<P>(&mut self, path: P) -> Result<()>
where
P: AsRef<Path>,
{
self._read(path.as_ref())
}
fn _read(&mut self, path: &Path) -> Result<()> {
self.inner.import(DisplayPath(path.to_path_buf()));
while let Some(path) = self.inner.next() {
let DisplayPath(path) = path;
let realpath = path
.canonicalize()
.map_err(Error::custom)
.map_err(|mut e| {
e.trace = self.inner.trace(DisplayPath(path));
e
})?;
if !self.evaluated.insert(realpath.clone()) {
return Err(Error::cycle()).map_err(|mut e| {
e.trace = self.inner.trace(DisplayPath(realpath));
e
});
}
let Module { value, imports } = match self.format.read(&realpath) {
Ok(x) => x,
Err(mut e) => {
e.trace = self.inner.trace(DisplayPath(realpath));
return Err(e);
}
};
let basename = realpath
.parent()
.expect("file path should always have an ancestor");
let imports = imports
.into_iter()
.map(|x| basename.join(x))
.map(DisplayPath)
.collect();
self.inner.eval(DisplayPath(realpath), imports, value)?;
}
Ok(())
}
}
#[expect(clippy::missing_panics_doc)]
pub fn read<T, F>(path: impl AsRef<Path>, format: F) -> Result<T, Error>
where
T: Merge + DeserializeOwned,
F: Format,
{
let mut file = File::new(format);
file.read(path)?;
let value = file
.finish()
.expect("File should have read at least one module");
Ok(value)
}
impl<T, F> Default for File<T, F>
where
T: Merge,
F: Default,
{
fn default() -> Self {
Self::new(Default::default())
}
}
#[derive(Clone)]
struct DisplayPath(PathBuf);
impl fmt::Debug for DisplayPath {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.0.fmt(f)
}
}
impl fmt::Display for DisplayPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.display().fmt(f)
}
}