1use std::{
11 convert::TryFrom,
12 net::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs},
13 str::FromStr,
14};
15
16use snafu::{ensure, AsErrorSource, ResultExt, Snafu};
17
18#[derive(Debug, Clone, Eq, Hash, PartialEq)]
50pub struct FullAeAddr<T> {
51 ae_title: String,
52 socket_addr: T,
53}
54
55impl<T> FullAeAddr<T> {
56 pub fn new(ae_title: impl Into<String>, socket_addr: T) -> Self {
58 FullAeAddr {
59 ae_title: ae_title.into(),
60 socket_addr,
61 }
62 }
63
64 pub fn ae_title(&self) -> &str {
66 &self.ae_title
67 }
68
69 pub fn socket_addr(&self) -> &T {
71 &self.socket_addr
72 }
73
74 pub fn into_parts(self) -> (String, T) {
76 (self.ae_title, self.socket_addr)
77 }
78}
79
80impl<T> From<(String, T)> for FullAeAddr<T> {
81 fn from((ae_title, socket_addr): (String, T)) -> Self {
82 Self::new(ae_title, socket_addr)
83 }
84}
85
86#[derive(Debug, Clone, Eq, PartialEq, Snafu)]
88pub enum ParseAeAddressError<E>
89where
90 E: std::fmt::Debug + AsErrorSource,
91{
92 MissingPart,
94
95 ParseSocketAddress { source: E },
97}
98
99impl<T> FromStr for FullAeAddr<T>
100where
101 T: FromStr,
102 T::Err: std::fmt::Debug + AsErrorSource,
103{
104 type Err = ParseAeAddressError<<T as FromStr>::Err>;
105
106 fn from_str(s: &str) -> Result<Self, Self::Err> {
107 if let Some((ae_title, addr)) = s.split_once('@') {
109 ensure!(!ae_title.is_empty(), MissingPartSnafu);
110 Ok(FullAeAddr {
111 ae_title: ae_title.to_string(),
112 socket_addr: addr.parse().context(ParseSocketAddressSnafu)?,
113 })
114 } else {
115 Err(ParseAeAddressError::MissingPart)
116 }
117 }
118}
119
120impl<T> ToSocketAddrs for FullAeAddr<T>
121where
122 T: ToSocketAddrs,
123{
124 type Iter = T::Iter;
125
126 fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
127 self.socket_addr.to_socket_addrs()
128 }
129}
130
131impl<T> std::fmt::Display for FullAeAddr<T>
132where
133 T: std::fmt::Display,
134{
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 f.write_str(&self.ae_title.replace('@', "\\@"))?;
137 f.write_str("@")?;
138 std::fmt::Display::fmt(&self.socket_addr, f)
139 }
140}
141
142#[derive(Debug, Clone, Eq, Hash, PartialEq)]
177pub struct AeAddr<T> {
178 ae_title: Option<String>,
179 socket_addr: T,
180}
181
182impl<T> AeAddr<T> {
183 pub fn new(ae_title: impl Into<String>, socket_addr: T) -> Self {
185 AeAddr {
186 ae_title: Some(ae_title.into()),
187 socket_addr,
188 }
189 }
190
191 pub fn new_socket_addr(socket_addr: T) -> Self {
193 AeAddr {
194 ae_title: None,
195 socket_addr,
196 }
197 }
198
199 pub fn ae_title(&self) -> Option<&str> {
201 self.ae_title.as_deref()
202 }
203
204 pub fn socket_addr(&self) -> &T {
206 &self.socket_addr
207 }
208
209 pub fn with_ae_title(self, ae_title: impl Into<String>) -> FullAeAddr<T> {
212 FullAeAddr {
213 ae_title: ae_title.into(),
214 socket_addr: self.socket_addr,
215 }
216 }
217
218 pub fn with_default_ae_title(self, ae_title: impl Into<String>) -> FullAeAddr<T> {
221 FullAeAddr {
222 ae_title: self.ae_title.unwrap_or_else(|| ae_title.into()),
223 socket_addr: self.socket_addr,
224 }
225 }
226
227 pub fn into_parts(self) -> (Option<String>, T) {
229 (self.ae_title, self.socket_addr)
230 }
231}
232
233impl From<SocketAddr> for AeAddr<SocketAddr> {
235 fn from(socket_addr: SocketAddr) -> Self {
236 AeAddr {
237 ae_title: None,
238 socket_addr,
239 }
240 }
241}
242
243impl From<SocketAddrV4> for AeAddr<SocketAddrV4> {
245 fn from(socket_addr: SocketAddrV4) -> Self {
246 AeAddr {
247 ae_title: None,
248 socket_addr,
249 }
250 }
251}
252
253impl From<SocketAddrV6> for AeAddr<SocketAddrV6> {
255 fn from(socket_addr: SocketAddrV6) -> Self {
256 AeAddr {
257 ae_title: None,
258 socket_addr,
259 }
260 }
261}
262
263impl<T> From<FullAeAddr<T>> for AeAddr<T> {
264 fn from(full: FullAeAddr<T>) -> Self {
265 AeAddr {
266 ae_title: Some(full.ae_title),
267 socket_addr: full.socket_addr,
268 }
269 }
270}
271
272impl<T> FromStr for AeAddr<T>
273where
274 T: FromStr,
275{
276 type Err = <T as FromStr>::Err;
277
278 fn from_str(s: &str) -> Result<Self, Self::Err> {
279 if let Some((ae_title, address)) = s.split_once('@') {
281 Ok(AeAddr {
282 ae_title: Some(ae_title)
283 .filter(|s| !s.is_empty())
284 .map(|s| s.to_string()),
285 socket_addr: address.parse()?,
286 })
287 } else {
288 Ok(AeAddr {
289 ae_title: None,
290 socket_addr: s.parse()?,
291 })
292 }
293 }
294}
295
296impl<'a> TryFrom<&'a str> for AeAddr<String> {
297 type Error = <AeAddr<String> as FromStr>::Err;
298
299 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
300 s.parse()
301 }
302}
303
304impl<T> ToSocketAddrs for AeAddr<T>
305where
306 T: ToSocketAddrs,
307{
308 type Iter = T::Iter;
309
310 fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
311 self.socket_addr.to_socket_addrs()
312 }
313}
314
315impl<T> std::fmt::Display for AeAddr<T>
316where
317 T: std::fmt::Display,
318{
319 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320 let socket_addr = self.socket_addr.to_string();
321 if let Some(ae_title) = &self.ae_title {
322 f.write_str(&ae_title.replace('@', "\\@"))?;
323 f.write_str("@")?;
324 } else if socket_addr.contains('@') {
325 f.write_str("@")?;
330 }
331
332 std::fmt::Display::fmt(&socket_addr, f)
333 }
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339
340 #[test]
341 fn ae_addr_parse() {
342 let addr: FullAeAddr<String> = "SCP-STORAGE@127.0.0.1:104".parse().unwrap();
344 assert_eq!(addr.ae_title(), "SCP-STORAGE");
345 assert_eq!(addr.socket_addr(), "127.0.0.1:104");
346
347 let addr: FullAeAddr<SocketAddr> = "SCP_STORAGE@127.0.0.1:104".parse().unwrap();
349 assert_eq!(addr.ae_title(), "SCP_STORAGE");
350 assert_eq!(addr.socket_addr(), &SocketAddr::from(([127, 0, 0, 1], 104)));
351 assert_eq!(&addr.to_string(), "SCP_STORAGE@127.0.0.1:104");
352
353 let addr: FullAeAddr<SocketAddrV4> = "MAMMOSTORE@10.0.0.11:104".parse().unwrap();
355 assert_eq!(addr.ae_title(), "MAMMOSTORE");
356 assert_eq!(
357 addr.socket_addr(),
358 &SocketAddrV4::new([10, 0, 0, 11].into(), 104)
359 );
360 assert_eq!(&addr.to_string(), "MAMMOSTORE@10.0.0.11:104");
361 }
362
363 #[test]
365 fn ae_addr_parse_no_ae() {
366 let res = FullAeAddr::<String>::from_str("pacs.hospital.example.com:104");
368 assert!(matches!(res, Err(ParseAeAddressError::MissingPart)));
369 let res = FullAeAddr::<String>::from_str("@pacs.hospital.example.com:104");
371 assert!(matches!(res, Err(ParseAeAddressError::MissingPart)));
372
373 let addr: AeAddr<String> = "pacs.hospital.example.com:104".parse().unwrap();
375 assert_eq!(addr.ae_title(), None);
376 assert_eq!(addr.socket_addr(), "pacs.hospital.example.com:104");
377 let addr: AeAddr<String> = "@pacs.hospital.example.com:104".parse().unwrap();
379 assert_eq!(addr.ae_title(), None);
380 assert_eq!(addr.socket_addr(), "pacs.hospital.example.com:104");
381 }
382
383 #[test]
384 fn ae_addr_parse_weird_scenarios() {
385 let addr: FullAeAddr<String> = "ABC@DICOM@pacs.archive.example.com:104".parse().unwrap();
387 assert_eq!(addr.ae_title(), "ABC");
388 assert_eq!(addr.socket_addr(), "DICOM@pacs.archive.example.com:104");
389 assert_eq!(&addr.to_string(), "ABC@DICOM@pacs.archive.example.com:104");
390 }
391}