use std::collections::{BTreeMap, BTreeSet};
use camino::Utf8Path;
use crate::{
Build, FileName, InvalidFileName, InvalidRuleId, InvalidVariable, InvalidVariableId, RuleId,
Variable, VariableId,
};
#[derive(Debug, Clone)]
pub struct BuildBuilder {
rule: Option<RuleId>,
explicit: BTreeSet<FileName>,
implicit: BTreeSet<FileName>,
order: BTreeSet<FileName>,
dyndep: Option<FileName>,
shadow: BTreeMap<VariableId, Variable>,
errors: Vec<BuildBuilderError>,
}
#[derive(Debug, Clone)]
pub enum BuildBuilderError {
InvalidRuleId(InvalidRuleId),
InvalidFileName(InvalidFileName),
InvalidVariable(InvalidVariable),
InvalidVariableId(InvalidVariableId),
DynDepAlreadyDefined {
previous: FileName,
dyndep: FileName,
},
VariableAlreadyDefined {
id: VariableId,
previous: Variable,
var: Variable,
},
}
impl BuildBuilder {
pub fn new<Id>(rule_id: Id) -> Self
where
Id: AsRef<str>,
{
let rule_id = RuleId::try_create(rule_id);
let mut errors = vec![];
if let Some(err) = rule_id.clone().err() {
errors.push(BuildBuilderError::InvalidRuleId(err));
}
let rule = rule_id.ok();
BuildBuilder {
rule,
errors,
explicit: BTreeSet::new(),
implicit: BTreeSet::new(),
order: BTreeSet::new(),
dyndep: None,
shadow: BTreeMap::new(),
}
}
pub fn build(&self) -> Result<Build, Vec<BuildBuilderError>> {
if !self.errors.is_empty() {
return Err(self.errors.clone());
}
let build = Build {
rule: self.rule.clone().unwrap(),
explicit: self.explicit.clone(),
implicit: self.implicit.clone(),
order: self.order.clone(),
dyndep: self.dyndep.clone(),
shadow: self.shadow.clone(),
};
Ok(build)
}
pub fn explicit<Dep>(mut self, dep: Dep) -> Self
where
Dep: AsRef<Utf8Path>,
{
let filename = FileName::try_create(dep);
match filename {
Ok(file) => {
self.explicit.insert(file);
}
Err(err) => self.errors.push(BuildBuilderError::InvalidFileName(err)),
}
self
}
pub fn implicit<Dep>(mut self, dep: Dep) -> Self
where
Dep: AsRef<Utf8Path>,
{
let filename = FileName::try_create(dep);
match filename {
Ok(file) => {
self.implicit.insert(file);
}
Err(err) => self.errors.push(BuildBuilderError::InvalidFileName(err)),
}
self
}
pub fn order<Dep>(mut self, dep: Dep) -> Self
where
Dep: AsRef<Utf8Path>,
{
let filename = FileName::try_create(dep);
match filename {
Ok(file) => {
self.order.insert(file);
}
Err(err) => self.errors.push(BuildBuilderError::InvalidFileName(err)),
}
self
}
pub fn dyndep<Dep>(mut self, dep: Dep) -> Self
where
Dep: AsRef<Utf8Path>,
{
let filename = FileName::try_create(dep);
match (filename, self.dyndep.clone()) {
(Ok(file), None) => self.dyndep = Some(file),
(Ok(dyndep), Some(previous)) => self
.errors
.push(BuildBuilderError::DynDepAlreadyDefined { previous, dyndep }),
(Err(err), _) => self.errors.push(BuildBuilderError::InvalidFileName(err)),
}
self
}
pub fn variable<Id, Var>(mut self, id: Id, var: Var) -> Self
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.shadow.insert(id.clone(), var.clone()) {
Some(previous) if var == previous => (),
Some(previous) => self.errors.push(BuildBuilderError::VariableAlreadyDefined {
id,
previous,
var,
}),
None => (),
},
(Ok(_), Err(v)) => self.errors.push(BuildBuilderError::InvalidVariable(v)),
(Err(i), Ok(_)) => self.errors.push(BuildBuilderError::InvalidVariableId(i)),
(Err(i), Err(v)) => {
self.errors.push(BuildBuilderError::InvalidVariable(v));
self.errors.push(BuildBuilderError::InvalidVariableId(i));
}
}
self
}
}
impl From<Build> for BuildBuilder {
fn from(value: Build) -> Self {
BuildBuilder {
rule: Some(value.rule),
explicit: value.explicit,
implicit: value.implicit,
order: value.order,
dyndep: value.dyndep,
shadow: value.shadow,
errors: vec![],
}
}
}