1use std::{process, str::FromStr};
2
3use http::{HttpMethod, Result};
4
5#[derive(Debug, Clone)]
6pub enum OutFile {
7 Stdout,
8 Filename(String),
9 GetFromUrl,
10}
11
12#[derive(Clone, Debug)]
13pub struct ClientConfig {
14 pub url: String,
15 pub method: HttpMethod,
16 pub host: String,
17 pub port: u16,
18 pub user_agent: String,
19 pub out_file: OutFile,
20}
21
22impl ClientConfig {
23 pub fn parse<I>(args: I) -> Result<Self>
25 where
26 I: Iterator<Item = String>,
27 {
28 let mut conf = Self::default();
29 let mut args = args.into_iter();
30
31 while let Some(arg) = args.next() {
32 macro_rules! parse_next {
33 () => {
34 args.next_parse().ok_or_else(|| {
35 format!("Missing or incorrect argument for \"{}\"", arg.as_str())
36 })?
37 };
38 (as $t:ty) => {{
39 let _next: $t = parse_next!();
40 _next
41 }};
42 }
43
44 match arg.as_str() {
45 "-m" | "--method" => conf.method = parse_next!(),
46 "--host" => conf.host = parse_next!(),
47 "-a" | "--user-agent" => conf.user_agent = parse_next!(),
48 "-h" | "--help" => help(),
49 "-O" => conf.out_file = OutFile::GetFromUrl,
50 "-o" => conf.out_file = OutFile::Filename(parse_next!()),
51 _ => conf.url = arg,
52 }
53 }
54
55 let mut host = conf.url.as_str();
56 host = conf.url.strip_prefix("http://").unwrap_or(host);
57
58 let mut url = "/";
59
60 if let Some(i) = host.find('/') {
61 url = &host[i..];
62 host = &host[..i];
63 }
64
65 if let Some(i) = host.find(':') {
66 conf.port = host[i + 1..].parse()?;
67 host = &host[..i];
68 }
69
70 conf.host = host.to_string();
71 conf.url = url.to_string();
72
73 if conf.host.is_empty() {
74 return Err("Missing host".into());
75 }
76
77 Ok(conf)
78 }
79}
80
81fn help() -> ! {
82 println!("TODO");
83 process::exit(0);
84}
85
86trait ParseIterator {
87 fn next_parse<T: FromStr>(&mut self) -> Option<T>;
88}
89
90impl<I> ParseIterator for I
91where
92 I: Iterator<Item = String>,
93{
94 fn next_parse<T: FromStr>(&mut self) -> Option<T> {
95 self.next()?.parse().ok()
96 }
97}
98
99impl Default for ClientConfig {
100 #[inline]
107 fn default() -> Self {
108 Self {
109 method: HttpMethod::GET,
110 url: String::new(),
111 port: 80,
112 host: String::new(),
113 user_agent: "http-client".to_string(),
114 out_file: OutFile::Stdout,
115 }
116 }
117}
118
119#[cfg(test)]
120mod test {
121 #![allow(clippy::unwrap_used)]
122
123 use super::ClientConfig;
124 use http::Result;
125
126 fn parse_from_vec(v: &[&str]) -> Result<ClientConfig> {
127 let conf = v.iter().map(|s| (*s).to_string());
128 ClientConfig::parse(conf)
129 }
130
131 #[test]
132 fn unknown() {
133 let conf = vec!["?unknown"];
134 let conf = parse_from_vec(&conf).unwrap();
135 assert_eq!(conf.host, "?unknown");
136 }
137}