use std::error::Error;
use ascesis::{Contextual, Runner, StopCondition, Multiplicity};
use super::{App, Command, Solve, Styled};
pub struct Go {
solve_command: Solve,
start_triggers: Vec<(String, Multiplicity)>,
stop_triggers: Vec<(String, Multiplicity)>,
}
impl Go {
fn trigger_parse<S: AsRef<str>>(name: S) -> (String, Multiplicity) {
let name = name.as_ref();
if let Some(pos) = name.rfind(':') {
if pos > 0 && pos < name.len() {
if let Ok(weight) = name[pos + 1..].parse::<Multiplicity>() {
return (name[..pos].to_owned(), weight)
}
}
}
(name.to_owned(), Multiplicity::one())
}
pub(crate) fn new(app: &mut App) -> Self {
let solve_command = Solve::new(app);
let mut start_triggers = Vec::new();
let mut stop_triggers = Vec::new();
if let Some(values) = app.values_of("START") {
start_triggers.extend(values.map(Self::trigger_parse));
}
if let Some(values) = app.values_of("GOAL") {
stop_triggers.extend(values.map(Self::trigger_parse));
}
app.apply_props(solve_command.get_context());
app.accept_selectors(&["SEMANTICS", "MAX_STEPS"]);
Self { solve_command, start_triggers, stop_triggers }
}
pub fn new_command(app: &mut App) -> Box<dyn Command> {
app.set_mode("Go");
Box::new(Self::new(app))
}
}
impl Command for Go {
fn name_of_log_file(&self) -> String {
self.solve_command.name_of_log_file()
}
fn console_level(&self) -> Option<log::LevelFilter> {
self.solve_command.console_level()
}
fn run(&mut self) -> Result<(), Box<dyn Error>> {
self.solve_command.run()?;
let ces = self.solve_command.get_ces();
if let Some(fset) = ces.get_firing_set() {
let mut runner = Runner::new(
ces.get_context(),
self.start_triggers.iter().map(|(name, mul)| (name, *mul)),
);
if !self.stop_triggers.is_empty() {
runner =
runner.with_goal(self.stop_triggers.iter().map(|(name, mul)| (name, *mul)))?;
}
println!("{}", "Go from".bright_green().bold());
println!("{} {}", "=>".bright_yellow().bold(), runner.get_initial_state());
let stop_condition = runner.go(fset)?;
let fcs = runner.get_firing_sequence();
let mut state = runner.get_initial_state().clone();
for (i, fc) in fcs.iter(fset).enumerate() {
if i > 0 {
println!("{} {}", "=>".bright_yellow().bold(), state);
}
println!(
"{}. {}",
format!("{:4}", (i + 1)).bright_yellow().bold(),
fc.with(ces.get_context())
);
fc.fire(&mut state)?;
}
if let Some(num_steps) = match stop_condition {
StopCondition::GoalReached(node_id, num_steps) => {
print!(
"{} reached (node \"{}\")",
"Goal".bright_cyan().bold(),
node_id.with(ces.get_context()),
);
Some(num_steps)
}
StopCondition::Stalemate(num_steps) => {
print!("{}", "Stuck".bright_red().bold());
Some(num_steps)
}
StopCondition::Pause(num_steps) => {
print!("{}", "Paused".bright_green().bold());
Some(num_steps)
}
StopCondition::UnimplementedFeature(feature) => {
println!(
"{}: {} isn't implemented yet.",
"Failed".bright_red().bold(),
feature
);
None
}
} {
println!(" after {} steps at", num_steps);
println!("{} {}", "=>".bright_yellow().bold(), state);
}
} else {
println!("{}.", "Structural deadlock".bright_red().bold());
}
Ok(())
}
}