use bytecoding::Bytecode; use std::fmt; fn test_encode(instructions: &[T], expected_bytes: &[u8]) { let mut bytes = Vec::new(); for i in instructions { i.encode(&mut bytes); } assert_eq!(&bytes, expected_bytes); } fn test_decode(bytes: &[u8], expected_instructions: &[T]) { let mut bytes: &[u8] = &bytes; let mut instructions = Vec::new(); while !bytes.is_empty() { instructions.push(T::decode(&mut bytes).unwrap()); } assert_eq!(instructions, expected_instructions); } fn test_encode_decode(instructions: &[T], expected_bytes: &[u8]) { test_encode(instructions, expected_bytes); test_decode(expected_bytes, instructions); } #[test] fn instruction_type() { #[derive(Debug, PartialEq, Eq, Bytecode)] #[bytecode(type = u8)] enum InstructionU8 { A, B, } test_encode_decode(&[InstructionU8::A, InstructionU8::B], &[0, 1]); #[derive(Debug, PartialEq, Eq, Bytecode)] #[bytecode(type = u16)] enum InstructionU16 { A, B, } test_encode_decode(&[InstructionU16::A, InstructionU16::B], &[0, 0, 0, 1]); #[derive(Debug, PartialEq, Eq, Bytecode)] #[bytecode(type = u32)] enum InstructionU32 { A, B, } test_encode_decode( &[InstructionU32::A, InstructionU32::B], &[0, 0, 0, 0, 0, 0, 0, 1], ); #[derive(Debug, PartialEq, Eq, Bytecode)] #[bytecode(type = u64)] enum InstructionU64 { A, B, } test_encode_decode( &[InstructionU64::A, InstructionU64::B], &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], ); } #[test] fn flatten() { #[derive(Debug, PartialEq, Eq, Bytecode)] #[bytecode(type = u8)] enum Instruction { A(u16, #[bytecode(flatten = [1, 0])] u64), B { #[bytecode(flatten = [10, 11, 12, 13])] a: u32, b: u8, }, C(#[bytecode(flatten = [100])] u8), } let instructions = [ Instruction::A(10, 1), Instruction::A(11, 0), Instruction::A(12, 2), Instruction::B { a: 10, b: 42 }, Instruction::B { a: 13, b: 42 }, Instruction::B { a: 0, b: 42 }, Instruction::B { a: 14, b: 42 }, Instruction::C(100), Instruction::C(99), ]; #[rustfmt::skip] let expected_bytes = [ 0, 0, 10, 1, 0, 11, 2, 0, 12, 0, 0, 0, 0, 0, 0, 0, 2, 3, 42, 6, 42, 7, 0, 0, 0, 0, 42, 7, 0, 0, 0, 14, 42, 8, 9, 99, ]; test_encode_decode(&instructions, &expected_bytes); } #[test] fn flatten_all() { #[derive(Debug, PartialEq, Eq)] enum Foo { Bar, Baz, } #[derive(Debug, PartialEq, Eq, Bytecode)] #[bytecode(type = u8)] enum Instruction { A(#[bytecode(flatten_all = [true, false])] bool), B { #[bytecode(flatten_all = [true, false])] a: bool, }, C(u32, #[bytecode(flatten_all = [false, true])] bool), D { #[bytecode(flatten_all = [false, true])] a: bool, b: u16, c: u8, }, E(#[bytecode(flatten_all = [Foo::Bar, Foo::Baz])] Foo), } let instructions = [ Instruction::A(true), Instruction::A(false), Instruction::B { a: true }, Instruction::B { a: false }, Instruction::C(10, false), Instruction::C(11, true), Instruction::D { a: false, b: 20, c: 1, }, Instruction::D { a: true, b: 21, c: 2, }, Instruction::E(Foo::Bar), Instruction::E(Foo::Baz), ]; #[rustfmt::skip] let expected_bytes = [ 0, 1, 2, 3, 4, 0, 0, 0, 10, 5, 0, 0, 0, 11, 6, 0, 20, 1, 7, 0, 21, 2, 8, 9, ]; test_encode_decode(&instructions, &expected_bytes); } #[test] fn skip() { #[derive(Debug, PartialEq, Eq, Bytecode)] struct Operand { #[bytecode(skip)] value1: String, value2: u8, } #[derive(Debug, PartialEq, Eq, Bytecode)] #[bytecode(type = u8)] enum Instruction { A(#[bytecode(skip)] bool), B { #[bytecode(skip)] a: bool, }, C(u8, #[bytecode(skip)] String), D { #[bytecode(skip)] a: usize, b: u8, c: u16, }, E(Operand), } let instructions = [ Instruction::A(true), Instruction::A(false), Instruction::B { a: true }, Instruction::B { a: false }, Instruction::C(10, "foo".to_owned()), Instruction::C(11, String::new()), Instruction::D { a: 42, b: 20, c: 1 }, Instruction::D { a: 43, b: 21, c: 2 }, Instruction::E(Operand { value1: "bar".to_owned(), value2: 1, }), Instruction::E(Operand { value1: "baz".to_owned(), value2: 2, }), ]; #[rustfmt::skip] let expected_bytes = [ 0, 0, 1, 1, 2, 10, 2, 11, 3, 20, 0, 1, 3, 21, 0, 2, 4, 1, 4, 2, ]; test_encode(&instructions, &expected_bytes); let expected_instructions = [ Instruction::A(false), Instruction::A(false), Instruction::B { a: false }, Instruction::B { a: false }, Instruction::C(10, String::new()), Instruction::C(11, String::new()), Instruction::D { a: 0, b: 20, c: 1 }, Instruction::D { a: 0, b: 21, c: 2 }, Instruction::E(Operand { value1: String::new(), value2: 1, }), Instruction::E(Operand { value1: String::new(), value2: 2, }), ]; test_decode(&expected_bytes, &expected_instructions); } #[test] fn explicit_code() { #[derive(Debug, PartialEq, Eq, Bytecode)] #[bytecode(type = u8)] enum Instruction { #[bytecode(code = 3)] A, #[bytecode(code = [1])] B, #[bytecode(code = 42)] C, #[bytecode(code = [10, 11, 12, 13])] D(#[bytecode(flatten = [0, 1, 2])] u8), #[bytecode(code = [20, 21, 22, 23, 24, 25])] E { #[bytecode(flatten = [0, 1])] a: u16, #[bytecode(flatten_all = [true, false])] b: bool, }, } let instructions = [ Instruction::A, Instruction::B, Instruction::C, Instruction::D(0), Instruction::D(1), Instruction::D(2), Instruction::D(3), Instruction::E { a: 0, b: true }, Instruction::E { a: 0, b: false }, Instruction::E { a: 1, b: true }, Instruction::E { a: 1, b: false }, Instruction::E { a: 2, b: true }, Instruction::E { a: 2, b: false }, ]; #[rustfmt::skip] let expected_bytes = [ 3, 1, 42, 10, 11, 12, 13, 3, 20, 21, 22, 23, 24, 0, 2, 25, 0, 2, ]; test_encode_decode(&instructions, &expected_bytes); } #[test] fn explicit_and_implicit_code() { #[derive(Debug, PartialEq, Eq, Bytecode)] #[bytecode(type = u8)] enum Instruction { A, #[bytecode(code = 0)] B, #[bytecode(code = 42)] C, D, #[bytecode(code = [2])] E, F { #[bytecode(flatten_all = [true, false])] a: bool, b: u32, }, #[bytecode(code = [10, 11])] G(#[bytecode(flatten = [1])] u16), } let instructions = [ Instruction::A, Instruction::B, Instruction::C, Instruction::D, Instruction::E, Instruction::F { a: true, b: 0 }, Instruction::F { a: false, b: 1 }, Instruction::G(0), Instruction::G(1), Instruction::G(2), ]; #[rustfmt::skip] let expected_bytes = [ 1, 0, 42, 3, 2, 4, 0, 0, 0, 0, 5, 0, 0, 0, 1, 11, 0, 0, 10, 11, 0, 2, ]; test_encode_decode(&instructions, &expected_bytes); }