decon-spf 0.3.3

This is a simple tool that allows you to deconstruct an existing SPF record that might be retreived in a normal DNS TXT lookup. With version 0.2.0 you can now also construct a new Spf record.
Documentation
use crate::SpfBuilder;
use crate::SpfError;
mod redirect {
    use super::*;
    mod valid {
        use super::*;
        use crate::spf::builder::Parsed;
        #[test]
        fn test_redirect() {
            let input = "v=spf1 redirect=_spf.google.com";

            let spf: SpfBuilder<Parsed> = input.parse().unwrap();

            assert_eq!(spf.is_redirect(), true);
            assert_eq!(spf.includes().is_none(), true);
            assert_eq!(spf.a().is_none(), true);
            assert_eq!(spf.mx().is_none(), true);
            assert_eq!(spf.ip4().is_none(), true);
            assert_eq!(spf.ip6().is_none(), true);
            assert_eq!(spf.ptr().is_none(), true);
            assert_eq!(spf.exists().is_none(), true);
            assert_eq!(spf.all().is_none(), true);
            assert_eq!(spf.redirect().unwrap().qualifier().as_str(), "");
            assert_eq!(spf.redirect().unwrap().raw(), "_spf.google.com");
            assert_eq!(
                spf.redirect().unwrap().to_string(),
                "redirect=_spf.google.com"
            );
            assert_eq!(
                spf.to_string(),
                "v=spf1 redirect=_spf.google.com".to_string()
            );
        }
    }
    mod invalid {
        use super::*;
        use crate::Parsed;

        #[test]
        fn redirect_x2() {
            let input = "v=spf1 redirect=_spf.google.com redirect=_spf.example.com";
            let spf = input.parse::<SpfBuilder<Parsed>>().unwrap_err();
            assert!(matches!(spf, SpfError::ModifierMayOccurOnlyOnce(_)))
        }
    }
}
mod valid_spf_from_str {
    use crate::spf::SpfError;
    use crate::SpfBuilder;

    #[test]
    fn test_hotmail() {
        let input = "v=spf1 ip4:157.55.9.128/25 include:spf.protection.outlook.com include:spf-a.outlook.com include:spf-b.outlook.com include:spf-a.hotmail.com include:_spf-ssg-b.microsoft.com include:_spf-ssg-c.microsoft.com ~all";

        let spf: SpfBuilder<_> = input.parse().unwrap();

        assert_eq!(spf.is_redirect(), false);
        assert_eq!(!spf.includes().unwrap().is_empty(), true);
        assert_eq!(spf.includes().unwrap().len(), 6);
        assert_eq!(
            spf.includes().unwrap()[0].to_string(),
            "include:spf.protection.outlook.com"
        );
        assert_eq!(spf.ip4().unwrap().len(), 1);
        assert_eq!(spf.ip4().unwrap()[0].to_string(), "ip4:157.55.9.128/25");
        assert_eq!(spf.all().unwrap().to_string(), "~all");
    }

    #[test]
    fn test_netblocks2_google_com() {
        let input = "v=spf1 ip6:2001:4860:4000::/36 ip6:2404:6800:4000::/36 ip6:2607:f8b0:4000::/36 ip6:2800:3f0:4000::/36 ip6:2a00:1450:4000::/36 ip6:2c0f:fb50:4000::/36 ~all";

        let spf: SpfBuilder<_> = input.parse().unwrap();
        assert_eq!(spf.includes().is_none(), true);
        assert_eq!(spf.ip4().is_none(), true);
        assert_eq!(!spf.ip6().is_none(), true);
        assert_eq!(spf.ip6().unwrap().len(), 6);
        assert_eq!(spf.ip6().unwrap()[0].to_string(), "ip6:2001:4860:4000::/36");
        assert_eq!(
            spf.ip6().unwrap()[0].as_network().to_string(),
            "2001:4860:4000::/36"
        );
        assert_eq!(spf.all().unwrap().to_string(), "~all");
    }

    #[test]
    fn valid_spf1() {
        let input = "v=spf1 a";
        let spf: Result<SpfBuilder<_>, SpfError> = input.parse();
        assert_eq!(spf.is_ok(), true);
    }

    #[test]
    fn valid_spf2() {
        let input = "spf2.0/pra a";
        let spf: SpfBuilder<_> = input.parse().unwrap();

        assert_eq!(spf.version(), "spf2.0/pra");
    }
}

#[cfg(test)]
mod invalid_spf_from_str {
    use crate::spf::SpfError;
    use crate::{Parsed, SpfBuilder};

    #[test]
    fn invalid_spf1() {
        let input = "v=sf a";
        let spf: Result<SpfBuilder<_>, SpfError> = input.parse();
        assert_eq!(spf.is_err(), true);
        let err = spf.unwrap_err();
        assert_eq!(err.is_spf_error(), true);
        assert_eq!(err.version_is_invalid(), true);
        assert_eq!(err.to_string(), "Version string not valid.");
        assert_eq!(err, SpfError::InvalidVersion);
    }

    #[test]
    fn invalid_spf2() {
        let input = "spf2 a";
        let spf: Result<SpfBuilder<Parsed>, SpfError> = input.parse();
        assert_eq!(spf.is_err(), true);
    }

    #[test]
    fn valid_spf2_pra() {
        let input = "spf2.0/pra a";
        let spf: Result<SpfBuilder<Parsed>, SpfError> = input.parse();
        assert_eq!(spf.is_ok(), true);
    }

    #[test]
    fn valid_spf2_mfrom() {
        let input = "spf2.0/mfrom a";
        let spf: Result<SpfBuilder<Parsed>, SpfError> = input.parse();
        assert_eq!(spf.is_ok(), true);
    }

    #[test]
    fn valid_spf2_mfrom_pra() {
        let input = "spf2.0/mfrom,pra a";
        let spf: Result<SpfBuilder<Parsed>, SpfError> = input.parse();
        assert_eq!(spf.is_ok(), true);
    }

    #[test]
    fn valid_spf2_pra_mfrom() {
        let input = "spf2.0/pra,mfrom a";
        let spf: Result<SpfBuilder<Parsed>, SpfError> = input.parse();
        assert_eq!(spf.is_ok(), true);
    }
}

#[cfg(test)]
mod invalid_ip {
    use crate::spf::builder::Parsed;
    use crate::spf::mechanism::MechanismError::InvalidIPNetwork;
    use crate::spf::SpfError;
    use crate::SpfBuilder;
    use crate::SpfError::InvalidMechanism;
    use ipnetwork::IpNetworkError::InvalidAddr;

    #[test]
    fn invalid_ip4() {
        let input = "v=spf1 ip4:203.32.10.0/33";
        let spf: Result<SpfBuilder<Parsed>, SpfError> = input.parse();
        assert_eq!(spf.is_err(), true);
        let error = spf.unwrap_err();
        assert_eq!(
            error,
            InvalidMechanism(InvalidIPNetwork(InvalidAddr("203.32.10.0/33".to_string())))
        );
        assert_eq!(error.is_invalid_ip_addr(), true);
        assert_eq!(error.to_string(), "invalid address: 203.32.10.0/33");
    }

    #[test]
    fn invalid_ip6() {
        let input = "v=spf1 ip6:2001:4860:4000::/129";
        let spf: Result<SpfBuilder<Parsed>, SpfError> = input.parse();

        assert_eq!(spf.is_err(), true);
        let error = spf.unwrap_err();
        assert_eq!(error.is_invalid_ip_addr(), true);
        assert_eq!(error.to_string(), "invalid address: 2001:4860:4000::/129");
    }
}