Skip to main content

amethyst/
config.rs

1use colored::*;
2use core::fmt::Display;
3use std::fs;
4pub struct Config {
5    // Starting configuration of the tape
6    pub input: String,
7    // Displays the tape content from the head to the first @ symbol
8    pub show_output: bool,
9    // Displays the entire tape
10    pub show_tape: bool,
11    // Which Turing machine to run from the .myst file
12    pub start: String,
13    // Memory bound for the tape
14    pub bound: Option<usize>,
15    // Maximum number of iterations since the Turing Machine may never halt
16    pub iterations: u32,
17    // Displays the current state of the Turing Machine
18    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}