summaryrefslogtreecommitdiffstats
path: root/CLI/rust/src/game_of_life/opts/validators.rs
blob: 5ea37e29adf53f59073f725415980b366b4af393 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! Clap validators module
//!
//! Used for validating different CLI args (like width, height and delay)
//! Ref: https://docs.rs/clap/3.0.0-beta.2/clap/struct.Arg.html#method.validator

use terminal_size::{terminal_size, Height, Width};

#[cfg(test)]
use mocktopus::macros::mockable;

/// An internal method used to allow mocking terminal size in tests
#[cfg_attr(test, mockable)]
fn terminal_size_wrapper() -> (Width, Height) {
    terminal_size().unwrap()
}

/// Validator to check if width configuration can be used
///
/// The maximum width allowed is the current terminal window width
pub(crate) fn is_fitting_term_width(val: &str) -> Result<(), String> {
    let (Width(term_width), Height(_)) = terminal_size_wrapper();
    match val.parse::<u16>() {
        Ok(n) if n > 0 && n <= term_width => Ok(()),
        Ok(_) => Err(format!(
            "must be between 1 and {} (current terminal width)",
            term_width
        )),
        Err(e) => Err(e.to_string()),
    }
}

#[cfg(test)]
mod is_fitting_term_width {
    use super::*;
    use mocktopus::mocking::*;

    #[test]
    fn positive_value() {
        terminal_size_wrapper.mock_safe(|| MockResult::Return((Width(42), Height(42))));
        assert!(is_fitting_term_width("13").is_ok());
    }

    #[test]
    fn value_too_large() {
        terminal_size_wrapper.mock_safe(|| MockResult::Return((Width(42), Height(42))));
        assert!(is_fitting_term_width("1337").is_err());
    }

    #[test]
    fn negative_value() {
        terminal_size_wrapper.mock_safe(|| MockResult::Return((Width(42), Height(42))));
        assert!(is_fitting_term_width("-13").is_err());
    }

    #[test]
    fn string_value() {
        terminal_size_wrapper.mock_safe(|| MockResult::Return((Width(42), Height(42))));
        assert!(is_fitting_term_width("not a number").is_err());
    }
}

/// Validator to check if height configuration can be used
///
/// The maximum height allowed is the current terminal window height - 2
/// The last 2 lines are used for status reporting (seed/input) and space
/// for ease of use
pub(crate) fn is_fitting_term_height(val: &str) -> Result<(), String> {
    let (Width(_), Height(term_height)) = terminal_size_wrapper();
    let term_height = term_height - 2;
    match val.parse::<u16>() {
        Ok(n) if n > 0 && n <= term_height => Ok(()),
        Ok(_) => Err(format!(
            "must be between 1 and {} (current terminal height)",
            term_height
        )),
        Err(e) => Err(e.to_string()),
    }
}

#[cfg(test)]
mod is_fitting_term_height {
    use super::*;
    use mocktopus::mocking::*;

    #[test]
    fn positive_value() {
        terminal_size_wrapper.mock_safe(|| MockResult::Return((Width(42), Height(42))));
        assert!(is_fitting_term_height("13").is_ok());
    }

    #[test]
    fn value_too_large() {
        terminal_size_wrapper.mock_safe(|| MockResult::Return((Width(42), Height(42))));
        assert!(is_fitting_term_height("1337").is_err());
    }

    #[test]
    fn negative_value() {
        terminal_size_wrapper.mock_safe(|| MockResult::Return((Width(42), Height(42))));
        assert!(is_fitting_term_height("-13").is_err());
    }

    #[test]
    fn string_value() {
        terminal_size_wrapper.mock_safe(|| MockResult::Return((Width(42), Height(42))));
        assert!(is_fitting_term_height("not a number").is_err());
    }
}

/// Validator to check if delay value is positive
pub(crate) fn is_positive(val: &str) -> Result<(), String> {
    match val.parse::<i16>() {
        Ok(n) if n.is_positive() => Ok(()),
        Ok(_) => Err(String::from("must be postive value (so far)")),
        Err(e) => Err(e.to_string()),
    }
}

#[cfg(test)]
mod is_positive {
    use super::*;

    #[test]
    fn positive_value() {
        assert!(is_positive("1337").is_ok());
    }

    #[test]
    fn negative_value() {
        assert!(is_positive("-1337").is_err());
    }

    #[test]
    fn string_value() {
        assert!(is_positive("not a number").is_err());
    }
}