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
/// A block, consisting of multiple commands.
#[derive(Debug, PartialEq)]
#[non_exhaustive]
pub(crate) struct Block {
    /// The commands in the block.
    pub commands: Vec<Command>,
    /// The literal string of the input commands. Used to generate the output.
    pub literal: String,
    /// The block's line number position in the script.
    pub line_number: u32,
}

/// A command.
#[derive(Debug, PartialEq)]
#[non_exhaustive]
pub struct Command {
    /// The name of the command. Never empty.
    pub name: String,
    /// The command's arguments, in the given order.
    pub args: Vec<Argument>,
    /// The command prefix, if given.
    pub prefix: Option<String>,
    /// Silences the output of this command. This is handled automatically, the
    /// [`Runner`](crate::Runner) does not have to take this into account.
    pub silent: bool,
    /// The command's line number position in the script.
    pub line_number: u32,
}

/// A command argument.
#[derive(Debug, PartialEq)]
#[non_exhaustive]
pub struct Argument {
    /// The argument key, for `key=value` style arguments. Not guaranteed to be
    /// unique, the [`Runner`](crate::Runner) can handle this as desired.
    pub key: Option<String>,
    /// The argument value. Can be empty.
    pub value: String,
}

impl Argument {
    /// Parses the argument value as a T using core::str::parse(). Convenience
    /// method that returns a string error to ease error handling in a
    /// [`Runner`](crate::Runner).
    pub fn parse<T>(&self) -> Result<T, String>
    where
        T: std::str::FromStr,
        <T as std::str::FromStr>::Err: std::fmt::Display,
    {
        self.value.parse().map_err(|e| format!("invalid argument '{}': {e}", self.value))
    }
}

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

    /// Basic tests of Argument.parse(). Not comprehensive, since it dispatches
    /// to core::str::parse().
    #[test]
    fn argument_parse() {
        macro_rules! arg {
            ($s:expr) => {
                Argument { key: None, value: $s.to_string() }
            };
        }

        assert_eq!(arg!("-1").parse(), Ok(-1_i64));
        assert_eq!(arg!("0").parse(), Ok(0_i64));
        assert_eq!(arg!("1").parse(), Ok(1_i64));
        assert_eq!(
            arg!("").parse::<i64>(),
            Err("invalid argument '': cannot parse integer from empty string".to_string())
        );
        assert_eq!(
            arg!("foo").parse::<i64>(),
            Err("invalid argument 'foo': invalid digit found in string".to_string())
        );

        assert_eq!(arg!("false").parse(), Ok(false));
        assert_eq!(arg!("true").parse(), Ok(true));
        assert_eq!(
            arg!("").parse::<bool>(),
            Err("invalid argument '': provided string was not `true` or `false`".to_string())
        );
    }
}