use std::str::FromStr;
use std::num::ParseIntError;
use std::net::{IpAddr, SocketAddr};
use abstract_ns;
use abstract_ns::name::{self, Name};
use quick_error::ResultExt;
quick_error! {
#[derive(Debug)]
pub enum Error {
Name(name: String, err: name::Error) {
cause(err)
context(name: &'a str, err: name::Error)
-> (name.to_string(), err)
}
Port(name: String, err: ParseIntError) {
cause(err)
context(name: &'a str, err: ParseIntError)
-> (name.to_string(), err)
}
}
}
#[derive(Debug)]
pub enum AutoName<'a> {
Auto(&'a str),
HostPort(&'a str, u16),
HostDefaultPort(&'a str),
Service(&'a str),
IpAddr(IpAddr),
SocketAddr(SocketAddr),
}
pub trait IntoNameIter<'a> {
type Item: Into<AutoName<'a>>;
type IntoIter: Iterator<Item=Self::Item>;
fn into_name_iter(&'a self) -> Self::IntoIter;
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub(crate) enum InternalName {
HostPort(Name, u16),
Service(Name),
Addr(SocketAddr),
}
impl<'a> AutoName<'a> {
pub(crate) fn parse(&self, default_port: u16)
-> Result<InternalName, Error>
{
use self::AutoName as A;
use self::InternalName as I;
match *self {
A::Auto(x) => {
if let Ok(ip) = x.parse() {
Ok(I::Addr(SocketAddr::new(ip, default_port)))
} else if let Ok(sa) = x.parse() {
Ok(I::Addr(sa))
} else if x.starts_with("_") {
Ok(I::Service(Name::from_str(x).context(x)?))
} else if let Some(pos) = x.find(':') {
Ok(I::HostPort(Name::from_str(&x[..pos]).context(x)?,
x[pos+1..].parse().context(x)?))
} else {
Ok(I::HostPort(Name::from_str(x).context(x)?,
default_port))
}
}
A::HostPort(name, port)
=> Ok(I::HostPort(Name::from_str(name).context(name)?, port)),
A::HostDefaultPort(name)
=> Ok(I::HostPort(Name::from_str(name).context(name)?, default_port)),
A::Service(name)
=> Ok(I::Service(Name::from_str(name).context(name)?)),
A::IpAddr(ip) => Ok(I::Addr(SocketAddr::new(ip, default_port))),
A::SocketAddr(sa) => Ok(I::Addr(sa)),
}
}
}
impl<'a, T: AsRef<str> + 'a> From<&'a T> for AutoName<'a> {
fn from(val: &'a T) -> AutoName<'a> {
AutoName::Auto(val.as_ref())
}
}
impl<'a> From<&'a str> for AutoName<'a> {
fn from(val: &'a str) -> AutoName<'a> {
AutoName::Auto(val)
}
}
impl<'a, T: 'a> IntoNameIter<'a> for T
where &'a T: IntoIterator,
<&'a T as IntoIterator>::Item: Into<AutoName<'a>>,
{
type Item = <&'a T as IntoIterator>::Item;
type IntoIter = <&'a T as IntoIterator>::IntoIter;
fn into_name_iter(&'a self) -> Self::IntoIter {
self.into_iter()
}
}
impl Into<abstract_ns::Error> for Error {
fn into(self) -> abstract_ns::Error {
match self {
Error::Name(name, _) => {
abstract_ns::Error::InvalidName(name, "bad name")
}
Error::Port(name, _) => {
abstract_ns::Error::InvalidName(name, "bad port number")
}
}
}
}
#[cfg(test)]
mod test {
use abstract_ns::Name;
use super::AutoName as A;
use super::InternalName as I;
fn name(name: &str) -> Name {
name.parse().unwrap()
}
#[test]
fn auto() {
assert_eq!(A::Auto("localhost").parse(1234).unwrap(),
I::HostPort(name("localhost"), 1234));
assert_eq!(A::Auto("localhost:8080").parse(1234).unwrap(),
I::HostPort(name("localhost"), 8080));
assert_eq!(A::Auto("_my._svc.localhost").parse(1234).unwrap(),
I::Service(name("_my._svc.localhost")));
}
#[test]
#[should_panic(expected="InvalidChar")]
fn bad_names() {
A::Auto("_my._svc.localhost:8080").parse(1234).unwrap();
}
}