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)*) => { ... };
}Expand description
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:
ⓘ
// 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 ] })