[][src]Macro bin_io::seq

macro_rules! seq {
    ($($ty:ident)::+ { $($field:ident),* }, $($rest:tt)*) => { ... };
    ($($ty:ident)::+ ( $($field:ident),* ), $($rest:tt)*) => { ... };
    ($($ty:ident)::+, $($rest:tt)*) => { ... };
    ((), $($rest:tt)*) => { ... };
    (__impl r $e:expr, $r:ident, ) => { ... };
    (__impl r $e:expr, $r:ident, $name:ident : $expr:expr => $($rest:tt)*) => { ... };
    (__impl r $e:expr, $r:ident, $name:ident : $expr:expr, $def:expr => $($rest:tt)*) => { ... };
    (__impl r $e:expr, $r:ident, $expr:expr => $($rest:tt)*) => { ... };
    (__impl w $w:ident, ) => { ... };
    (__impl w $w:ident, $name:ident : $expr:expr => $($rest:tt)*) => { ... };
    (__impl w $w:ident, $name:ident : $expr:expr, $def:expr => $($rest:tt)*) => { ... };
    (__impl w $w:ident, $expr:expr => $($rest:tt)*) => { ... };
}

Macro used to generate a write/read tuple from sequence of operations.

Remarks

Due to internal limitations assigning a different name to a variable in the capture is not valid:

This example deliberately fails to compile
// Won't compile!!
seq!(
    Bar { a: a1, b: b1 },
    a1: be_u8 => 
    b1: be_u8 =>
)

While reading variables used are owned copies of the values, while writing the values are references to those values, so it's necessary that you always use either as_ref() or to_owned() to collapse the two states into a reference or an owned copy.

use std::io::Cursor;
use bin_io::{ seq, count, read };
use bin_io::numbers::{ be_u8, be_i16 };
 
#[derive(Debug, PartialEq, Eq)]
struct Foo {
    a: Vec<i16>
}
 
/* Won't compile
let tuple = seq!(
    Foo { a },
    len: be_u8(), a.len() as _ =>
    a: count(be_i16(), len as usize) =>
);
*/
 
let tuple = seq!(
    Foo { a },
    len: be_u8(), a.len() as _ =>
    a: count(be_i16(), len.to_owned() as usize) =>
);
 
let mut vec = vec![ 0x02, 0x00, 0x01, 0x00, 0x02 ];
let mut cursor = Cursor::new(&mut vec);
 
let foo = read(&mut cursor, tuple)
    .unwrap();
 
assert_eq!(foo, Foo { a: vec![ 1, 2 ] });

Examples

use std::io::Cursor;
use bin_io::{ seq, skip, count, bind, read };
use bin_io::numbers::{ be_u8, be_u16, le_u16, be_i32 };
use bin_io::strings::null_utf16;
 
mod bar {
    pub struct Foo {
        pub a: u8,
        pub b: u16,
        pub c: Vec<i32>,
        pub d: String,
    }
}
 
let tuple = seq!(
    // Here we specify wich variables 
    // are inside Foo
    bar::Foo { a, b, c, d },
 
    // And now we start the definition
    bind(be_u8(), 0x50) =>
    a: be_u8() =>
    b: le_u16() =>
    skip(be_u16(), 1557) =>
    c: count(be_i32(), b.to_owned() as usize) =>
    d: null_utf16() =>
);
 
 
let test = read(r, tuple)
    .unwrap();

seq! is compatible with multiple data structures

use std::io::Cursor;
use bin_io::{ seq, read, bind };
use bin_io::numbers::{ be_i8, be_i16, be_i32, be_i64 };
 
mod foo {
    pub struct Bar1;
    pub struct Bar2(pub i32);
    pub struct Bar3 { pub a: i64 }
}
 
let void = seq!(
    (),
    bind(be_i8(), -20) =>
);
 
let bar1 = seq!(
    foo::Bar1,
    bind(be_i16(), 30) =>
);
 
let bar2 = seq!(
    foo::Bar2(a),
    a: be_i32() =>
);
 
let bar3 = seq!(
    foo::Bar3 { a },
    a: be_i64() =>
);
 

Sometimes you need extra variables during reading, but you don't want them in your final struct (imagine length/value based formats), with seq! you can do that too!

use std::io::Cursor;
use bin_io::{ seq, read, count };
use bin_io::numbers::{ be_u8, be_i16 };
 
#[derive(Debug, PartialEq, Eq)]
struct Foo {
    a: Vec<i16>
}
 
let tuple = seq!(
    // Capture everything normally
    Foo { a },
    // Give the field a default value or some expression to initialize it
    // Remember: this value is only used during writing and not reading
    length: be_u8(), a.len() as u8 =>
    a: count(be_i16(), length.to_owned() as _) =>
);
 
let vec = vec![ 0x2, 0x0, 0x50, 0x0, 0x60 ];
let mut cursor = Cursor::new(vec);
 
let foo = read(&mut cursor, tuple)
    .unwrap();
 
assert_eq!(foo, Foo { a: vec![ 0x50, 0x60 ] })