1use std::str::FromStr;
2use std::num::ParseIntError;
3use std::net::{IpAddr, SocketAddr};
4
5use abstract_ns;
6use abstract_ns::name::{self, Name};
7use quick_error::ResultExt;
8
9quick_error! {
10 #[derive(Debug)]
11 pub enum Error {
12 Name(name: String, err: name::Error) {
13 cause(err)
14 context(name: &'a str, err: name::Error)
15 -> (name.to_string(), err)
16 }
17 Port(name: String, err: ParseIntError) {
18 cause(err)
19 context(name: &'a str, err: ParseIntError)
20 -> (name.to_string(), err)
21 }
22 }
23}
24
25#[derive(Debug)]
53pub enum AutoName<'a> {
54 Auto(&'a str),
56 HostPort(&'a str, u16),
58 HostDefaultPort(&'a str),
60 Service(&'a str),
62 IpAddr(IpAddr),
64 SocketAddr(SocketAddr),
66}
67
68
69pub trait IntoNameIter<'a> {
80 type Item: Into<AutoName<'a>>;
82 type IntoIter: Iterator<Item=Self::Item>;
84 fn into_name_iter(&'a self) -> Self::IntoIter;
86}
87
88#[derive(Debug, PartialEq, Eq, Hash, Clone)]
89pub(crate) enum InternalName {
90 HostPort(Name, u16),
91 Service(Name),
92 Addr(SocketAddr),
93}
94
95impl<'a> AutoName<'a> {
96 pub(crate) fn parse(&self, default_port: u16)
97 -> Result<InternalName, Error>
98 {
99 use self::AutoName as A;
100 use self::InternalName as I;
101 match *self {
102 A::Auto(x) => {
103 if let Ok(ip) = x.parse() {
104 Ok(I::Addr(SocketAddr::new(ip, default_port)))
105 } else if let Ok(sa) = x.parse() {
106 Ok(I::Addr(sa))
107 } else if x.starts_with("_") {
108 Ok(I::Service(Name::from_str(x).context(x)?))
109 } else if let Some(pos) = x.find(':') {
110 Ok(I::HostPort(Name::from_str(&x[..pos]).context(x)?,
111 x[pos+1..].parse().context(x)?))
112 } else {
113 Ok(I::HostPort(Name::from_str(x).context(x)?,
114 default_port))
115 }
116 }
117 A::HostPort(name, port)
118 => Ok(I::HostPort(Name::from_str(name).context(name)?, port)),
119 A::HostDefaultPort(name)
120 => Ok(I::HostPort(Name::from_str(name).context(name)?, default_port)),
121 A::Service(name)
122 => Ok(I::Service(Name::from_str(name).context(name)?)),
123 A::IpAddr(ip) => Ok(I::Addr(SocketAddr::new(ip, default_port))),
124 A::SocketAddr(sa) => Ok(I::Addr(sa)),
125 }
126 }
127}
128
129impl<'a, T: AsRef<str> + 'a> From<&'a T> for AutoName<'a> {
130 fn from(val: &'a T) -> AutoName<'a> {
131 AutoName::Auto(val.as_ref())
132 }
133}
134
135impl<'a> From<&'a str> for AutoName<'a> {
136 fn from(val: &'a str) -> AutoName<'a> {
137 AutoName::Auto(val)
138 }
139}
140
141impl<'a, T: 'a> IntoNameIter<'a> for T
142 where &'a T: IntoIterator,
143 <&'a T as IntoIterator>::Item: Into<AutoName<'a>>,
144{
145 type Item = <&'a T as IntoIterator>::Item;
146 type IntoIter = <&'a T as IntoIterator>::IntoIter;
147 fn into_name_iter(&'a self) -> Self::IntoIter {
148 self.into_iter()
149 }
150}
151
152
153impl Into<abstract_ns::Error> for Error {
154 fn into(self) -> abstract_ns::Error {
155 match self {
156 Error::Name(name, _) => {
157 abstract_ns::Error::InvalidName(name, "bad name")
158 }
159 Error::Port(name, _) => {
160 abstract_ns::Error::InvalidName(name, "bad port number")
161 }
162 }
163 }
164}
165
166#[cfg(test)]
167mod test {
168 use abstract_ns::Name;
169 use super::AutoName as A;
170 use super::InternalName as I;
171
172 fn name(name: &str) -> Name {
173 name.parse().unwrap()
174 }
175
176 #[test]
177 fn auto() {
178 assert_eq!(A::Auto("localhost").parse(1234).unwrap(),
179 I::HostPort(name("localhost"), 1234));
180 assert_eq!(A::Auto("localhost:8080").parse(1234).unwrap(),
181 I::HostPort(name("localhost"), 8080));
182 assert_eq!(A::Auto("_my._svc.localhost").parse(1234).unwrap(),
183 I::Service(name("_my._svc.localhost")));
184 }
185
186 #[test]
187 #[should_panic(expected="InvalidChar")]
188 fn bad_names() {
189 A::Auto("_my._svc.localhost:8080").parse(1234).unwrap();
190 }
191}
192