socket_address/
lib.rs

1//! # ListenAddress
2//! ```
3//! use core::str::FromStr;
4//! use std::net::{IpAddr, Ipv4Addr, SocketAddr};
5//!
6//! use socket_address::ListenAddress;
7//!
8//! let addr = ListenAddress::from_str("0.0.0.0:80").unwrap();
9//! assert_eq!(addr, ListenAddress::Network(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 80)));
10//! ```
11//!
12//! ```
13//! use core::str::FromStr;
14//! use std::path::PathBuf;
15//!
16//! use socket_address::ListenAddress;
17//!
18//! let addr = ListenAddress::from_str("unix:myapp.sock").unwrap();
19//! assert_eq!(addr, ListenAddress::UnixSocket("myapp.sock".into()));
20//! ```
21//!
22//! # ConnectAddress
23//! ```
24//! use core::str::FromStr;
25//! use http::Uri;
26//!
27//! use socket_address::ConnectAddress;
28//!
29//! let addr = ConnectAddress::from_str("http://127.0.0.1/").unwrap();
30//! assert_eq!(addr, ConnectAddress::Uri(Uri::from_str("http://127.0.0.1/").unwrap()));
31//! ```
32//!
33//! ```
34//! use core::str::FromStr;
35//! use std::path::PathBuf;
36//!
37//! use socket_address::ConnectAddress;
38//!
39//! let addr = ConnectAddress::from_str("unix:myapp.sock").unwrap();
40//! assert_eq!(addr, ConnectAddress::UnixSocket("myapp.sock".into()));
41//! ```
42use core::fmt;
43use core::str::FromStr;
44use std::net::SocketAddr;
45use std::path::PathBuf;
46
47use http::Uri;
48
49#[derive(Clone, Debug, PartialEq, Eq)]
50pub enum ListenAddress {
51	Network(SocketAddr),
52	UnixSocket(PathBuf),
53}
54
55#[derive(Clone, Debug, PartialEq, Eq)]
56pub enum ConnectAddress {
57	Uri(Uri),
58	UnixSocket(PathBuf),
59}
60
61impl FromStr for ListenAddress {
62	type Err = Error;
63
64	fn from_str(s: &str) -> Result<Self, Self::Err> {
65		match s.strip_prefix("unix:") {
66			Some(v) => Ok(Self::UnixSocket(v.into())),
67			None => match SocketAddr::from_str(s) {
68				Ok(v) => Ok(Self::Network(v)),
69				Err(_) => Err(Error::InvalidListenAddress(s.to_owned())),
70			},
71		}
72	}
73}
74
75impl fmt::Display for ListenAddress {
76	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
77		match self {
78			Self::Network(addr) => addr.fmt(f),
79			Self::UnixSocket(path) => {
80				f.write_str("unix:")?;
81				path.display().fmt(f)
82			},
83		}
84	}
85}
86
87impl FromStr for ConnectAddress {
88	type Err = Error;
89
90	fn from_str(s: &str) -> Result<Self, Self::Err> {
91		match s.strip_prefix("unix:") {
92			Some(v) => Ok(Self::UnixSocket(v.into())),
93			None => Ok(Self::Uri(Uri::from_str(s)?)),
94		}
95	}
96}
97
98impl fmt::Display for ConnectAddress {
99	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
100		match self {
101			Self::UnixSocket(path) => {
102				f.write_str("unix:")?;
103				path.display().fmt(f)
104			},
105			Self::Uri(uri) => uri.fmt(f),
106		}
107	}
108}
109
110impl TryFrom<ConnectAddress> for ListenAddress {
111	type Error = Error;
112
113	fn try_from(value: ConnectAddress) -> Result<Self, Self::Error> {
114		Ok(match value {
115			ConnectAddress::Uri(uri) => {
116				let no_scheme = format!("{}:{}", uri.host().ok_or(Error::MissingHost)?, uri.port().ok_or(Error::MissingPort)?);
117				Self::Network(SocketAddr::from_str(&no_scheme).map_err(|_| Error::InvalidListenAddress(no_scheme))?)
118			},
119			ConnectAddress::UnixSocket(path) => Self::UnixSocket(path),
120		})
121	}
122}
123
124#[derive(Debug, thiserror::Error)]
125pub enum Error {
126	#[error("'{0}' is neither a valid IP:port address nor a path prefixed with 'unix:'")]
127	InvalidListenAddress(String),
128
129	#[error("String has a scheme prefix of either http or https but could not be parsed into a Uri: {0}")]
130	InvalidUri(#[from] http::uri::InvalidUri),
131
132	#[error("The address is missing a host")]
133	MissingHost,
134
135	#[error("The address is missing a port")]
136	MissingPort,
137}