1use std::error::Error;
2use ascesis::{Contextual, Runner, StopCondition, Multiplicity};
3use super::{App, Command, Solve, Styled};
4
5pub struct Go {
6 solve_command: Solve,
7 start_triggers: Vec<(String, Multiplicity)>,
8 stop_triggers: Vec<(String, Multiplicity)>,
9}
10
11impl Go {
12 fn trigger_parse<S: AsRef<str>>(name: S) -> (String, Multiplicity) {
13 let name = name.as_ref();
14
15 if let Some(pos) = name.rfind(':') {
16 if pos > 0 && pos < name.len() {
17 if let Ok(weight) = name[pos + 1..].parse::<Multiplicity>() {
18 return (name[..pos].to_owned(), weight)
19 }
20 }
21 }
22
23 (name.to_owned(), Multiplicity::one())
24 }
25
26 pub(crate) fn new(app: &mut App) -> Self {
27 let solve_command = Solve::new(app);
28 let mut start_triggers = Vec::new();
29 let mut stop_triggers = Vec::new();
30
31 if let Some(values) = app.values_of("START") {
32 start_triggers.extend(values.map(Self::trigger_parse));
33 }
34
35 if let Some(values) = app.values_of("GOAL") {
36 stop_triggers.extend(values.map(Self::trigger_parse));
37 }
38
39 app.apply_props(solve_command.get_context());
40 app.accept_selectors(&["SEMANTICS", "MAX_STEPS"]);
41
42 Self { solve_command, start_triggers, stop_triggers }
43 }
44
45 pub fn new_command(app: &mut App) -> Box<dyn Command> {
53 app.set_mode("Go");
54
55 Box::new(Self::new(app))
56 }
57}
58
59impl Command for Go {
60 fn name_of_log_file(&self) -> String {
61 self.solve_command.name_of_log_file()
62 }
63
64 fn console_level(&self) -> Option<log::LevelFilter> {
65 self.solve_command.console_level()
66 }
67
68 fn run(&mut self) -> Result<(), Box<dyn Error>> {
69 self.solve_command.run()?;
70
71 let ces = self.solve_command.get_ces();
72
73 if let Some(fset) = ces.get_firing_set() {
74 let mut runner = Runner::new(
75 ces.get_context(),
76 self.start_triggers.iter().map(|(name, mul)| (name, *mul)),
77 );
78
79 if !self.stop_triggers.is_empty() {
80 runner =
81 runner.with_goal(self.stop_triggers.iter().map(|(name, mul)| (name, *mul)))?;
82 }
83
84 println!("{}", "Go from".bright_green().bold());
85 println!("{} {}", "=>".bright_yellow().bold(), runner.get_initial_state());
86
87 let stop_condition = runner.go(fset)?;
88
89 let fcs = runner.get_firing_sequence();
90 let mut state = runner.get_initial_state().clone();
91
92 for (i, fc) in fcs.iter(fset).enumerate() {
93 if i > 0 {
94 println!("{} {}", "=>".bright_yellow().bold(), state);
95 }
96 println!(
97 "{}. {}",
98 format!("{:4}", (i + 1)).bright_yellow().bold(),
99 fc.with(ces.get_context())
100 );
101
102 fc.fire(&mut state)?;
103 }
104
105 if let Some(num_steps) = match stop_condition {
106 StopCondition::GoalReached(node_id, num_steps) => {
107 print!(
108 "{} reached (node \"{}\")",
109 "Goal".bright_cyan().bold(),
110 node_id.with(ces.get_context()),
111 );
112 Some(num_steps)
113 }
114 StopCondition::Stalemate(num_steps) => {
115 print!("{}", "Stuck".bright_red().bold());
116 Some(num_steps)
117 }
118 StopCondition::Pause(num_steps) => {
119 print!("{}", "Paused".bright_green().bold());
120 Some(num_steps)
121 }
122 StopCondition::UnimplementedFeature(feature) => {
123 println!(
124 "{}: {} isn't implemented yet.",
125 "Failed".bright_red().bold(),
126 feature
127 );
128 None
129 }
130 } {
131 println!(" after {} steps at", num_steps);
132 println!("{} {}", "=>".bright_yellow().bold(), state);
133 }
134 } else {
135 println!("{}.", "Structural deadlock".bright_red().bold());
136 }
137
138 Ok(())
139 }
140}