use crate::Parser;
use cl_ast::{ast_visitor::Fold, *};
use cl_lexer::Lexer;
use std::path::{Path, PathBuf};
pub type IoErrs = Vec<(PathBuf, std::io::Error)>;
pub type ParseErrs = Vec<(PathBuf, crate::error::Error)>;
pub struct ModuleInliner {
path: PathBuf,
io_errs: IoErrs,
parse_errs: ParseErrs,
}
impl ModuleInliner {
pub fn new(root: impl AsRef<Path>) -> Self {
Self {
path: root.as_ref().to_path_buf(),
io_errs: Default::default(),
parse_errs: Default::default(),
}
}
pub fn has_errors(&self) -> bool {
!(self.io_errs.is_empty() && self.parse_errs.is_empty())
}
pub fn into_errs(self) -> Option<(IoErrs, ParseErrs)> {
self.has_errors().then_some((self.io_errs, self.parse_errs))
}
pub fn inline(mut self, file: File) -> Result<File, (File, IoErrs, ParseErrs)> {
let file = self.fold_file(file);
match self.into_errs() {
Some((io, parse)) => Err((file, io, parse)),
None => Ok(file),
}
}
fn handle_io_error(&mut self, error: std::io::Error) -> ModuleKind {
self.io_errs.push((self.path.clone(), error));
ModuleKind::Outline
}
fn handle_parse_error(&mut self, error: crate::error::Error) -> ModuleKind {
self.parse_errs.push((self.path.clone(), error));
ModuleKind::Outline
}
}
impl Fold for ModuleInliner {
fn fold_module(&mut self, m: Module) -> Module {
let Module { name, kind } = m;
self.path.push(&*name); let kind = self.fold_module_kind(kind);
self.path.pop(); Module { name, kind }
}
fn fold_module_kind(&mut self, m: ModuleKind) -> ModuleKind {
if let ModuleKind::Inline(f) = m {
return ModuleKind::Inline(self.fold_file(f));
}
self.path.set_extension("cl");
let file = match std::fs::read_to_string(&self.path) {
Err(error) => return self.handle_io_error(error),
Ok(file) => file,
};
let kind = match Parser::new(Lexer::new(&file)).file() {
Err(e) => return self.handle_parse_error(e),
Ok(file) => ModuleKind::Inline(file),
};
self.path.set_extension("");
self.fold_module_kind(kind)
}
}