summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authora14m <[email protected]>2021-04-03 00:34:06 +0200
committera14m <[email protected]>2021-04-03 00:34:06 +0200
commit4c8b603efc65b33852e3a0aee69637b304e5ff4a (patch)
tree5de2c0067bbf6162d246cc386760c90c6d892544
parent05c9bc14148e318a83f702888799b0f61c7d765f (diff)
Add the Opts struct and parse CLI args into struct w/validation
And add the testing of the opts struct and from initializers
-rw-r--r--CLI/rust/src/game_of_life/opts.rs166
1 files changed, 166 insertions, 0 deletions
diff --git a/CLI/rust/src/game_of_life/opts.rs b/CLI/rust/src/game_of_life/opts.rs
index a5da4de..f32c4a8 100644
--- a/CLI/rust/src/game_of_life/opts.rs
+++ b/CLI/rust/src/game_of_life/opts.rs
@@ -1 +1,167 @@
+use clap::{ArgMatches, Error};
+use rand::{thread_rng, Rng};
+use terminal_size::{terminal_size, Height, Width};
+
+#[cfg(test)]
+use mocktopus::macros::mockable;
+
pub(crate) mod validators;
+
+#[derive(Debug, Clone)]
+pub(crate) struct Opts {
+ pub seed: usize,
+ pub input: Option<String>,
+ pub width: isize,
+ pub height: isize,
+ pub live_cell: char,
+ pub dead_cell: char,
+ pub delay: usize,
+}
+
+#[cfg_attr(test, mockable)]
+impl Opts {
+ pub fn from(args: ArgMatches) -> Result<Opts, Error> {
+ // Set the seed to a random generated number as default if not provided
+ let rnd = thread_rng().gen_range(0..1_000_000);
+ let seed = args.value_of_t::<usize>("seed").unwrap_or(rnd);
+
+ // Coerce the input to Options<String> from args
+ let input = match args.value_of("input") {
+ Some(val) => Some(String::from(val)),
+ None => None,
+ };
+
+ // Set the width and height to the maximum terminal size possible when not provided
+ let (Width(term_width), Height(term_height)) = terminal_size().unwrap();
+ let max_width: isize = term_width as isize;
+ let max_height: isize = term_height as isize - 2;
+ let width = args.value_of_t::<isize>("width").unwrap_or(max_width);
+ let height = args.value_of_t::<isize>("height").unwrap_or(max_height);
+
+ // Unwrap the defaults of the other arguments and set them
+ let live_cell = args.value_of_t::<char>("live-cell")?;
+ let dead_cell = args.value_of_t::<char>("dead-cell")?;
+ if live_cell == dead_cell {
+ return Err(Error::with_description(
+ String::from(
+ "The arguments '--live-cell', '--dead-cell' must have different values\n",
+ ),
+ clap::ErrorKind::InvalidValue,
+ ));
+ }
+
+ // unwrap the delay
+ let delay = args.value_of_t::<usize>("delay")?;
+
+ Ok(Opts {
+ seed,
+ input,
+ width,
+ height,
+ live_cell,
+ dead_cell,
+ delay,
+ })
+ }
+}
+
+#[cfg(test)]
+mod from {
+ use super::*;
+ use crate::game_of_life;
+
+ #[test]
+ fn no_options() {
+ let (Width(term_width), Height(term_height)) = terminal_size().unwrap();
+ let matches = game_of_life::app().try_get_matches_from(vec![""]).unwrap();
+ let opts = Opts::from(matches).unwrap();
+ assert!(matches!(opts.seed, 0..=1_000_000));
+ assert_eq!(opts.input, None);
+ assert_eq!(opts.width, term_width as isize);
+ assert_eq!(opts.height, term_height as isize - 2);
+ assert_eq!(opts.live_cell, '█');
+ assert_eq!(opts.dead_cell, ' ');
+ assert_eq!(opts.delay, 50);
+ }
+
+ #[test]
+ fn invalid_seed() {
+ let matches = game_of_life::app()
+ .try_get_matches_from(vec!["", "--seed=leet"])
+ .unwrap();
+ let opts = Opts::from(matches).unwrap();
+ assert!(matches!(opts.seed, 0..=1_000_000));
+ }
+
+ #[test]
+ fn seed() {
+ let matches = game_of_life::app()
+ .try_get_matches_from(vec!["", "--seed=1337"])
+ .unwrap();
+ let opts = Opts::from(matches).unwrap();
+ assert_eq!(opts.seed, 1337);
+ }
+
+ #[test]
+ fn input() {
+ let matches = game_of_life::app()
+ .try_get_matches_from(vec!["", "--input=file"])
+ .unwrap();
+ let opts = Opts::from(matches).unwrap();
+ assert_eq!(opts.input, Some(String::from("file")));
+ }
+
+ #[test]
+ fn width() {
+ let matches = game_of_life::app()
+ .try_get_matches_from(vec!["", "--width=13"])
+ .unwrap();
+ let opts = Opts::from(matches).unwrap();
+ assert_eq!(opts.width, 13);
+ }
+
+ #[test]
+ fn height() {
+ let matches = game_of_life::app()
+ .try_get_matches_from(vec!["", "--height=13"])
+ .unwrap();
+ let opts = Opts::from(matches).unwrap();
+ assert_eq!(opts.height, 13);
+ }
+
+ #[test]
+ fn live_cell() {
+ let matches = game_of_life::app()
+ .try_get_matches_from(vec!["", "--live-cell=l"])
+ .unwrap();
+ let opts = Opts::from(matches).unwrap();
+ assert_eq!(opts.live_cell, 'l');
+ }
+
+ #[test]
+ fn dead_cell() {
+ let matches = game_of_life::app()
+ .try_get_matches_from(vec!["", "--dead-cell=d"])
+ .unwrap();
+ let opts = Opts::from(matches).unwrap();
+ assert_eq!(opts.dead_cell, 'd');
+ }
+
+ #[test]
+ fn same_live_and_dead_cells() {
+ let matches = game_of_life::app()
+ .try_get_matches_from(vec!["", "--live-cell=x", "--dead-cell=x"])
+ .unwrap();
+ let opts = Opts::from(matches);
+ assert!(opts.is_err());
+ }
+
+ #[test]
+ fn delay() {
+ let matches = game_of_life::app()
+ .try_get_matches_from(vec!["", "--delay=13"])
+ .unwrap();
+ let opts = Opts::from(matches).unwrap();
+ assert_eq!(opts.delay, 13);
+ }
+}