plod 0.4.0

Plod - deriving plain old data
Documentation
use plod::*;
use std::fmt::Debug;
use std::io::{Read, Write};

#[derive(Plod, PartialEq, Debug)]
#[plod(tag_type(u8))]
enum TestEnum1 {
    #[plod(tag = 1)]
    A { x: u8, y: i16, z: u128 },
    #[plod(tag = 2, size_type(u32), byte_sized)]
    B { x: u8, val: Vec<i16> },
    #[plod(skip)]
    C,
    #[plod(skip)]
    D(u16),
}

#[derive(Plod, PartialEq, Debug)]
#[plod(tag_type(i8))]
enum TestEnum2 {
    #[plod(tag = 1)]
    A(TestStruct1),
    #[plod(tag = 2)]
    B(),
    #[plod(tag = 3)]
    C,
    #[plod(tag = 4, size_type(u16))]
    D(Vec<TestEnum1>),
    #[plod(tag = 5, keep_tag)]
    E(i8, u8),
    #[plod(tag=6..=8|10, keep_tag)]
    F(i8, u8),
    #[plod(keep_tag, keep_diff=-5)]
    G(i8, u8),
}

#[derive(Plod, PartialEq, Debug)]
#[plod(magic(u16 = 0xbaba))]
struct TestStruct1 {
    a: u16,
    #[plod(size_type(u32))]
    b: Vec<u8>,
    c: u32,
    #[plod(skip)]
    d: Option<u32>,
    e: (),
    f: (u16, u32),
    g: [u16; 3],
    #[plod(skip)]
    h: i32,
}

#[derive(Plod, PartialEq, Debug)]
struct TestStruct2(u16, TestEnum1);

#[derive(Plod, PartialEq, Debug)]
struct TestStruct3 {
    #[plod(size_type(u16))]
    a: Vec<u8>,
    b: TestStruct2,
    c: TestEnum2,
    d: TestEnum2,
    e: TestEnum2,
    f: TestEnum2,
    g: TestEnum2,
    h: TestEnum2,
    i: TestEnum2,
}

#[test]
fn test_structs() {
    let a1 = TestEnum1::A {
        x: 1,
        y: -1,
        z: u128::MAX,
    };
    let a1s = 1 + 1 + 2 + 16;
    assert_eq!(a1.size_at_rest(), a1s, "a1");
    it_reads_what_it_writes(&a1);

    let b1 = TestEnum1::B {
        x: 1,
        val: vec![1, -1, 3],
    };
    let b1s = 1 + 1 + 4 + 2 * 3;
    assert_eq!(b1.size_at_rest(), b1s, "b1");
    it_reads_what_it_writes(&b1);

    let s1 = TestStruct1 {
        a: 1,
        b: vec![1, 2, 3],
        c: 5,
        d: None,
        e: (),
        f: (1, 2),
        g: [1, 2, 3],
        h: 0,
    };
    let s1s = 2 + 2 + 4 + 3 + 4 + (2 + 4) + 3 * 2;
    assert_eq!(s1.size_at_rest(), s1s, "s1");
    it_reads_what_it_writes(&s1);

    let a2 = TestEnum2::A(s1);
    let a2s = 1 + s1s;
    assert_eq!(a2.size_at_rest(), a2s, "a2");
    it_reads_what_it_writes(&a2);

    let b2 = TestEnum2::B();
    let b2s = 1;
    assert_eq!(b2.size_at_rest(), b2s, "b2");
    it_reads_what_it_writes(&b2);

    let c2 = TestEnum2::C;
    let c2s = 1;
    assert_eq!(c2.size_at_rest(), c2s, "c2");
    it_reads_what_it_writes(&c2);

    let d2 = TestEnum2::D(vec![a1]);
    let d2s = 1 + 2 + a1s;
    assert_eq!(d2.size_at_rest(), d2s, "d2");
    it_reads_what_it_writes(&d2);

    let e2 = TestEnum2::E(5, 2);
    let e2s = 1 + 1;
    assert_eq!(e2.size_at_rest(), e2s, "e2");
    it_reads_what_it_writes(&e2);

    let f2 = TestEnum2::F(7, 2);
    let f2s = 1 + 1;
    assert_eq!(f2.size_at_rest(), f2s, "f2");
    it_reads_what_it_writes(&f2);

    let g2 = TestEnum2::G(14, 2);
    let g2s = 1 + 1;
    assert_eq!(g2.size_at_rest(), g2s, "g2");
    it_reads_what_it_writes(&g2);

    let s2 = TestStruct2(1234, b1);
    let s2s = 2 + b1s;
    assert_eq!(s2.size_at_rest(), s2s, "s2");
    it_reads_what_it_writes(&s2);

    let s3 = TestStruct3 {
        a: vec![9, 8, 7, 6],
        b: s2,
        c: a2,
        d: b2,
        e: c2,
        f: d2,
        g: e2,
        h: f2,
        i: g2,
    };
    let s3s = 2 + 4 + s2s + a2s + b2s + c2s + d2s + e2s + f2s + g2s;
    assert_eq!(s3.size_at_rest(), s3s, "s3");
    it_reads_what_it_writes(&s3);
}

fn it_reads_what_it_writes<T: Plod<Context = ()> + PartialEq + Debug>(t: &T) {
    let mut memory: Vec<u8> = Vec::new();
    let r = t.write_to(&mut memory, &());
    if r.is_err() {
        println!("Write error {:?}", r);
    }
    assert!(r.is_ok());

    println!("data {:?}", memory);
    let mut mem = std::io::Cursor::new(memory);
    let result = T::read_from(&mut mem, &());
    assert!(result.is_ok(), "read struct error");
    assert_eq!(t, &result.unwrap());
}

#[derive(Plod, PartialEq, Debug)]
#[plod(big_endian, magic(u16 = 0xabcd))]
struct TestMagic {
    a: u16,
}

#[test]
fn test_magic() {
    let big = TestMagic { a: 0x1234 };
    let mut memory: Vec<u8> = Vec::new();
    assert!(big.write_to(&mut memory, &()).is_ok());
    assert_eq!(memory, vec![0xab, 0xcd, 0x12, 0x34]);
}

#[test]
fn test_option() {
    let s1 = TestStruct1 {
        a: 1,
        b: vec![1, 2, 3],
        c: 5,
        d: Some(45),
        e: (),
        f: (1, 2),
        g: [2, 3, 4],
        h: 10,
    };
    let mut memory: Vec<u8> = Vec::new();
    assert!(s1.write_to(&mut memory, &()).is_ok());

    let mut mem = std::io::Cursor::new(memory);
    let result = TestStruct1::read_from(&mut mem, &());
    assert!(result.is_ok());

    let s2 = TestStruct1 {
        a: 1,
        b: vec![1, 2, 3],
        c: 5,
        d: None,
        e: (),
        f: (1, 2),
        g: [2, 3, 4],
        h: 0,
    };
    assert_eq!(s2, result.unwrap());
}

#[derive(Plod, PartialEq, Debug)]
struct TestVec<T: Plod<Context = ()>> {
    #[plod(size_type(u16))]
    a: Vec<u32>,
    #[plod(size_type(u16))]
    b: Vec<(u16, u16)>,
    #[plod(size_type(u16))]
    c: Vec<T>,
    #[plod(size_type(u16))]
    d: Vec<TestVec<TestEnum1>>,
}

#[test]
fn test_vecs() {
    let vec = TestVec {
        a: vec![1],
        b: vec![(2, 3)],
        c: vec![TestEnum2::E(5, 16)],
        d: vec![],
    };
    it_reads_what_it_writes(&vec);
}

/*
#[test]
fn test_tuple() {
    let t = (1, 2);
    it_reads_what_it_writes(&t);
}*/

#[test]
fn test_skip_fail() {
    let s1 = TestEnum1::C;
    let s2 = TestEnum1::D(0);
    let mut memory: Vec<u8> = Vec::new();
    assert!(Plod::write_to(&s1, &mut memory, &()).is_err());
    assert!(Plod::write_to(&s2, &mut memory, &()).is_err());
}

#[derive(Plod, PartialEq, Debug)]
struct TestGeneric<T: Plod<Context = ()>> {
    a: T,
}
#[test]
fn test_generic() {
    let val = TestGeneric {
        a: TestEnum2::E(5, 123),
    };
    it_reads_what_it_writes(&val);
}

#[derive(Plod, PartialEq, Debug)]
struct Context {
    count: u64,
}

impl Context {
    pub fn get(&self) -> u64 {
        self.count
    }
}
impl From<&Context> for &() {
    fn from(_: &Context) -> Self {
        &()
    }
}

#[derive(Plod, PartialEq, Debug)]
#[plod(context=Context)]
struct TestWithContext {
    a: u16,
    b: TestWithContext2,
}
#[derive(PartialEq, Debug)]
struct TestWithContext2 {
    a: u16,
}
impl Plod for TestWithContext2 {
    type Context = Context;

    fn size_at_rest(&self) -> usize {
        2
    }

    fn read_from<R: Read>(_form: &mut R, ctx: &Self::Context) -> Result<Self> {
        let a = ctx.get() as u16;
        Ok(TestWithContext2 { a })
    }

    fn write_to<W: Write>(&self, to: &mut W, _ctx: &Self::Context) -> Result<()> {
        let buffer: [u8; 2] = self.a.to_ne_bytes();
        to.write_all(&buffer)
    }
}

#[test]
fn test_with_context() {
    let val = TestWithContext {
        a: 123,
        b: TestWithContext2 { a: 0 },
    };
    let ctx = Context { count: 0 };
    let mut memory: Vec<u8> = Vec::new();
    assert!(val.write_to(&mut memory, &ctx).is_ok());

    let mut mem = std::io::Cursor::new(memory);
    let result = TestWithContext::read_from(&mut mem, &ctx);
    assert!(result.is_ok(), "read struct error");
    assert_eq!(&val, &result.unwrap());
}

#[derive(Plod, PartialEq, Debug)]
struct TestPartialContext {
    a: u16,
    #[plod(is_context)]
    c: Context,
    b: TestWithContext,
}

// TODO test with generic in struct
// TODO test endianness mix and match