i-dunno 0.6.0

RFC 8771 Internationalized Deliberately Unreadable Network Notation
Documentation
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

use crate::bytesbits::BytesBits;

pub fn decode(input: &str) -> Option<IpAddr> {
    let bits = bits_from_utf8_bytes(input.as_bytes());

    // Chunk the bits we found into bytes
    let mut out_vec = Vec::new();
    for bits in bits.chunks(8) {
        out_vec.push(bits_to_byte(bits.iter()))
    }

    if out_vec.len() == 16 {
        Some(
            Ipv6Addr::new(
                u16::from_be_bytes([out_vec[0], out_vec[1]]),
                u16::from_be_bytes([out_vec[2], out_vec[3]]),
                u16::from_be_bytes([out_vec[4], out_vec[5]]),
                u16::from_be_bytes([out_vec[6], out_vec[7]]),
                u16::from_be_bytes([out_vec[8], out_vec[9]]),
                u16::from_be_bytes([out_vec[10], out_vec[11]]),
                u16::from_be_bytes([out_vec[12], out_vec[13]]),
                u16::from_be_bytes([out_vec[14], out_vec[15]]),
            )
            .into(),
        )
    } else if out_vec.len() == 4 {
        Some(
            Ipv4Addr::new(out_vec[0], out_vec[1], out_vec[2], out_vec[3])
                .into(),
        )
    } else {
        None
    }
}

fn bits_from_utf8_bytes(utf8_bytes: &[u8]) -> Vec<bool> {
    let mut ret = Vec::new();
    // Copy out all bits that come after a zero bit
    for byte in utf8_bytes {
        let mut started = false;
        for bit in BytesBits::new(vec![*byte]) {
            if started {
                ret.push(bit);
            } else if bit == false {
                started = true;
            }
        }
    }
    ret
}

fn bits_to_byte<'a, I: Iterator<Item = &'a bool>>(bits: I) -> u8 {
    let mut ret = 0;
    for bit in bits {
        ret <<= 1;
        if *bit {
            ret |= 1;
        }
    }
    ret
}

#[cfg(test)]
mod tests {
    use hex;

    use super::*;
    use crate::encode;
    use crate::ConfusionLevel;

    fn hex2string(input: &str) -> String {
        String::from_utf8(hex::decode(input).unwrap()).unwrap()
    }

    fn assert_roundtrip(ip: &str) {
        assert_eq!(
            ip,
            decode(
                &encode(ip.parse().unwrap(), ConfusionLevel::Minimum).unwrap()
            )
            .unwrap()
            .to_string()
        );
    }

    #[test]
    fn bits_from_utf8_bytes_extracts_the_right_bits() {
        assert_eq!(
            vec![false, false, false, false, false, false, false],
            bits_from_utf8_bytes(&[0b0000000])
        );
        assert_eq!(
            vec![false, true, false, false, false, true, false],
            bits_from_utf8_bytes(&[0b0100010])
        );
        assert_eq!(
            vec![
                true, false, true, false, true, false, true, false, true,
                false, true, true, false, false, true, true, true, true, true,
                false, false, false
            ],
            bits_from_utf8_bytes(&[
                0b11101010, 0b10101010, 0b10110011, 0b10111000
            ])
        );
    }

    #[test]
    fn can_make_byte_from_8_bits() {
        assert_eq!(
            bits_to_byte(
                [false, false, false, false, false, false, true, false].iter()
            ),
            2
        );
    }

    #[test]
    fn decode_a_tiny_number() {
        assert_eq!(
            decode(&hex2string("00000001")).unwrap(),
            Ipv4Addr::new(0, 0, 0, 1)
        );
    }

    #[test]
    fn decode_a_small_number() {
        assert_eq!(
            decode(&hex2string("000000c481")).unwrap(),
            Ipv4Addr::new(0, 0, 1, 1)
        );
        assert_roundtrip("0.0.1.1");
    }

    #[test]
    fn decode_localhost() {
        assert_eq!(
            decode(&hex2string("3fd0800001")).unwrap(),
            Ipv4Addr::new(127, 0, 0, 1)
        );
        assert_eq!(
            decode(&hex2string("cfb8000001")).unwrap(),
            Ipv4Addr::new(127, 0, 0, 1)
        );
        assert_roundtrip("127.0.0.1");
    }

    #[test]
    fn decoding_something_with_only_one_delightful_but_more_satisfactory() {
        assert_eq!(
            decode(&hex2string("3e0800cc8a")).unwrap(),
            Ipv4Addr::new(124, 32, 3, 10)
        );
        assert_eq!(
            decode(&hex2string("cfa100060a")).unwrap(),
            Ipv4Addr::new(124, 32, 3, 10)
        );
        assert_roundtrip("124.32.3.10");
    }

    #[test]
    fn decoding_198_51_100_164() {
        assert_eq!(
            decode(&hex2string("630c6cd2a4")).unwrap(),
            Ipv4Addr::new(198, 51, 100, 164)
        );
        assert_eq!(
            decode(&hex2string("630cdb8924")).unwrap(),
            Ipv4Addr::new(198, 51, 100, 164)
        );
        assert_eq!(
            decode(&hex2string("d8b14d4924")).unwrap(),
            Ipv4Addr::new(198, 51, 100, 164)
        );
        assert_roundtrip("198.51.100.164");
    }

    #[test]
    fn decoding_an_ipv6_address() {
        assert_eq!(
            decode(&hex2string("000000000000000000000000000000e2b08264"))
                .unwrap(),
            IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x16, 0x164))
        );
    }

    #[test]
    fn decoding_an_ipv6_address_with_lots_of_options() {
        assert_eq!(
            decode(&hex2string("1000215b400000000000000851380670e78cb4"))
                .unwrap(),
            IpAddr::V6(Ipv6Addr::new(
                0x2001, 0xdb8, 0, 0, 0, 0x8a2e, 0x370, 0x7334
            ))
        );
    }
}