1use colored::*;
2use core::fmt::Display;
3use std::fs;
4pub struct Config {
5 pub input: String,
7 pub show_output: bool,
9 pub show_tape: bool,
11 pub start: String,
13 pub bound: Option<usize>,
15 pub iterations: u32,
17 pub debug: bool,
19}
20
21impl Display for Config {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 write!(
24 f,
25 "[ input: {};{}{} iterations: {} ]",
26 self.input,
27 match (self.show_output, self.show_tape) {
28 (true, true) => " show: output, tape;",
29 (true, false) => " show: output;",
30 (false, true) => " show: tape;",
31 _ => "",
32 },
33 match self.bound {
34 None => "".to_owned(),
35 Some(bound) => format!(" bound: {};", bound),
36 },
37 self.iterations,
38 )
39 }
40}
41
42impl Config {
43 pub fn display_run(&self) {
44 println!(
45 "Running the automata {} on input {}",
46 self.start, self.input
47 )
48 }
49}
50
51#[derive(Default)]
52pub struct ConfigBuilder {
53 input: Option<String>,
54 show_output: Option<bool>,
55 show_tape: Option<bool>,
56 start: Option<String>,
57 bound: Option<usize>,
58 iterations: Option<u32>,
59 debug: Option<bool>,
60 config_file: Option<String>,
61}
62
63impl ConfigBuilder {
64 pub fn set_input(&mut self, input: String) {
65 self.input = Some(input);
66 }
67 pub fn set_output(&mut self, show_output: bool) {
68 self.show_output = Some(show_output)
69 }
70 pub fn set_tape(&mut self, show_tape: bool) {
71 self.show_tape = Some(show_tape)
72 }
73 pub fn set_start(&mut self, start: String) {
74 self.start = Some(start);
75 }
76 pub fn set_bound(&mut self, bound: usize) {
77 self.bound = Some(bound);
78 }
79 pub fn set_iterations(&mut self, iterations: u32) {
80 self.iterations = Some(iterations);
81 }
82 pub fn set_debug(&mut self, debug: bool) {
83 self.debug = Some(debug);
84 }
85 pub fn set_config_file(&mut self, config_file: String) {
86 self.config_file = Some(config_file);
87 }
88 pub fn build(self) -> Config {
89 Config {
90 input: self.input.unwrap_or("".to_owned()),
91 show_output: self.show_output.unwrap_or(false),
92 show_tape: self.show_tape.unwrap_or(false),
93 start: self.start.unwrap_or("main".to_owned()),
94 bound: self.bound,
95 iterations: self.iterations.unwrap_or(1200),
96 debug: self.debug.unwrap_or(false),
97 }
98 }
99}
100
101fn display_warning(msg: &str) {
102 println!("{}", msg.yellow())
103}
104
105fn parse_flags(args: Vec<String>) -> ConfigBuilder {
106 let mut configuration = ConfigBuilder::default();
107 let mut flags = args.iter();
108
109 while let Some(flag) = flags.next() {
110 match flag.as_str() {
111 "-input" | "-i" => match flags.next() {
112 None => display_warning("Please provide an input to the Turing Machine!"),
113 Some(input) => configuration.set_input(input.to_owned()),
114 },
115 "-output" | "-o" => configuration.set_output(true),
116 "-tape" | "-t" => configuration.set_tape(true),
117 "-start" | "-s" => match flags.next() {
118 None => display_warning("Please provide the name of the Turing Machine to start!"),
119 Some(start) => configuration.set_start(start.to_owned()),
120 },
121 "-bound" | "-b" => match flags.next() {
122 None => display_warning("Please provide the upper bound of the tape!"),
123 Some(bound) => match bound.parse::<usize>() {
124 Err(_) => display_warning("Please provide a positive number for the bound!"),
125 Ok(cells) => configuration.set_bound(cells),
126 },
127 },
128 "-iterations" | "-iter" | "-limit" | "-l" => match flags.next() {
129 None => display_warning("Please provide the maximum number of tape iterations!"),
130 Some(steps) => match steps.parse::<u32>() {
131 Err(_) => {
132 display_warning("Please provide a positive number for the iterations!")
133 }
134 Ok(limit) => configuration.set_iterations(limit),
135 },
136 },
137 "-debug" | "-d" => configuration.set_debug(true),
138 "-config" | "-c" => match flags.next() {
139 None => display_warning("Please provide the path to a configuration file!"),
140
141 Some(config) => configuration.set_config_file(config.to_owned()),
142 },
143 _ => display_warning(format!("Unknown flag {}", flag).as_str()),
144 }
145 }
146 configuration
147}
148
149pub fn merge_configs(main_config: ConfigBuilder, secondary_config: ConfigBuilder) -> ConfigBuilder {
150 ConfigBuilder {
151 input: main_config.input.or(secondary_config.input),
152 show_output: main_config.show_output.or(secondary_config.show_output),
153 show_tape: main_config.show_tape.or(secondary_config.show_tape),
154 start: main_config.start.or(secondary_config.start),
155 bound: main_config.bound.or(secondary_config.bound),
156 iterations: main_config.iterations.or(secondary_config.iterations),
157 debug: main_config.debug.or(secondary_config.debug),
158 config_file: secondary_config.config_file,
159 }
160}
161
162pub fn parse_config(args: Vec<String>) -> Config {
163 let console_config = parse_flags(args);
164 match console_config.config_file {
165 None => console_config.build(),
166 Some(ref config_file) => {
167 let content = match fs::read_to_string(config_file.clone()) {
168 Ok(content) => content,
169 Err(_) => {
170 display_warning(format!("Cannot find config file {}!", config_file).as_str());
171 return console_config.build();
172 }
173 };
174 let file_config =
175 parse_flags(content.split_whitespace().map(|s| s.to_owned()).collect());
176 merge_configs(console_config, file_config).build()
177 }
178 }
179}