use std::collections::BTreeMap;
use crate::{
Command, CommandBuilder, CommandBuilderError, InvalidVariable, InvalidVariableId, Rule,
Variable, VariableId,
};
pub struct RuleBuilder {
command: Option<Command>,
shadow: BTreeMap<VariableId, Variable>,
generator: bool,
errors: Vec<RuleBuilderError>,
}
#[derive(Clone, Debug)]
pub enum RuleBuilderError {
InvalidCommand(Vec<CommandBuilderError>),
InvalidVariableId(InvalidVariableId),
InvalidVariable(InvalidVariable),
VariableAlreadyDefined {
id: VariableId,
previous: Variable,
var: Variable,
},
}
impl RuleBuilder {
pub fn new<C>(command_builder: C) -> Self
where
C: Into<CommandBuilder>,
{
let mut command = None;
let mut errors = vec![];
match command_builder.into().build() {
Ok(com) => command = Some(com),
Err(err) => errors.push(RuleBuilderError::InvalidCommand(err)),
};
RuleBuilder {
command,
shadow: BTreeMap::new(),
generator: false,
errors,
}
}
pub fn build(&self) -> Result<Rule, Vec<RuleBuilderError>> {
let errors = self.errors.clone();
let shadow = self.shadow.clone();
let generator = self.generator;
if !self.errors.is_empty() {
return Err(errors);
}
let command = self.command.clone().unwrap();
let rule = Rule {
command,
shadow,
generator,
};
Ok(rule)
}
pub fn generator(mut self, generator: bool) -> Self {
self.generator = generator;
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(RuleBuilderError::VariableAlreadyDefined { id, previous, var })
}
None => (),
},
(Ok(_), Err(v)) => self.errors.push(RuleBuilderError::InvalidVariable(v)),
(Err(i), Ok(_)) => self.errors.push(RuleBuilderError::InvalidVariableId(i)),
(Err(i), Err(v)) => {
self.errors.push(RuleBuilderError::InvalidVariable(v));
self.errors.push(RuleBuilderError::InvalidVariableId(i));
}
}
self
}
}
impl From<Rule> for RuleBuilder {
fn from(value: Rule) -> Self {
let mut builder = RuleBuilder::new(value.command).generator(value.generator);
for (id, var) in value.shadow.into_iter() {
builder = builder.variable(id, var);
}
builder
}
}