salvo_core 0.91.1

Salvo is a powerful web framework that can make your work easier.
Documentation
//! Socket Address module.
use std::fmt::{self, Display, Formatter};
#[cfg(unix)]
use std::sync::Arc;

/// Network socket address
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum SocketAddr {
    /// Unknown address
    Unknown,
    /// IPv4 socket address
    IPv4(std::net::SocketAddrV4),
    /// IPv6 socket address
    IPv6(std::net::SocketAddrV6),
    /// Unix socket address
    #[cfg(unix)]
    #[cfg_attr(docsrs, doc(cfg(unix)))]
    Unix(Arc<tokio::net::unix::SocketAddr>),
}
impl From<std::net::SocketAddr> for SocketAddr {
    #[inline]
    fn from(addr: std::net::SocketAddr) -> Self {
        match addr {
            std::net::SocketAddr::V4(val) => Self::IPv4(val),
            std::net::SocketAddr::V6(val) => Self::IPv6(val),
        }
    }
}
impl From<std::net::SocketAddrV4> for SocketAddr {
    #[inline]
    fn from(addr: std::net::SocketAddrV4) -> Self {
        Self::IPv4(addr)
    }
}
impl From<std::net::SocketAddrV6> for SocketAddr {
    #[inline]
    fn from(addr: std::net::SocketAddrV6) -> Self {
        Self::IPv6(addr)
    }
}

#[cfg(unix)]
impl From<tokio::net::unix::SocketAddr> for SocketAddr {
    #[inline]
    fn from(addr: tokio::net::unix::SocketAddr) -> Self {
        Self::Unix(addr.into())
    }
}
#[cfg(unix)]
impl From<Arc<tokio::net::unix::SocketAddr>> for SocketAddr {
    #[inline]
    fn from(addr: Arc<tokio::net::unix::SocketAddr>) -> Self {
        Self::Unix(addr)
    }
}
impl SocketAddr {
    /// Returns if it is an IPv4 socket address.
    #[inline]
    #[must_use]
    pub fn is_ipv4(&self) -> bool {
        matches!(*self, Self::IPv4(_))
    }
    /// Returns if it is an IPv6 socket address.
    #[inline]
    #[must_use]
    pub fn is_ipv6(&self) -> bool {
        matches!(*self, Self::IPv6(_))
    }

    /// Returns the IP address associated with this socket address.
    ///
    /// Returns `None` if this address is not an IP-based socket address.
    #[inline]
    #[must_use]
    pub fn ip(&self) -> Option<std::net::IpAddr> {
        match self {
            Self::IPv4(a) => Some(std::net::IpAddr::V4(*a.ip())),
            Self::IPv6(a) => Some(std::net::IpAddr::V6(*a.ip())),
            _ => None,
        }
    }

    /// Returns the port number associated with this socket address.
    ///
    /// Returns `None` if this address is not an IP-based socket address.
    #[must_use]
    #[inline]
    pub fn port(&self) -> Option<u16> {
        match self {
            Self::IPv4(a) => Some(a.port()),
            Self::IPv6(a) => Some(a.port()),
            _ => None,
        }
    }

    /// Convert to [`std::net::SocketAddr`].
    #[inline]
    #[must_use]
    pub fn into_std(self) -> Option<std::net::SocketAddr> {
        match self {
            Self::IPv4(addr) => Some(addr.into()),
            Self::IPv6(addr) => Some(addr.into()),
            _ => None,
        }
    }

    cfg_feature! {
        #![all(feature = "unix", unix)]
        /// Returns if it is a Unix socket address.
        #[inline]
        #[must_use] pub fn is_unix(&self) -> bool {
            matches!(*self, Self::Unix(_))
        }
    }

    /// Returns IPv6 socket address.
    #[inline]
    #[must_use]
    pub fn as_ipv6(&self) -> Option<&std::net::SocketAddrV6> {
        match self {
            Self::IPv6(addr) => Some(addr),
            _ => None,
        }
    }
    /// Returns IPv4 socket address.
    #[inline]
    #[must_use]
    pub fn as_ipv4(&self) -> Option<&std::net::SocketAddrV4> {
        match self {
            Self::IPv4(addr) => Some(addr),
            _ => None,
        }
    }

    cfg_feature! {
        #![all(feature = "unix", unix)]
        /// Returns Unix socket address.
        #[inline]
        #[must_use] pub fn as_unix(&self) -> Option<&tokio::net::unix::SocketAddr> {
            match self {
                Self::Unix(addr) => Some(addr),
                _ => None,
            }
        }
    }
}

impl Display for SocketAddr {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Self::Unknown => write!(f, "unknown"),
            Self::IPv4(addr) => write!(f, "socket://{addr}"),
            Self::IPv6(addr) => write!(f, "socket://{addr}"),
            #[cfg(unix)]
            Self::Unix(addr) => match addr.as_pathname() {
                Some(path) => write!(f, "unix://{}", path.display()),
                None => f.write_str("unix://unknown"),
            },
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_addr_ipv4() {
        let ipv4: std::net::SocketAddr = "127.0.0.1:8080".parse().unwrap();
        let ipv4: SocketAddr = ipv4.into();
        assert!(ipv4.is_ipv4());
        assert!(!ipv4.is_ipv6());
        #[cfg(all(feature = "unix", unix))]
        assert!(!ipv4.is_unix());
        assert_eq!(ipv4.as_ipv4().unwrap().to_string(), "127.0.0.1:8080");
        assert!(ipv4.as_ipv6().is_none());
        #[cfg(all(feature = "unix", unix))]
        assert!(ipv4.as_unix().is_none());
    }

    #[tokio::test]
    async fn test_addr_ipv6() {
        let ipv6 = std::net::SocketAddr::new(
            std::net::IpAddr::V6(std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 65535, 0, 1)),
            8080,
        );
        let ipv6: SocketAddr = ipv6.into();
        assert!(!ipv6.is_ipv4());
        assert!(ipv6.is_ipv6());
        #[cfg(all(feature = "unix", unix))]
        assert!(!ipv6.is_unix());
        assert!(ipv6.as_ipv4().is_none());
        assert_eq!(ipv6.as_ipv6().unwrap().to_string(), "[::ffff:0.0.0.1]:8080");
        #[cfg(all(feature = "unix", unix))]
        assert!(ipv6.as_unix().is_none());
    }
}