1use super::SetOpt;
5use curl::easy::{Easy2, List};
6use http::Uri;
7use std::{convert::TryFrom, fmt, net::SocketAddr, str::FromStr};
8
9#[derive(Clone, Debug, Eq, PartialEq)]
11pub struct DialerParseError(());
12
13impl fmt::Display for DialerParseError {
14 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
15 fmt.write_str("invalid dial address syntax")
16 }
17}
18
19impl std::error::Error for DialerParseError {}
20
21#[derive(Clone, Debug)]
45pub struct Dialer(Inner);
46
47#[derive(Clone, Debug, Eq, PartialEq)]
48enum Inner {
49 Default,
50
51 IpSocket(String),
52
53 #[cfg(unix)]
54 UnixSocket(std::path::PathBuf),
55}
56
57impl Dialer {
58 pub fn ip_socket(addr: impl Into<SocketAddr>) -> Self {
80 Self(Inner::IpSocket(format!("::{}", addr.into())))
82 }
83
84 #[cfg(unix)]
102 pub fn unix_socket(path: impl Into<std::path::PathBuf>) -> Self {
103 Self(Inner::UnixSocket(path.into()))
104 }
105}
106
107impl Default for Dialer {
108 fn default() -> Self {
109 Self(Inner::Default)
110 }
111}
112
113impl From<SocketAddr> for Dialer {
114 fn from(socket_addr: SocketAddr) -> Self {
115 Self::ip_socket(socket_addr)
116 }
117}
118
119impl FromStr for Dialer {
120 type Err = DialerParseError;
121
122 fn from_str(s: &str) -> Result<Self, Self::Err> {
123 if s.starts_with("tcp:") {
124 let addr_str = s[4..].trim_start_matches('/');
125
126 return addr_str
127 .parse::<SocketAddr>()
128 .map(Self::ip_socket)
129 .map_err(|_| DialerParseError(()));
130 }
131
132 #[cfg(unix)]
133 {
134 if s.starts_with("unix:") {
135 let mut path = std::path::PathBuf::from("/");
137 path.push(&s[5..].trim_start_matches('/'));
138
139 return Ok(Self(Inner::UnixSocket(path)));
140 }
141 }
142
143 Err(DialerParseError(()))
144 }
145}
146
147impl TryFrom<&'_ str> for Dialer {
148 type Error = DialerParseError;
149
150 fn try_from(str: &str) -> Result<Self, Self::Error> {
151 str.parse()
152 }
153}
154
155impl TryFrom<String> for Dialer {
156 type Error = DialerParseError;
157
158 fn try_from(string: String) -> Result<Self, Self::Error> {
159 string.parse()
160 }
161}
162
163impl TryFrom<Uri> for Dialer {
164 type Error = DialerParseError;
165
166 fn try_from(uri: Uri) -> Result<Self, Self::Error> {
167 uri.to_string().parse()
169 }
170}
171
172impl SetOpt for Dialer {
173 fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
174 let mut connect_to = List::new();
175
176 if let Inner::IpSocket(addr) = &self.0 {
177 connect_to.append(addr)?;
178 }
179
180 easy.connect_to(connect_to)?;
181
182 #[cfg(unix)]
183 easy.unix_socket_path(match &self.0 {
184 Inner::UnixSocket(path) => Some(path),
185 _ => None,
186 })?;
187
188 Ok(())
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 #[test]
197 fn parse_tcp_socket_and_port_uri() {
198 let dialer = "tcp:127.0.0.1:1200".parse::<Dialer>().unwrap();
199
200 assert_eq!(dialer.0, Inner::IpSocket("::127.0.0.1:1200".into()));
201 }
202
203 #[test]
204 fn parse_invalid_tcp_uri() {
205 let result = "tcp:127.0.0.1-1200".parse::<Dialer>();
206
207 assert!(result.is_err());
208 }
209
210 #[test]
211 #[cfg(unix)]
212 fn parse_unix_socket_uri() {
213 let dialer = "unix:/path/to/my.sock".parse::<Dialer>().unwrap();
214
215 assert_eq!(dialer.0, Inner::UnixSocket("/path/to/my.sock".into()));
216 }
217
218 #[test]
219 #[cfg(unix)]
220 fn from_unix_socket_uri() {
221 let uri = "unix://path/to/my.sock".parse::<http::Uri>().unwrap();
222 let dialer = Dialer::try_from(uri).unwrap();
223
224 assert_eq!(dialer.0, Inner::UnixSocket("/path/to/my.sock".into()));
225 }
226}