#![allow(dead_code)]

#[macro_use]
extern crate bitfield;

use std::net::Ipv4Addr;

bitfield! {
    struct IpV4Header(MSB0 [u8]);
    impl Debug;
    u32;
    get_version, _: 3, 0;
    get_ihl, _: 7, 4;
    get_dscp, _: 13, 8;
    get_ecn, _: 15, 14;
    get_total_length, _: 31, 16;
    get_identification, _: 47, 31;
    get_df, _: 49;
    get_mf, _: 50;
    get_fragment_offset, _: 63, 51;
    get_time_to_live, _: 71, 64;
    get_protocol, _: 79, 72;
    get_header_checksum, _: 95, 79;
    u8, get_source_address, _: 103, 96, 4;
    u32, into Ipv4Addr, get_destination_address, _: 159, 128;
}

impl<T: AsRef<[u8]> + AsMut<[u8]>> IpV4Header<T> {
    fn get_source_as_ip_addr(&self) -> Ipv4Addr {
        let mut src = [0; 4];
        for (i, src) in src.iter_mut().enumerate() {
            *src = self.get_source_address(i);
        }
        src.into()
    }
}

fn main() {
    let data = [
        0x45, 0x00, 0x00, 0x40, 0x69, 0x27, 0x40, 0x00, 0x40, 0x11, 0x4d, 0x0d, 0xc0, 0xa8, 0x01,
        0x2a, 0xc0, 0xa8, 0x01, 0xfe,
    ];

    let header = IpV4Header(data);

    assert_eq!(header.get_version(), 4);
    assert_eq!(header.get_total_length(), 64);
    assert_eq!(header.get_identification(), 0x6927);
    assert!(header.get_df());
    assert!(!header.get_mf());
    assert_eq!(header.get_fragment_offset(), 0);
    assert_eq!(header.get_protocol(), 0x11);
    println!(
        "from {} to {}",
        header.get_source_as_ip_addr(),
        header.get_destination_address()
    );

    println!("{:#?}", header);
}
