···
4
+
net::{AddrParseError, SocketAddr},
8
+
#[derive(Debug, PartialEq, Eq)]
11
+
Host { boats: PathBuf, addr: SocketAddr },
12
+
Join { boats: PathBuf, addr: SocketAddr },
15
+
#[derive(Debug, PartialEq, Eq)]
16
+
pub enum ArgsParseError {
18
+
InvalidMode(String),
19
+
InvalidAddr(AddrParseError),
22
+
impl Error for ArgsParseError {
23
+
fn source(&self) -> Option<&(dyn Error + 'static)> {
25
+
ArgsParseError::NotEnoughArguments => None,
26
+
ArgsParseError::InvalidMode(_) => None,
27
+
ArgsParseError::InvalidAddr(e) => Some(e),
32
+
impl Display for ArgsParseError {
33
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35
+
ArgsParseError::NotEnoughArguments => write!(f, "not enough arguments"),
36
+
ArgsParseError::InvalidMode(mode) => write!(f, "invalid mode: '{mode}'"),
37
+
ArgsParseError::InvalidAddr(e) => write!(f, "error parsing address: {e}"),
43
+
pub fn parse<S: AsRef<str>>(iter: impl IntoIterator<Item = S>) -> Result<Self, ArgsParseError> {
44
+
let mut iter = iter.into_iter();
45
+
let Some(arg) = iter.next() else {
46
+
return Ok(Self::Help);
49
+
if matches!(arg.as_ref(), "-h" | "--help") {
50
+
return Ok(Self::Help);
53
+
let boats = PathBuf::from(arg.as_ref());
54
+
let arg = iter.next().ok_or(ArgsParseError::NotEnoughArguments)?;
58
+
.ok_or(ArgsParseError::NotEnoughArguments)?
60
+
.parse::<SocketAddr>()
61
+
.map_err(ArgsParseError::InvalidAddr)?;
63
+
match arg.as_ref() {
64
+
"host" => Ok(Self::Host { boats, addr }),
65
+
"join" => Ok(Self::Join { boats, addr }),
66
+
a => Err(ArgsParseError::InvalidMode(a.to_string())),
70
+
pub fn print_help(arg0: &str) {
71
+
println!("Battleship game!");
74
+
println!(" {arg0} [-h|--help]");
75
+
println!(" \tPrint help information and exit.");
76
+
println!(" {arg0} <BOATS_FILE> host <ADDRESS>:<PORT>");
77
+
println!(" \tHost a game. Use 127.0.0.1 for a local");
78
+
println!(" \t game. Use 0.0.0.0 to make it joinable by");
79
+
println!(" \t other hosts. IPv6 is also supported!");
80
+
println!(" {arg0} <BOATS_FILE> join <ADDRESS>:<PORT>");
81
+
println!(" \tJoin a game. IPv4 and IPv6 are supported!");
87
+
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
92
+
fn parse_nothing() {
93
+
assert_eq!(Args::parse::<String>([]), Ok(Args::Help));
98
+
assert_eq!(Args::parse(["-h"]), Ok(Args::Help));
99
+
assert_eq!(Args::parse(["--help"]), Ok(Args::Help));
105
+
Args::parse(["./my_boats", "host", "127.0.0.1:8080"]),
107
+
boats: "./my_boats".into(),
108
+
addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8080),
112
+
Args::parse(["./my_boats", "host", "0.0.0.0:8080"]),
114
+
boats: "./my_boats".into(),
115
+
addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 8080),
119
+
Args::parse(["./my_boats", "host", "[::1]:8080"]),
121
+
boats: "./my_boats".into(),
122
+
addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 8080),
126
+
Args::parse(["./my_boats", "host", "[::]:8080"]),
128
+
boats: "./my_boats".into(),
129
+
addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 8080),
137
+
Args::parse(["./my_boats", "join", "127.0.0.1:8080"]),
139
+
boats: "./my_boats".into(),
140
+
addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8080),
144
+
Args::parse(["./my_boats", "join", "0.0.0.0:8080"]),
146
+
boats: "./my_boats".into(),
147
+
addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 8080),
151
+
Args::parse(["./my_boats", "join", "[::1]:8080"]),
153
+
boats: "./my_boats".into(),
154
+
addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 8080),
158
+
Args::parse(["./my_boats", "join", "[::]:8080"]),
160
+
boats: "./my_boats".into(),
161
+
addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 8080),
167
+
fn parse_errors() {
169
+
Args::parse(["abcoabcuobacwa"]),
170
+
Err(ArgsParseError::NotEnoughArguments)
173
+
Args::parse(["./my_boats", "foobar"]),
174
+
Err(ArgsParseError::NotEnoughArguments)
177
+
Args::parse(["./my_boats", "foobar", "127.0.0.1:8080"]),
178
+
Err(ArgsParseError::InvalidMode("foobar".into()))
181
+
Args::parse(["./my_boats", "host", "blablabla"]),
182
+
Err(ArgsParseError::InvalidAddr(_))
185
+
Args::parse(["./my_boats", "host", "localhost"]),
186
+
Err(ArgsParseError::InvalidAddr(_))
189
+
Args::parse(["./my_boats", "host", "0.0.0.0"]),
190
+
Err(ArgsParseError::InvalidAddr(_))
193
+
Args::parse(["./my_boats", "join", "127.0.0.1"]),
194
+
Err(ArgsParseError::InvalidAddr(_))
197
+
Args::parse(["./my_boats", "join", "localhost"]),
198
+
Err(ArgsParseError::InvalidAddr(_))
201
+
Args::parse(["./my_boats", "join", "localhost:8080"]),
202
+
Err(ArgsParseError::InvalidAddr(_))