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
138
139
140
141
use crate::{
    check_environment,
    error::{Error, ErrorExt},
    str_extension::StrExtension,
};
use std::{
    fmt::Display,
    str::{FromStr, SplitAsciiWhitespace},
};

pub type DomainName = String;

const ITEM: &str = "item";
const LIST: &str = "list";

#[derive(Debug, PartialEq)]
pub enum DomainCommand {
    /// # Example
    ///
    /// ```
    /// use transip_command::{DomainCommand, TransipCommand};
    ///
    /// let commandline = "domain list";
    /// assert_eq!(
    ///     commandline.parse::<TransipCommand>().unwrap(),
    ///     TransipCommand::Domain(DomainCommand::List),
    /// );
    /// ```
    List,

    /// # Example
    ///
    /// ```
    /// use transip_command::{DomainCommand, TransipCommand};
    ///
    /// let commandline = "domain item oiwerhy.nl";
    /// assert_eq!(
    ///     commandline.parse::<TransipCommand>().unwrap(),
    ///     TransipCommand::Domain(
    ///         DomainCommand::Item(
    ///             "oiwerhy.nl".to_owned(),
    ///         )
    ///     ),
    /// );
    /// ```
    Item(DomainName),
}

impl Display for DomainCommand {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            DomainCommand::Item(item) => write!(f, "{} {}", ITEM, item),
            DomainCommand::List => write!(f, "{}", LIST),
        }
    }
}

impl FromStr for DomainCommand {
    type Err = Error;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        if s.trim() == LIST {
            return Ok(DomainCommand::List);
        }
        if let Some(domain_name) = s.one_param(ITEM) {
            Ok(DomainCommand::Item(check_environment(domain_name)?))
        } else {
            Err(Error::ParseDomainCommand(s.to_owned()))
        }
    }
}

impl<'a> TryFrom<SplitAsciiWhitespace<'a>> for DomainCommand {
    type Error = Error;

    fn try_from(mut value: SplitAsciiWhitespace<'a>) -> Result<Self, Self::Error> {
        let first = value.next();
        if first == Some(LIST) {
            if value.next().is_none() {
                return Ok(DomainCommand::List);
            } else {
                return Err(Error::ParseDomainCommand(
                    "domain item takes no parameters".to_owned(),
                ));
            }
        }
        if first == Some(ITEM) {
            let second = value
                .next()
                .ok_or(Error::ParseDnsCommand("no domain name".to_owned()))?;
            if value.next().is_none() {
                return check_environment(second)
                    .err_into()
                    .map(DomainCommand::Item);
            } else {
                return Err(Error::ParseDomainCommand(
                    "domain item takes 1 parameter only".to_owned(),
                ));
            }
        }
        Err(Error::ParseDomainCommand(
            "unknown subcommand for domain. Please use list or item".to_owned(),
        ))
    }
}

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

    #[test]
    fn display() {
        assert_eq!(
            DomainCommand::Item("paulmin.nl".to_owned()).to_string(),
            "item paulmin.nl".to_owned(),
        );

        assert_eq!(DomainCommand::List.to_string(), "list".to_owned(),);
    }

    #[test]
    fn try_from() {
        assert_eq!(
            DomainCommand::try_from("  item   paulmin.nl   ".split_ascii_whitespace()).unwrap(),
            DomainCommand::Item("paulmin.nl".to_owned())
        );
    }

    #[test]
    fn from_str() {
        assert_eq!(
            "list".parse::<DomainCommand>().unwrap(),
            DomainCommand::List,
        );

        assert_eq!(
            "item paulmin.nl".parse::<DomainCommand>().unwrap(),
            DomainCommand::Item("paulmin.nl".to_owned()),
        );
    }
}