use std::{
fs::{File, read_dir},
io::{BufRead, BufReader},
path::{Path, PathBuf},
};
use super::{AspectExpressionError, Error, MultilinearParser, NamedMultilinearInfo};
#[derive(Error, Debug)]
pub enum AspectErrorKind {
#[error("Failed to open multilinear aspect file")]
OpeningFile,
#[error("Failed to read multilinear aspect file")]
ReadingFile,
#[error("{0}")]
AddingAspectExpression(#[source] AspectExpressionError),
}
impl AspectErrorKind {
fn error(self, path: PathBuf) -> AspectError {
AspectError { path, kind: self }
}
}
#[derive(Error, Debug)]
#[error("Error parsing aspect file \"{path}\": {kind}", path = path.display())]
pub struct AspectError {
path: PathBuf,
kind: AspectErrorKind,
}
#[derive(Error, Debug)]
pub enum DirectoryOrFileErrorKind {
#[error("Path does not exist")]
PathNotFound,
#[error("Failed to open multilinear definition file")]
OpeningFile,
#[error("Parsing error: {0}")]
Parsing(#[source] Error),
#[error("Failed to read directory")]
ReadingDirectory,
}
impl DirectoryOrFileErrorKind {
fn error(self, path: PathBuf) -> DirectoryOrFileError {
DirectoryOrFileError { path, kind: self }
}
}
#[derive(Error, Debug)]
#[error("Error parsing \"{path}\": {kind}", path = path.display())]
pub struct DirectoryOrFileError {
path: PathBuf,
kind: DirectoryOrFileErrorKind,
}
#[derive(Error, Debug)]
pub enum ExtendedError {
#[error("{0}")]
DirectoryOrFile(
#[source]
#[from]
DirectoryOrFileError,
),
#[error("{0}")]
AspectFile(
#[source]
#[from]
AspectError,
),
}
impl MultilinearParser {
pub fn parse_aspect_defaults(&mut self, path: &Path) -> Result<(), AspectError> {
let Ok(file) = File::open(path) else {
return Err(AspectErrorKind::OpeningFile.error(path.into()));
};
for line in BufReader::new(file).lines() {
let Ok(line) = line else {
return Err(AspectErrorKind::ReadingFile.error(path.into()));
};
if let Err(err) = self.add_aspect_expression(&line) {
return Err(AspectErrorKind::AddingAspectExpression(err).error(path.into()));
}
}
Ok(())
}
pub fn parse_directory_or_file(
&mut self,
path: &Path,
namespace: &mut Vec<Box<str>>,
) -> Result<(), DirectoryOrFileError> {
if !path.exists() {
return Err(DirectoryOrFileErrorKind::PathNotFound.error(path.into()));
}
if path.is_file() {
let valid_path = path.extension().is_some_and(|e| e == "mld");
if !valid_path {
return Ok(());
}
let Ok(multilinear_file) = File::open(path) else {
return Err(DirectoryOrFileErrorKind::OpeningFile.error(path.into()));
};
if let Err(source) = self.parse(multilinear_file, namespace.as_ref()) {
return Err(DirectoryOrFileErrorKind::Parsing(source).error(path.into()));
}
return Ok(());
}
let Ok(dir) = read_dir(path) else {
return Err(DirectoryOrFileErrorKind::ReadingDirectory.error(path.into()));
};
for file in dir.flatten() {
let path = file.path();
let Some(name) = path.file_stem() else {
continue;
};
let Some(name) = name.to_str() else { continue };
namespace.push(name.into());
let result = self.parse_directory_or_file(&path, namespace);
namespace.pop();
result?;
}
Ok(())
}
}
pub fn parse_multilinear_extended(
input_path: &Path,
aspects_path: Option<&Path>,
) -> Result<NamedMultilinearInfo, ExtendedError> {
let mut multilinear_parser = MultilinearParser::default();
if let Some(aspects_path) = &aspects_path {
multilinear_parser.parse_aspect_defaults(aspects_path)?;
}
multilinear_parser.parse_directory_or_file(input_path, &mut Vec::new())?;
Ok(multilinear_parser.into_info())
}