webrtc-sdp 0.3.13

webrtc-sdp parses strings in the format of the Session Description Protocol according to RFC4566. It specifically supports the subset of features required to support WebRTC according to the JSEP draft.
Documentation
/* 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<Self, Self::Err> {
        let mut e: Option<SdpParserInternalError> = 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<ExplicitlyTypedAddress> 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<Self, Self::Err> {
        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<IpAddr> for ExplicitlyTypedAddress {
    fn from(item: IpAddr) -> Self {
        ExplicitlyTypedAddress::Ip(item)
    }
}

impl From<Ipv4Addr> for ExplicitlyTypedAddress {
    fn from(item: Ipv4Addr) -> Self {
        ExplicitlyTypedAddress::Ip(IpAddr::V4(item))
    }
}

impl From<Ipv6Addr> 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<Self, Self::Error> {
        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;