redirectionio 3.1.0

Redirection IO Library to handle matching rule, redirect and filtering headers and body.
Documentation
use std::{
    net::{IpAddr, SocketAddr},
    str::FromStr,
};

pub struct Addr {
    pub addr: IpAddr,
    pub port: Option<u16>,
}

impl std::fmt::Display for Addr {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let ipv = if self.addr.is_ipv4() { "4" } else { "6" };

        let hex = match self.addr {
            IpAddr::V4(addr) => u32::from(addr) as u128,
            IpAddr::V6(addr) => u128::from(addr),
        };

        match self.port {
            Some(p) => write!(f, "address: {}, port: {}, hex: {:x} (IPv{})", self.addr, p, hex, ipv),

            None => write!(f, "address: {}, port: N/A, hex: {:x} (IPv{}) ", self.addr, hex, ipv),
        }
    }
}

impl FromStr for Addr {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let trimmed = s.trim_matches(|c| c == '\0' || c == '\n' || c == '\r' || c == '\t' || c == ' ');

        if let Ok(addr) = trimmed.parse::<IpAddr>() {
            Ok(Self { addr, port: None })
        } else if let Ok(sock) = trimmed.parse::<SocketAddr>() {
            Ok(Self {
                addr: sock.ip(),
                port: Some(sock.port()),
            })
        } else {
            Err(())
        }
    }
}

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

    #[test]
    fn test_invalid() {
        let addr = "invalid".parse::<Addr>();

        assert!(addr.is_err());
    }

    #[test]
    fn test_ipv4_without_port() {
        let addr = "127.0.0.1".parse::<Addr>().unwrap();

        assert!(addr.addr.is_ipv4());
        assert!(addr.port.is_none());
        assert_eq!(addr.addr.to_string(), "127.0.0.1");
    }

    #[test]
    fn test_ipv4_with_null() {
        let addr = "127.0.0.1\0".parse::<Addr>().unwrap();

        assert!(addr.addr.is_ipv4());
        assert!(addr.port.is_none());
        assert_eq!(addr.addr.to_string(), "127.0.0.1");
    }

    #[test]
    fn test_ipv4_with_port() {
        let addr = "127.0.0.1:8080".parse::<Addr>().unwrap();

        assert!(addr.addr.is_ipv4());
        assert!(addr.port.is_some());
        assert_eq!(addr.port.unwrap(), 8080);
        assert_eq!(addr.addr.to_string(), "127.0.0.1");
    }

    #[test]
    fn test_ipv6_without_port() {
        let addr = "::1".parse::<Addr>().unwrap();

        assert!(addr.addr.is_ipv6());
        assert!(addr.port.is_none());
        assert_eq!(addr.addr.to_string(), "::1");
    }

    #[test]
    fn test_ipv6_with_port() {
        let addr = "[::1]:8080".parse::<Addr>().unwrap();

        assert!(addr.addr.is_ipv6());
        assert!(addr.port.is_some());
        assert_eq!(addr.port.unwrap(), 8080);
        assert_eq!(addr.addr.to_string(), "::1");
    }
}