/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ extern crate url; use self::url::Host; use error::SdpParserInternalError; use std::convert::TryFrom; use std::fmt; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::str::FromStr; #[derive(Clone, Debug)] #[cfg_attr(feature = "serialize", derive(Serialize))] pub enum Address { Fqdn(String), Ip(IpAddr), } impl fmt::Display for Address { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Address::Fqdn(fqdn) => fqdn.fmt(f), Address::Ip(ip) => ip.fmt(f), } } } impl FromStr for Address { type Err = SdpParserInternalError; fn from_str(s: &str) -> Result { let mut e: Option = None; if s.find(':').is_some() { match IpAddr::from_str(s) { Ok(ip) => return Ok(Address::Ip(ip)), Err(err) => e = Some(err.into()), } } Host::parse(s) .map(|host| match host { Host::Domain(s) => Address::Fqdn(s), Host::Ipv4(ip) => Address::Ip(IpAddr::V4(ip)), Host::Ipv6(ip) => Address::Ip(IpAddr::V6(ip)), }) .map_err(|err| e.unwrap_or_else(|| err.into())) } } impl From for Address { fn from(item: ExplicitlyTypedAddress) -> Self { match item { ExplicitlyTypedAddress::Fqdn { domain, .. } => Address::Fqdn(domain), ExplicitlyTypedAddress::Ip(ip) => Address::Ip(ip), } } } impl PartialEq for Address { fn eq(&self, other: &Self) -> bool { match (self, other) { (Address::Fqdn(a), Address::Fqdn(b)) => a.to_lowercase() == b.to_lowercase(), (Address::Ip(a), Address::Ip(b)) => a == b, (_, _) => false, } } } #[derive(Clone, Copy, PartialEq, Debug)] #[cfg_attr(feature = "serialize", derive(Serialize))] pub enum AddressType { IpV4 = 4, IpV6 = 6, } impl fmt::Display for AddressType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { AddressType::IpV4 => "IP4", AddressType::IpV6 => "IP6", } .fmt(f) } } impl FromStr for AddressType { type Err = SdpParserInternalError; fn from_str(s: &str) -> Result { match s.to_uppercase().as_str() { "IP4" => Ok(AddressType::IpV4), "IP6" => Ok(AddressType::IpV6), _ => Err(SdpParserInternalError::UnknownAddressType(s.to_owned())), } } } pub trait AddressTyped { fn address_type(&self) -> AddressType; } impl AddressTyped for IpAddr { fn address_type(&self) -> AddressType { match self { IpAddr::V4(_) => AddressType::IpV4, IpAddr::V6(_) => AddressType::IpV6, } } } #[derive(Clone, Debug)] #[cfg_attr(feature = "serialize", derive(Serialize))] pub enum ExplicitlyTypedAddress { Fqdn { address_type: AddressType, domain: String, }, Ip(IpAddr), } impl fmt::Display for ExplicitlyTypedAddress { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "IN {} ", self.address_type())?; match self { ExplicitlyTypedAddress::Fqdn { domain, .. } => domain.fmt(f), ExplicitlyTypedAddress::Ip(ip) => ip.fmt(f), } } } impl AddressTyped for ExplicitlyTypedAddress { fn address_type(&self) -> AddressType { match self { ExplicitlyTypedAddress::Fqdn { address_type, .. } => *address_type, ExplicitlyTypedAddress::Ip(ip) => ip.address_type(), } } } impl From for ExplicitlyTypedAddress { fn from(item: IpAddr) -> Self { ExplicitlyTypedAddress::Ip(item) } } impl From for ExplicitlyTypedAddress { fn from(item: Ipv4Addr) -> Self { ExplicitlyTypedAddress::Ip(IpAddr::V4(item)) } } impl From for ExplicitlyTypedAddress { fn from(item: Ipv6Addr) -> Self { ExplicitlyTypedAddress::Ip(IpAddr::V6(item)) } } impl TryFrom<(AddressType, &str)> for ExplicitlyTypedAddress { type Error = SdpParserInternalError; fn try_from(item: (AddressType, &str)) -> Result { match Address::from_str(item.1)? { Address::Ip(ip) => { if ip.address_type() != item.0 { Err(SdpParserInternalError::AddressTypeMismatch { found: ip.address_type(), expected: item.0, }) } else { Ok(ExplicitlyTypedAddress::Ip(ip)) } } Address::Fqdn(domain) => Ok(ExplicitlyTypedAddress::Fqdn { address_type: item.0, domain, }), } } } impl PartialEq for ExplicitlyTypedAddress { fn eq(&self, other: &Self) -> bool { match (self, other) { ( ExplicitlyTypedAddress::Fqdn { address_type: a1, domain: d1, }, ExplicitlyTypedAddress::Fqdn { address_type: a2, domain: d2, }, ) => a1 == a2 && d1.to_lowercase() == d2.to_lowercase(), (ExplicitlyTypedAddress::Ip(a), ExplicitlyTypedAddress::Ip(b)) => a == b, (_, _) => false, } } } #[cfg(test)] #[path = "./address_tests.rs"] mod address_tests;