1 //! Test commands.
2 //!
3 //! A `.clif` file can begin with one or more *test commands* which specify what is to be tested.
4 //! The general syntax is:
5 //!
6 //! <pre>
7 //! test <i>&lt;command&gt;</i> <i>[options]</i>...
8 //! </pre>
9 //!
10 //! The options are either a single identifier flag, or setting values like `identifier=value`.
11 //!
12 //! The parser does not understand the test commands or which options are valid. It simply parses
13 //! the general format into a `TestCommand` data structure.
14 
15 use std::fmt::{self, Display, Formatter};
16 
17 /// A command appearing in a test file.
18 #[derive(Clone, PartialEq, Eq, Debug)]
19 pub struct TestCommand<'a> {
20     /// The command name as a string.
21     pub command: &'a str,
22     /// The options following the command name.
23     pub options: Vec<TestOption<'a>>,
24 }
25 
26 /// An option on a test command.
27 #[derive(Clone, PartialEq, Eq, Debug)]
28 pub enum TestOption<'a> {
29     /// Single identifier flag: `foo`.
30     Flag(&'a str),
31     /// A value assigned to an identifier: `foo=bar`.
32     Value(&'a str, &'a str),
33 }
34 
35 impl<'a> TestCommand<'a> {
36     /// Create a new TestCommand by parsing `s`.
37     /// The returned command contains references into `s`.
38     pub fn new(s: &'a str) -> Self {
39         let mut parts = s.split_whitespace();
40         let cmd = parts.next().unwrap_or("");
41         Self {
42             command: cmd,
43             options: parts
44                 .filter(|s| !s.is_empty())
45                 .map(TestOption::new)
46                 .collect(),
47         }
48     }
49 }
50 
51 impl<'a> Display for TestCommand<'a> {
52     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
53         write!(f, "{}", self.command)?;
54         for opt in &self.options {
55             write!(f, " {opt}")?;
56         }
57         writeln!(f)
58     }
59 }
60 
61 impl<'a> TestOption<'a> {
62     /// Create a new TestOption by parsing `s`.
63     /// The returned option contains references into `s`.
64     pub fn new(s: &'a str) -> Self {
65         match s.find('=') {
66             None => TestOption::Flag(s),
67             Some(p) => TestOption::Value(&s[0..p], &s[p + 1..]),
68         }
69     }
70 }
71 
72 impl<'a> Display for TestOption<'a> {
73     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
74         match *self {
75             TestOption::Flag(s) => write!(f, "{s}"),
76             TestOption::Value(s, v) => write!(f, "{s}={v}"),
77         }
78     }
79 }
80 
81 #[cfg(test)]
82 mod tests {
83     use super::*;
84 
85     #[test]
86     fn parse_option() {
87         assert_eq!(TestOption::new(""), TestOption::Flag(""));
88         assert_eq!(TestOption::new("foo"), TestOption::Flag("foo"));
89         assert_eq!(TestOption::new("foo=bar"), TestOption::Value("foo", "bar"));
90     }
91 
92     #[test]
93     fn parse_command() {
94         assert_eq!(&TestCommand::new("").to_string(), "\n");
95         assert_eq!(&TestCommand::new("cat").to_string(), "cat\n");
96         assert_eq!(&TestCommand::new("cat  ").to_string(), "cat\n");
97         assert_eq!(&TestCommand::new("cat  1  ").to_string(), "cat 1\n");
98         assert_eq!(
99             &TestCommand::new("cat  one=4   two t").to_string(),
100             "cat one=4 two t\n"
101         );
102     }
103 }
104