use camino::Utf8Path;
use crate::{
Build, BuildBuilder, BuildBuilderError, File, FileName, InvalidFileName, InvalidRuleId,
InvalidVariable, InvalidVariableId, Rule, RuleBuilder, RuleBuilderError, RuleId, Variable,
VariableId,
};
#[derive(Clone, Debug, Default)]
pub struct FileBuilder {
file: File,
errors: Vec<FileBuilderError>,
}
#[derive(Clone, Debug)]
pub enum FileBuilderError {
InvalidVariableId(InvalidVariableId),
InvalidVariable(InvalidVariable),
InvaidRuleId(InvalidRuleId),
InvalidFileName(InvalidFileName),
InvalidRule {
id: RuleId,
errors: Vec<RuleBuilderError>,
},
InvalidBuild {
id: FileName,
errors: Vec<BuildBuilderError>,
},
RuleAlreadyExists {
id: RuleId,
previous: Rule,
rule: Rule,
},
BuildAlreadyExists {
id: FileName,
previous: Build,
build: Build,
},
VariableAlreadyExists(VariableId),
}
impl FileBuilder {
pub fn new() -> FileBuilder {
let file = File::default();
let errors = vec![];
FileBuilder { file, errors }
}
pub fn build(&self) -> Result<File, Vec<FileBuilderError>> {
if self.errors.is_empty() {
Ok(self.file.clone())
} else {
Err(self.errors.clone())
}
}
pub fn variable<Id, Var>(mut self, id: Id, var: Var) -> FileBuilder
where
Id: AsRef<str>,
Var: AsRef<str>,
{
let id = VariableId::try_create(id);
let var = Variable::try_create(var);
match (id, var) {
(Ok(id), Ok(var)) => match self.file.variables.insert(id.clone(), var.clone()) {
Some(exist) if exist == var => (),
Some(_) => self
.errors
.push(FileBuilderError::VariableAlreadyExists(id)),
None => (),
},
(Ok(_), Err(e)) => self.errors.push(FileBuilderError::InvalidVariable(e)),
(Err(e), Ok(_)) => self.errors.push(FileBuilderError::InvalidVariableId(e)),
(Err(e1), Err(e2)) => {
self.errors.push(FileBuilderError::InvalidVariableId(e1));
self.errors.push(FileBuilderError::InvalidVariable(e2));
}
}
self
}
pub fn rule<Id, Rule>(mut self, id: Id, rule: Rule) -> FileBuilder
where
Id: AsRef<str>,
Rule: Into<RuleBuilder>,
{
let id = RuleId::try_create(id);
let rule = rule.into().build();
if let Some(err) = id.clone().err() {
self.errors.push(FileBuilderError::InvaidRuleId(err));
return self;
}
let id = id.unwrap();
if let Some(err) = rule.clone().err() {
self.errors
.push(FileBuilderError::InvalidRule { id, errors: err });
return self;
}
let rule = rule.unwrap();
match self.file.rules.get(&id) {
Some(previous) if previous == &rule => (),
Some(previous) => self.errors.push(FileBuilderError::RuleAlreadyExists {
id,
previous: previous.clone(),
rule,
}),
None => {
let _ = self.file.rules.insert(id, rule);
}
}
self
}
pub fn file<File, Build>(mut self, filename: File, build: Build) -> Self
where
File: AsRef<Utf8Path>,
Build: Into<BuildBuilder>,
{
let filename = FileName::try_create(filename);
let build = build.into().build();
if let Some(err) = filename.clone().err() {
self.errors.push(FileBuilderError::InvalidFileName(err));
return self;
}
let filename = filename.unwrap();
if let Some(errors) = build.clone().err() {
self.errors.push(FileBuilderError::InvalidBuild {
id: filename.clone(),
errors,
})
}
let build = build.unwrap();
match self.file.statements.get(&filename) {
Some(previous) if previous == &build => (),
Some(previous) => self.errors.push(FileBuilderError::BuildAlreadyExists {
id: filename,
previous: previous.clone(),
build,
}),
None => {
let _ = self.file.statements.insert(filename, build);
}
}
self
}
pub fn merge(mut self, other: &Self) -> Self {
self.errors.extend(other.errors.clone());
let mut file = self;
for (id, var) in other.file.variables.iter() {
file = file.variable(id.clone(), var.clone());
}
for (id, rule) in other.file.rules.iter() {
file = file.rule(id.clone(), rule.clone());
}
for (id, rule) in other.file.statements.iter() {
file = file.file(id.clone(), rule.clone());
}
file
}
}