use std::ascii::AsciiExt;
use std::fmt::{self, Formatter};
use std::net::{Ipv4Addr, Ipv6Addr};
use parser::{ParseResult, ParseError};
use percent_encoding::{percent_decode};
#[derive(PartialEq, Eq, Clone, Debug, Hash, PartialOrd, Ord)]
#[cfg_attr(feature="heap_size", derive(HeapSizeOf))]
pub enum Host {
Domain(String),
V4(Ipv4Addr),
V6(Ipv6Addr),
}
impl Host {
pub fn parse(input: &str) -> ParseResult<Host> {
if input.len() == 0 {
Err(ParseError::EmptyHost)
} else if input.starts_with("[") {
if input.ends_with("]") {
if let Ok(addr) = input[1..input.len() - 1].parse() {
Ok(Host::V6(addr))
} else {
Err(ParseError::InvalidIpv6Address)
}
} else {
Err(ParseError::InvalidIpv6Address)
}
} else {
if let Ok(addr) = input.parse() {
Ok(Host::V4(addr))
} else {
let decoded = percent_decode(input.as_bytes());
let domain = String::from_utf8_lossy(&decoded);
if !domain.is_ascii() {
Err(ParseError::NonAsciiDomainsNotSupportedYet)
} else if domain.find(&[
'\0', '\t', '\n', '\r', ' ', '#', '%', '/', ':', '?', '@', '[', '\\', ']'
][..]).is_some() {
Err(ParseError::InvalidDomainCharacter)
} else {
Ok(Host::Domain(domain.to_ascii_lowercase()))
}
}
}
}
pub fn serialize(&self) -> String {
self.to_string()
}
}
impl fmt::Display for Host {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match *self {
Host::Domain(ref domain) => domain.fmt(f),
Host::V4(ref addr) => addr.fmt(f),
Host::V6(ref addr) => write!(f, "[{}]", addr),
}
}
}