···
3
+
net::{AddrParseError, SocketAddr},
7
+
#[derive(Debug, PartialEq, Eq)]
10
+
Host { boats: PathBuf, addr: SocketAddr },
11
+
Join { boats: PathBuf, addr: SocketAddr },
14
+
#[derive(Debug, PartialEq, Eq)]
15
+
pub enum ArgsParseError {
17
+
InvalidMode(String),
18
+
InvalidAddr(AddrParseError),
21
+
impl Error for ArgsParseError {
22
+
fn source(&self) -> Option<&(dyn Error + 'static)> {
24
+
ArgsParseError::NotEnoughArguments => None,
25
+
ArgsParseError::InvalidMode(_) => None,
26
+
ArgsParseError::InvalidAddr(e) => Some(e),
31
+
impl std::fmt::Display for ArgsParseError {
32
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34
+
ArgsParseError::NotEnoughArguments => write!(f, "not enough arguments"),
35
+
ArgsParseError::InvalidMode(mode) => write!(f, "invalid mode: '{mode}'"),
36
+
ArgsParseError::InvalidAddr(e) => write!(f, "error parsing address: {e}"),
42
+
pub fn parse<S: AsRef<str>>(iter: impl IntoIterator<Item = S>) -> Result<Self, ArgsParseError> {
43
+
let mut iter = iter.into_iter();
44
+
let Some(arg) = iter.next() else {
45
+
return Ok(Self::Help);
48
+
if matches!(arg.as_ref(), "-h" | "--help") {
49
+
return Ok(Self::Help);
52
+
let boats = PathBuf::from(arg.as_ref());
53
+
let arg = iter.next().ok_or(ArgsParseError::NotEnoughArguments)?;
57
+
.ok_or(ArgsParseError::NotEnoughArguments)?
59
+
.parse::<SocketAddr>()
60
+
.map_err(ArgsParseError::InvalidAddr)?;
62
+
match arg.as_ref() {
63
+
"host" => Ok(Self::Host { boats, addr }),
64
+
"join" => Ok(Self::Join { boats, addr }),
65
+
a => Err(ArgsParseError::InvalidMode(a.to_string())),
72
+
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
77
+
fn parse_nothing() {
78
+
assert_eq!(Args::parse::<String>([]), Ok(Args::Help));
83
+
assert_eq!(Args::parse(["-h"]), Ok(Args::Help));
84
+
assert_eq!(Args::parse(["--help"]), Ok(Args::Help));
90
+
Args::parse(["./my_boats", "host", "127.0.0.1:8080"]),
92
+
boats: "./my_boats".into(),
93
+
addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8080),
97
+
Args::parse(["./my_boats", "host", "0.0.0.0:8080"]),
99
+
boats: "./my_boats".into(),
100
+
addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 8080),
104
+
Args::parse(["./my_boats", "host", "[::1]:8080"]),
106
+
boats: "./my_boats".into(),
107
+
addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 8080),
111
+
Args::parse(["./my_boats", "host", "[::]:8080"]),
113
+
boats: "./my_boats".into(),
114
+
addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 8080),
122
+
Args::parse(["./my_boats", "join", "127.0.0.1:8080"]),
124
+
boats: "./my_boats".into(),
125
+
addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8080),
129
+
Args::parse(["./my_boats", "join", "0.0.0.0:8080"]),
131
+
boats: "./my_boats".into(),
132
+
addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 8080),
136
+
Args::parse(["./my_boats", "join", "[::1]:8080"]),
138
+
boats: "./my_boats".into(),
139
+
addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 8080),
143
+
Args::parse(["./my_boats", "join", "[::]:8080"]),
145
+
boats: "./my_boats".into(),
146
+
addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 8080),
152
+
fn parse_errors() {
154
+
Args::parse(["abcoabcuobacwa"]),
155
+
Err(ArgsParseError::NotEnoughArguments)
158
+
Args::parse(["./my_boats", "foobar"]),
159
+
Err(ArgsParseError::NotEnoughArguments)
162
+
Args::parse(["./my_boats", "foobar", "127.0.0.1:8080"]),
163
+
Err(ArgsParseError::InvalidMode("foobar".into()))
166
+
Args::parse(["./my_boats", "host", "blablabla"]),
167
+
Err(ArgsParseError::InvalidAddr(_))
170
+
Args::parse(["./my_boats", "host", "localhost"]),
171
+
Err(ArgsParseError::InvalidAddr(_))
174
+
Args::parse(["./my_boats", "host", "0.0.0.0"]),
175
+
Err(ArgsParseError::InvalidAddr(_))
178
+
Args::parse(["./my_boats", "join", "127.0.0.1"]),
179
+
Err(ArgsParseError::InvalidAddr(_))
182
+
Args::parse(["./my_boats", "join", "localhost"]),
183
+
Err(ArgsParseError::InvalidAddr(_))
186
+
Args::parse(["./my_boats", "join", "localhost:8080"]),
187
+
Err(ArgsParseError::InvalidAddr(_))