transip_command/
lib.rs

1use std::str::FromStr;
2
3pub use clap::error::ErrorKind;
4use clap::{Error, Parser, Subcommand, ValueEnum};
5
6#[derive(Clone, Debug, ValueEnum)]
7#[value(rename_all = "UPPER")]
8pub enum RecordType {
9    A,
10    AAAA,
11    CNAME,
12    MX,
13    NS,
14    TXT,
15    SRV,
16}
17
18#[derive(Clone, Debug, ValueEnum, PartialEq)]
19pub enum OnError {
20    Print,
21    Exit,
22}
23
24#[derive(Clone, Debug, Parser)]
25pub struct DnsEntry {
26    pub domain: String,
27    pub name: String,
28    pub ttl: u32,
29    pub r#type: RecordType,
30    pub content: String,
31}
32
33#[cfg(feature = "propagation")]
34#[derive(Debug, Subcommand)]
35pub enum DnsCommand {
36    AcmeValidationDelete { domain: String },
37    AcmeValidationSet { domain: String, challenge: String },
38    AcmeValidationCheck { domain: String, challenge: String },
39    Delete(DnsEntry),
40    Insert(DnsEntry),
41    List { domain: String },
42}
43
44#[cfg(not(feature = "propagation"))]
45#[derive(Debug, Subcommand)]
46pub enum DnsCommand {
47    AcmeValidationDelete { domain: String },
48    AcmeValidationSet { domain: String, challenge: String },
49    Delete(DnsEntry),
50    Insert(DnsEntry),
51    List { domain: String },
52}
53
54#[derive(Debug, Subcommand)]
55pub enum DomainCommand {
56    List,
57    Item { domain: String },
58}
59
60#[derive(Debug, Subcommand)]
61pub enum InvoiceCommand {
62    List,
63    Item { number: String },
64    Pdf { number: String },
65}
66
67#[derive(Debug, Subcommand)]
68pub enum ProductCommand {
69    List,
70    Elements { name: String },
71}
72
73#[derive(Debug, Subcommand)]
74pub enum EmailBoxCommand {
75    List {
76        domain: String,
77    },
78    Item {
79        domain: String,
80        id: String,
81    },
82    Delete {
83        domain: String,
84        id: String,
85    },
86    Insert {
87        domain: String,
88        username: String,
89        password: String,
90    },
91}
92
93#[derive(Debug, Subcommand)]
94pub enum EmailForwardCommand {
95    List {
96        domain: String,
97    },
98    Item {
99        domain: String,
100        id: String,
101    },
102    Delete {
103        domain: String,
104        id: String,
105    },
106    Insert {
107        domain: String,
108        local_part: String,
109        forward_to: String,
110    },
111}
112
113#[derive(Debug, Subcommand)]
114pub enum VpsCommand {
115    List,
116    Item { name: String },
117    Start { name: String },
118    Stop { name: String },
119    Reset { name: String },
120    Lock { name: String },
121    Unlock { name: String },
122}
123
124#[derive(Debug, Subcommand)]
125pub enum SubCommand {
126    AvailibilityZones,
127    Comment {
128        text: String,
129    },
130    #[command(subcommand)]
131    Dns(DnsCommand),
132    #[command(subcommand)]
133    Domain(DomainCommand),
134    #[command(subcommand)]
135    EmailBox(EmailBoxCommand),
136    #[command(subcommand)]
137    EmailForward(EmailForwardCommand),
138    #[command(subcommand)]
139    Invoice(InvoiceCommand),
140    Onerror {
141        on_error: OnError,
142    },
143    Ping,
144    #[command(subcommand)]
145    Product(ProductCommand),
146    Sleep {
147        number_of_seconds: u64,
148    },
149    #[command(subcommand)]
150    Vps(VpsCommand),
151}
152
153#[derive(Debug, Parser)]
154#[command(multicall = true)]
155pub struct TransipCommand {
156    #[command(subcommand)]
157    pub command: SubCommand,
158}
159
160fn command_line<S: AsRef<str>>(line: S) -> Result<Vec<String>, clap::Error> {
161    if line.as_ref().trim_start().starts_with("#") {
162        Ok(vec!["comment".to_owned(), line.as_ref().to_owned()])
163    } else {
164        shlex::split(line.as_ref()).ok_or(clap::Error::new(clap::error::ErrorKind::Format))
165    }
166}
167
168impl FromStr for TransipCommand {
169    type Err = Error;
170
171    fn from_str(s: &str) -> Result<Self, Self::Err> {
172        command_line(s).and_then(TransipCommand::try_parse_from)
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use std::io::{BufRead, BufReader};
179
180    use super::{command_line, TransipCommand};
181    use clap::Parser;
182
183    const COMMANDS: &[u8] = include_bytes!("commands.txt");
184
185    #[test]
186    fn try_command_lines() {
187        let lines = BufReader::new(COMMANDS).lines();
188        for args_option in lines.map_while(Result::ok).map(command_line) {
189            match args_option {
190                Ok(args) => {
191                    let result = TransipCommand::try_parse_from(args).unwrap();
192                    println!("{:?}", &result.command);
193                }
194                Err(error) => eprintln!("Error parsing line: {error}"),
195            }
196        }
197    }
198}