writing data.
+| rw | [`stream`](#stream-access-and-manipulation) | struct, non-unit enum, unit-like enum | Exposes the underlying readwrite stream.
| r | [`temp`](#temp) | field | Uses a field as a temporary variable. Only usable with the [`binread`](macro@crate::binread) attribute macro.
| r | [`try`](#try) | field | Tries to parse and stores the [`default`](core::default::Default) value for the type if parsing fails instead of returning an error.
| rw | [`try_map`](#map) | all except unit variant | Like `map`, but returns a [`BinResult`](crate::BinResult).
@@ -2177,6 +2179,272 @@ position is reset to where it was before
parsingserialisation
started.
+# Stream access and manipulation
+
+The `stream` directive allows direct access to the underlying
+readwrite stream on a struct or
+enum:
+
+
+
+```text
+#[br(stream = $ident:ident)] or #[br(stream($ident:ident))]
+```
+
+
+
+```text
+#[bw(stream = $ident:ident)] or #[bw(stream($ident:ident))]
+```
+
+
+The `map_stream` directive allows the readwrite
+stream to be replaced with another stream when readingwriting
+an object or field:
+
+
+
+```text
+#[br(map_stream = $map_fn:expr)] or #[br(map_stream($map_fn:expr))]
+```
+
+
+
+```text
+#[bw(map_stream = $map_fn:expr)] or #[bw(map_stream($map_fn:expr))]
+```
+
+
+The map function can be a plain function, closure, or call expression which
+returns a plain function or closure. The returned object must implement
+[`Read`](crate::io::Read) + [`Seek`](crate::io::Seek).
+
+## Examples
+
+
+
+### Verifying a checksum
+
+```
+# use core::num::Wrapping;
+# use binrw::{binread, BinRead, io::{Cursor, Read, Seek, SeekFrom}};
+#
+# struct Checksum {
+# inner: T,
+# check: Wrapping,
+# }
+#
+# impl Checksum {
+# fn new(inner: T) -> Self {
+# Self {
+# inner,
+# check: Wrapping(0),
+# }
+# }
+#
+# fn check(&self) -> u8 {
+# self.check.0
+# }
+# }
+#
+# impl Read for Checksum {
+# fn read(&mut self, buf: &mut [u8]) -> binrw::io::Result {
+# let size = self.inner.read(buf)?;
+# for b in &buf[0..size] {
+# self.check += b;
+# }
+# Ok(size)
+# }
+# }
+#
+# impl Seek for Checksum {
+# fn seek(&mut self, pos: SeekFrom) -> binrw::io::Result {
+# self.inner.seek(pos)
+# }
+# }
+#
+#[binread]
+# #[derive(Debug, PartialEq)]
+#[br(little, stream = r, map_stream = Checksum::new)]
+struct Test {
+ a: u16,
+ b: u16,
+ #[br(temp, assert(c == r.check() - c, "bad checksum: {:#x?} != {:#x?}", c, r.check() - c))]
+ c: u8,
+}
+
+assert_eq!(
+ Test::read(&mut Cursor::new(b"\x01\x02\x03\x04\x0a")).unwrap(),
+ Test {
+ a: 0x201,
+ b: 0x403,
+ }
+);
+```
+
+### Reading encrypted blocks
+
+```
+# use binrw::{binread, BinRead, io::{Cursor, Read, Seek, SeekFrom}, helpers::until_eof};
+#
+# struct BadCrypt {
+# inner: T,
+# key: u8,
+# }
+#
+# impl BadCrypt {
+# fn new(inner: T, key: u8) -> Self {
+# Self { inner, key }
+# }
+# }
+#
+# impl Read for BadCrypt {
+# fn read(&mut self, buf: &mut [u8]) -> binrw::io::Result {
+# let size = self.inner.read(buf)?;
+# for b in &mut buf[0..size] {
+# *b ^= core::mem::replace(&mut self.key, *b);
+# }
+# Ok(size)
+# }
+# }
+#
+# impl Seek for BadCrypt {
+# fn seek(&mut self, pos: SeekFrom) -> binrw::io::Result {
+# self.inner.seek(pos)
+# }
+# }
+#
+#[binread]
+# #[derive(Debug, PartialEq)]
+#[br(little)]
+struct Test {
+ iv: u8,
+ #[br(parse_with = until_eof, map_stream = |reader| BadCrypt::new(reader, iv))]
+ data: Vec,
+}
+
+assert_eq!(
+ Test::read(&mut Cursor::new(b"\x01\x03\0\x04\x01")).unwrap(),
+ Test {
+ iv: 1,
+ data: vec![2, 3, 4, 5],
+ }
+);
+```
+
+
+
+
+
+### Writing a checksum
+
+```
+# use core::num::Wrapping;
+# use binrw::{binwrite, BinWrite, io::{Cursor, Write, Seek, SeekFrom}};
+# struct Checksum {
+# inner: T,
+# check: Wrapping,
+# }
+#
+# impl Checksum {
+# fn new(inner: T) -> Self {
+# Self {
+# inner,
+# check: Wrapping(0),
+# }
+# }
+#
+# fn check(&self) -> u8 {
+# self.check.0
+# }
+# }
+#
+# impl Write for Checksum {
+# fn write(&mut self, buf: &[u8]) -> binrw::io::Result {
+# for b in buf {
+# self.check += b;
+# }
+# self.inner.write(buf)
+# }
+#
+# fn flush(&mut self) -> binrw::io::Result<()> {
+# self.inner.flush()
+# }
+# }
+#
+# impl Seek for Checksum {
+# fn seek(&mut self, pos: SeekFrom) -> binrw::io::Result {
+# self.inner.seek(pos)
+# }
+# }
+#
+#[binwrite]
+#[bw(little, stream = w, map_stream = Checksum::new)]
+struct Test {
+ a: u16,
+ b: u16,
+ #[bw(calc(w.check()))]
+ c: u8,
+}
+
+let mut out = Cursor::new(Vec::new());
+Test { a: 0x201, b: 0x403 }.write(&mut out).unwrap();
+
+assert_eq!(out.into_inner(), b"\x01\x02\x03\x04\x0a");
+```
+
+### Writing encrypted blocks
+
+```
+# use binrw::{binwrite, BinWrite, io::{Cursor, Write, Seek, SeekFrom}};
+#
+# struct BadCrypt {
+# inner: T,
+# key: u8,
+# }
+#
+# impl BadCrypt {
+# fn new(inner: T, key: u8) -> Self {
+# Self { inner, key }
+# }
+# }
+#
+# impl Write for BadCrypt {
+# fn write(&mut self, buf: &[u8]) -> binrw::io::Result {
+# let mut w = 0;
+# for b in buf {
+# self.key ^= b;
+# w += self.inner.write(&[self.key])?;
+# }
+# Ok(w)
+# }
+#
+# fn flush(&mut self) -> binrw::io::Result<()> {
+# self.inner.flush()
+# }
+# }
+#
+# impl Seek for BadCrypt {
+# fn seek(&mut self, pos: SeekFrom) -> binrw::io::Result {
+# self.inner.seek(pos)
+# }
+# }
+#
+#[binwrite]
+# #[derive(Debug, PartialEq)]
+#[bw(little)]
+struct Test {
+ iv: u8,
+ #[bw(map_stream = |writer| BadCrypt::new(writer, *iv))]
+ data: Vec,
+}
+
+let mut out = Cursor::new(Vec::new());
+Test { iv: 1, data: vec![2, 3, 4, 5] }.write(&mut out).unwrap();
+assert_eq!(out.into_inner(), b"\x01\x03\0\x04\x01");
+```
+
+
# Temp
diff --git a/binrw/src/private.rs b/binrw/src/private.rs
index d3955406..c90b796c 100644
--- a/binrw/src/private.rs
+++ b/binrw/src/private.rs
@@ -122,6 +122,24 @@ where
args
}
+pub fn map_reader_type_hint<'a, Reader, MapFn, Output>(x: MapFn) -> MapFn
+where
+ Reader: Read + Seek + 'a,
+ MapFn: Fn(&'a mut Reader) -> Output,
+ Output: Read + Seek + 'a,
+{
+ x
+}
+
+pub fn map_writer_type_hint<'a, Writer, MapFn, Output>(x: MapFn) -> MapFn
+where
+ Writer: Write + Seek + 'a,
+ MapFn: Fn(&'a mut Writer) -> Output,
+ Output: Write + Seek + 'a,
+{
+ x
+}
+
pub fn write_fn_type_hint(x: WriterFn) -> WriterFn
where
Args: Clone,
diff --git a/binrw/tests/derive/struct.rs b/binrw/tests/derive/struct.rs
index df2cd258..125d0950 100644
--- a/binrw/tests/derive/struct.rs
+++ b/binrw/tests/derive/struct.rs
@@ -318,6 +318,48 @@ fn magic_const() {
assert_eq!(Test::MAGIC, b'a');
}
+#[test]
+fn map_stream() {
+ use binrw::io::TakeSeekExt;
+
+ #[derive(BinRead, Debug, PartialEq)]
+ #[br(map_stream = |reader| reader.take_seek(4))]
+ struct Test {
+ #[br(parse_with = binrw::helpers::until_eof)]
+ a: Vec,
+ }
+
+ assert_eq!(
+ Test::read_le(&mut Cursor::new(b"hello world")).unwrap(),
+ Test {
+ a: b"hell".to_vec()
+ }
+ );
+}
+
+#[test]
+fn map_stream_field() {
+ use binrw::io::TakeSeekExt;
+
+ #[derive(BinRead, Debug, PartialEq)]
+ struct Test {
+ #[br(map_stream = |reader| reader.take_seek(5), parse_with = binrw::helpers::until_eof)]
+ a: Vec,
+ b: u8,
+ #[br(map_stream = |reader| reader.take_seek(5), parse_with = binrw::helpers::until_eof)]
+ c: Vec,
+ }
+
+ assert_eq!(
+ Test::read_le(&mut Cursor::new(b"hello world")).unwrap(),
+ Test {
+ a: b"hello".to_vec(),
+ b: b' ',
+ c: b"world".to_vec(),
+ }
+ );
+}
+
#[test]
fn named_args_trailing_commas() {
#[rustfmt::skip]
@@ -509,6 +551,61 @@ fn raw_ident() {
Test::read_le(&mut Cursor::new(vec![0x00, 0x00, 0x00, 0x00])).unwrap();
}
+#[test]
+fn reader_var() {
+ struct Checksum {
+ inner: T,
+ check: core::num::Wrapping,
+ }
+
+ impl Checksum {
+ fn new(inner: T) -> Self {
+ Self {
+ inner,
+ check: core::num::Wrapping(0),
+ }
+ }
+
+ fn check(&self) -> u8 {
+ self.check.0
+ }
+ }
+
+ impl binrw::io::Read for Checksum {
+ fn read(&mut self, buf: &mut [u8]) -> binrw::io::Result {
+ let size = self.inner.read(buf)?;
+ for b in &buf[0..size] {
+ self.check += b;
+ }
+ Ok(size)
+ }
+ }
+
+ impl Seek for Checksum {
+ fn seek(&mut self, pos: SeekFrom) -> binrw::io::Result {
+ self.inner.seek(pos)
+ }
+ }
+
+ #[derive(BinRead, Debug, PartialEq)]
+ #[br(little, stream = r, map_stream = Checksum::new)]
+ struct Test {
+ a: u16,
+ b: u16,
+ #[br(calc(r.check()))]
+ c: u8,
+ }
+
+ assert_eq!(
+ Test::read(&mut Cursor::new(b"\x01\x02\x03\x04")).unwrap(),
+ Test {
+ a: 0x201,
+ b: 0x403,
+ c: 10,
+ }
+ );
+}
+
#[test]
fn rewind_on_assert() {
#[allow(dead_code)]
diff --git a/binrw/tests/derive/write/map_stream.rs b/binrw/tests/derive/write/map_stream.rs
new file mode 100644
index 00000000..128fb0f8
--- /dev/null
+++ b/binrw/tests/derive/write/map_stream.rs
@@ -0,0 +1,70 @@
+use binrw::{
+ io::{Cursor, Seek, SeekFrom, Write},
+ BinWrite,
+};
+
+struct BadCrypt {
+ inner: T,
+ key: u8,
+}
+
+impl BadCrypt {
+ fn new(inner: T) -> Self {
+ Self { inner, key: 0 }
+ }
+}
+
+impl Write for BadCrypt {
+ fn write(&mut self, buf: &[u8]) -> binrw::io::Result {
+ let mut w = 0;
+ for b in buf {
+ self.key ^= b;
+ w += self.inner.write(&[self.key])?;
+ }
+ Ok(w)
+ }
+
+ fn flush(&mut self) -> binrw::io::Result<()> {
+ self.inner.flush()
+ }
+}
+
+impl Seek for BadCrypt {
+ fn seek(&mut self, pos: SeekFrom) -> binrw::io::Result {
+ self.inner.seek(pos)
+ }
+}
+
+#[test]
+fn map_stream() {
+ #[derive(BinWrite, Debug, PartialEq)]
+ #[bw(big, map_stream = |inner| BadCrypt { inner, key: 0x80 })]
+ struct Test(Vec);
+
+ let mut out = Cursor::new(vec![]);
+ Test(vec![0, 1, 2, 3]).write(&mut out).unwrap();
+
+ assert_eq!(out.into_inner(), &[0x80, 0x81, 0x83, 0x80],);
+}
+
+#[test]
+fn map_stream_field() {
+ #[derive(BinWrite, Debug, PartialEq)]
+ #[bw(big)]
+ struct Test {
+ #[bw(map_stream = BadCrypt::new)]
+ a: Vec,
+ #[bw(map_stream = |inner| BadCrypt { inner, key: 0x80 })]
+ b: Vec,
+ }
+
+ let mut out = Cursor::new(vec![]);
+ Test {
+ a: vec![0, 1, 2, 3],
+ b: vec![4, 5, 6, 7],
+ }
+ .write(&mut out)
+ .unwrap();
+
+ assert_eq!(out.into_inner(), &[0, 1, 3, 0, 132, 129, 135, 128],);
+}
diff --git a/binrw/tests/derive/write/mod.rs b/binrw/tests/derive/write/mod.rs
index acb4e86e..e8adea3a 100644
--- a/binrw/tests/derive/write/mod.rs
+++ b/binrw/tests/derive/write/mod.rs
@@ -11,7 +11,9 @@ mod ignore;
mod import;
mod magic;
mod map;
+mod map_stream;
mod padding;
mod restore_position;
mod simple;
+mod stream;
mod top_level_map;
diff --git a/binrw/tests/derive/write/stream.rs b/binrw/tests/derive/write/stream.rs
new file mode 100644
index 00000000..8b6f5332
--- /dev/null
+++ b/binrw/tests/derive/write/stream.rs
@@ -0,0 +1,59 @@
+use binrw::{
+ binwrite,
+ io::{Cursor, Seek, SeekFrom, Write},
+ BinWrite,
+};
+
+#[test]
+fn writer_var() {
+ struct Checksum {
+ inner: T,
+ check: core::num::Wrapping,
+ }
+
+ impl Checksum {
+ fn new(inner: T) -> Self {
+ Self {
+ inner,
+ check: core::num::Wrapping(0),
+ }
+ }
+
+ fn check(&self) -> u8 {
+ self.check.0
+ }
+ }
+
+ impl Write for Checksum {
+ fn write(&mut self, buf: &[u8]) -> binrw::io::Result {
+ for b in buf {
+ self.check += b;
+ }
+ self.inner.write(buf)
+ }
+
+ fn flush(&mut self) -> binrw::io::Result<()> {
+ self.inner.flush()
+ }
+ }
+
+ impl Seek for Checksum {
+ fn seek(&mut self, pos: SeekFrom) -> binrw::io::Result {
+ self.inner.seek(pos)
+ }
+ }
+
+ #[binwrite]
+ #[bw(little, stream = w, map_stream = Checksum::new)]
+ struct Test {
+ a: u16,
+ b: u16,
+ #[bw(calc(w.check()))]
+ c: u8,
+ }
+
+ let mut out = Cursor::new(vec![]);
+ Test { a: 0x201, b: 0x403 }.write(&mut out).unwrap();
+
+ assert_eq!(out.into_inner(), b"\x01\x02\x03\x04\x0a");
+}
diff --git a/binrw/tests/ui/invalid_keyword_enum.stderr b/binrw/tests/ui/invalid_keyword_enum.stderr
index 447f66e9..77b20d67 100644
--- a/binrw/tests/ui/invalid_keyword_enum.stderr
+++ b/binrw/tests/ui/invalid_keyword_enum.stderr
@@ -1,5 +1,5 @@
-error: expected one of: `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `magic`, `import`, `import_raw`, `assert`, `pre_assert`, `return_all_errors`, `return_unexpected_error`
- --> $DIR/invalid_keyword_enum.rs:4:6
+error: expected one of: `stream`, `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `map_stream`, `magic`, `import`, `import_raw`, `assert`, `pre_assert`, `return_all_errors`, `return_unexpected_error`
+ --> tests/ui/invalid_keyword_enum.rs:4:6
|
4 | #[br(invalid_enum_keyword)]
| ^^^^^^^^^^^^^^^^^^^^
diff --git a/binrw/tests/ui/invalid_keyword_enum_variant.stderr b/binrw/tests/ui/invalid_keyword_enum_variant.stderr
index 7c5fdf3c..94086714 100644
--- a/binrw/tests/ui/invalid_keyword_enum_variant.stderr
+++ b/binrw/tests/ui/invalid_keyword_enum_variant.stderr
@@ -1,5 +1,5 @@
-error: expected one of: `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `magic`, `import`, `import_raw`, `assert`, `pre_assert`
- --> $DIR/invalid_keyword_enum_variant.rs:5:10
+error: expected one of: `stream`, `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `map_stream`, `magic`, `import`, `import_raw`, `assert`, `pre_assert`
+ --> tests/ui/invalid_keyword_enum_variant.rs:5:10
|
5 | #[br(invalid_enum_variant_keyword)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/binrw/tests/ui/invalid_keyword_struct.stderr b/binrw/tests/ui/invalid_keyword_struct.stderr
index 52f4d0ac..de1346ed 100644
--- a/binrw/tests/ui/invalid_keyword_struct.stderr
+++ b/binrw/tests/ui/invalid_keyword_struct.stderr
@@ -1,5 +1,5 @@
-error: expected one of: `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `magic`, `import`, `import_raw`, `assert`, `pre_assert`
- --> $DIR/invalid_keyword_struct.rs:4:6
+error: expected one of: `stream`, `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `map_stream`, `magic`, `import`, `import_raw`, `assert`, `pre_assert`
+ --> tests/ui/invalid_keyword_struct.rs:4:6
|
4 | #[br(invalid_struct_keyword)]
| ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/binrw/tests/ui/invalid_keyword_struct_field.stderr b/binrw/tests/ui/invalid_keyword_struct_field.stderr
index cd84daab..bb956fc9 100644
--- a/binrw/tests/ui/invalid_keyword_struct_field.stderr
+++ b/binrw/tests/ui/invalid_keyword_struct_field.stderr
@@ -1,5 +1,5 @@
-error: expected one of: `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `magic`, `args`, `args_raw`, `calc`, `default`, `ignore`, `parse_with`, `count`, `offset`, `offset_after`, `if`, `deref_now`, `postprocess_now`, `restore_position`, `try`, `temp`, `assert`, `err_context`, `pad_before`, `pad_after`, `align_before`, `align_after`, `seek_before`, `pad_size_to`, `dbg`
- --> $DIR/invalid_keyword_struct_field.rs:5:10
+error: expected one of: `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `map_stream`, `magic`, `args`, `args_raw`, `calc`, `default`, `ignore`, `parse_with`, `count`, `offset`, `offset_after`, `if`, `deref_now`, `postprocess_now`, `restore_position`, `try`, `temp`, `assert`, `err_context`, `pad_before`, `pad_after`, `align_before`, `align_after`, `seek_before`, `pad_size_to`, `dbg`
+ --> tests/ui/invalid_keyword_struct_field.rs:5:10
|
5 | #[br(invalid_struct_field_keyword)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/binrw/tests/ui/invalid_keyword_unit_enum.stderr b/binrw/tests/ui/invalid_keyword_unit_enum.stderr
index 22b0d2bc..6c4a87f9 100644
--- a/binrw/tests/ui/invalid_keyword_unit_enum.stderr
+++ b/binrw/tests/ui/invalid_keyword_unit_enum.stderr
@@ -1,4 +1,4 @@
-error: expected one of: `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `magic`, `import`, `import_raw`
+error: expected one of: `stream`, `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `map_stream`, `magic`, `import`, `import_raw`
--> tests/ui/invalid_keyword_unit_enum.rs:4:6
|
4 | #[br(invalid_unit_enum_keyword)]
diff --git a/binrw/tests/ui/invalid_keyword_with_imports.stderr b/binrw/tests/ui/invalid_keyword_with_imports.stderr
index d2594dc1..ca2d1f85 100644
--- a/binrw/tests/ui/invalid_keyword_with_imports.stderr
+++ b/binrw/tests/ui/invalid_keyword_with_imports.stderr
@@ -1,5 +1,5 @@
-error: expected one of: `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `magic`, `import`, `import_raw`, `assert`, `pre_assert`
- --> $DIR/invalid_keyword_with_imports.rs:5:6
+error: expected one of: `stream`, `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `map_stream`, `magic`, `import`, `import_raw`, `assert`, `pre_assert`
+ --> tests/ui/invalid_keyword_with_imports.rs:5:6
|
5 | #[br(invalid_struct_keyword)]
| ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/binrw/tests/ui/non_blocking_errors.stderr b/binrw/tests/ui/non_blocking_errors.stderr
index c1b10de5..4fad276a 100644
--- a/binrw/tests/ui/non_blocking_errors.stderr
+++ b/binrw/tests/ui/non_blocking_errors.stderr
@@ -1,17 +1,17 @@
-error: expected one of: `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `magic`, `import`, `import_raw`, `assert`, `pre_assert`
- --> $DIR/non_blocking_errors.rs:6:6
+error: expected one of: `stream`, `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `map_stream`, `magic`, `import`, `import_raw`, `assert`, `pre_assert`
+ --> tests/ui/non_blocking_errors.rs:6:6
|
6 | #[br(invalid_keyword_struct)]
| ^^^^^^^^^^^^^^^^^^^^^^
-error: expected one of: `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `magic`, `args`, `args_raw`, `calc`, `default`, `ignore`, `parse_with`, `count`, `offset`, `offset_after`, `if`, `deref_now`, `postprocess_now`, `restore_position`, `try`, `temp`, `assert`, `err_context`, `pad_before`, `pad_after`, `align_before`, `align_after`, `seek_before`, `pad_size_to`, `dbg`
- --> $DIR/non_blocking_errors.rs:8:10
+error: expected one of: `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `map_stream`, `magic`, `args`, `args_raw`, `calc`, `default`, `ignore`, `parse_with`, `count`, `offset`, `offset_after`, `if`, `deref_now`, `postprocess_now`, `restore_position`, `try`, `temp`, `assert`, `err_context`, `pad_before`, `pad_after`, `align_before`, `align_after`, `seek_before`, `pad_size_to`, `dbg`
+ --> tests/ui/non_blocking_errors.rs:8:10
|
8 | #[br(invalid_keyword_struct_field_a)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: expected one of: `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `magic`, `args`, `args_raw`, `calc`, `default`, `ignore`, `parse_with`, `count`, `offset`, `offset_after`, `if`, `deref_now`, `postprocess_now`, `restore_position`, `try`, `temp`, `assert`, `err_context`, `pad_before`, `pad_after`, `align_before`, `align_after`, `seek_before`, `pad_size_to`, `dbg`
- --> $DIR/non_blocking_errors.rs:10:10
+error: expected one of: `big`, `little`, `is_big`, `is_little`, `map`, `try_map`, `repr`, `map_stream`, `magic`, `args`, `args_raw`, `calc`, `default`, `ignore`, `parse_with`, `count`, `offset`, `offset_after`, `if`, `deref_now`, `postprocess_now`, `restore_position`, `try`, `temp`, `assert`, `err_context`, `pad_before`, `pad_after`, `align_before`, `align_after`, `seek_before`, `pad_size_to`, `dbg`
+ --> tests/ui/non_blocking_errors.rs:10:10
|
10 | #[br(invalid_keyword_struct_field_b)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/binrw_derive/src/binrw/codegen/read_options.rs b/binrw_derive/src/binrw/codegen/read_options.rs
index a6a8b9b2..cdb50c61 100644
--- a/binrw_derive/src/binrw/codegen/read_options.rs
+++ b/binrw_derive/src/binrw/codegen/read_options.rs
@@ -8,18 +8,19 @@ use crate::{
codegen::{
get_endian,
sanitization::{
- ARGS, ASSERT_MAGIC, BIN_ERROR, OPT, POS, READER, SEEK_FROM, SEEK_TRAIT,
+ ARGS, ASSERT_MAGIC, BIN_ERROR, MAP_READER_TYPE_HINT, OPT, POS, READER, SEEK_FROM,
+ SEEK_TRAIT,
},
},
parser::{Input, Magic, Map},
},
- util::IdentStr,
+ util::{quote_spanned_any, IdentStr},
};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use r#enum::{generate_data_enum, generate_unit_enum};
use r#struct::{generate_struct, generate_unit_struct};
-use syn::Ident;
+use syn::{spanned::Spanned, Ident};
pub(crate) fn generate(input: &Input, derive_input: &syn::DeriveInput) -> TokenStream {
let name = Some(&derive_input.ident);
@@ -42,12 +43,15 @@ pub(crate) fn generate(input: &Input, derive_input: &syn::DeriveInput) -> TokenS
},
};
+ let reader_var = input.stream_ident_or(READER);
+
quote! {
- let #POS = #SEEK_TRAIT::stream_position(#READER)?;
+ let #reader_var = #READER;
+ let #POS = #SEEK_TRAIT::stream_position(#reader_var)?;
(|| {
#inner
})().or_else(|error| {
- #SEEK_TRAIT::seek(#READER, #SEEK_FROM::Start(#POS))?;
+ #SEEK_TRAIT::seek(#reader_var, #SEEK_FROM::Start(#POS))?;
Err(error)
})
}
@@ -55,13 +59,16 @@ pub(crate) fn generate(input: &Input, derive_input: &syn::DeriveInput) -> TokenS
struct PreludeGenerator<'input> {
input: &'input Input,
+ reader_var: TokenStream,
out: TokenStream,
}
impl<'input> PreludeGenerator<'input> {
fn new(input: &'input Input) -> Self {
+ let reader_var = input.stream_ident_or(READER);
Self {
input,
+ reader_var,
out: TokenStream::new(),
}
}
@@ -94,7 +101,7 @@ impl<'input> PreludeGenerator<'input> {
fn add_magic_pre_assertion(mut self) -> Self {
let head = self.out;
- let magic = get_magic(self.input.magic(), OPT);
+ let magic = get_magic(self.input.magic(), &self.reader_var, OPT);
let pre_assertions = get_assertions(self.input.pre_assertions());
self.out = quote! {
#head
@@ -105,12 +112,27 @@ impl<'input> PreludeGenerator<'input> {
self
}
+ fn add_map_stream(mut self) -> Self {
+ if let Some(map_stream) = self.input.map_stream() {
+ let outer_reader = self.input.stream_ident_or(READER);
+ let inner_reader = &self.reader_var;
+ let tail = self.out;
+ self.out = quote_spanned_any! { map_stream.span()=>
+ let #inner_reader = &mut #MAP_READER_TYPE_HINT::(#map_stream)(#outer_reader);
+ #tail
+ }
+ }
+
+ self
+ }
+
fn reset_position_after_magic(mut self) -> Self {
if self.input.magic().is_some() {
+ let reader_var = &self.reader_var;
let head = self.out;
self.out = quote! {
#head
- let #POS = #SEEK_TRAIT::stream_position(#READER)?;
+ let #POS = #SEEK_TRAIT::stream_position(#reader_var)?;
};
};
@@ -118,11 +140,15 @@ impl<'input> PreludeGenerator<'input> {
}
}
-fn get_magic(magic: &Magic, endian_var: impl ToTokens) -> Option {
+fn get_magic(
+ magic: &Magic,
+ reader_var: impl ToTokens,
+ endian_var: impl ToTokens,
+) -> Option {
magic.as_ref().map(|magic| {
let magic = magic.deref_value();
quote! {
- #ASSERT_MAGIC(#READER, #magic, #endian_var)?;
+ #ASSERT_MAGIC(#reader_var, #magic, #endian_var)?;
}
})
}
diff --git a/binrw_derive/src/binrw/codegen/read_options/enum.rs b/binrw_derive/src/binrw/codegen/read_options/enum.rs
index 77f6d54e..266dc5ee 100644
--- a/binrw_derive/src/binrw/codegen/read_options/enum.rs
+++ b/binrw_derive/src/binrw/codegen/read_options/enum.rs
@@ -28,8 +28,8 @@ pub(super) fn generate_unit_enum(
.finish();
let read = match en.map.as_repr() {
- Some(repr) => generate_unit_enum_repr(repr, &en.fields),
- None => generate_unit_enum_magic(&en.fields),
+ Some(repr) => generate_unit_enum_repr(&input.stream_ident_or(READER), repr, &en.fields),
+ None => generate_unit_enum_magic(&input.stream_ident_or(READER), &en.fields),
};
quote! {
@@ -38,7 +38,11 @@ pub(super) fn generate_unit_enum(
}
}
-fn generate_unit_enum_repr(repr: &TokenStream, variants: &[UnitEnumField]) -> TokenStream {
+fn generate_unit_enum_repr(
+ reader_var: &TokenStream,
+ repr: &TokenStream,
+ variants: &[UnitEnumField],
+) -> TokenStream {
let clauses = variants.iter().map(|variant| {
let ident = &variant.ident;
let pre_assertions = variant
@@ -54,7 +58,7 @@ fn generate_unit_enum_repr(repr: &TokenStream, variants: &[UnitEnumField]) -> To
});
quote! {
- let #TEMP: #repr = #READ_METHOD(#READER, #OPT, ())?;
+ let #TEMP: #repr = #READ_METHOD(#reader_var, #OPT, ())?;
#(#clauses else)* {
Err(#WITH_CONTEXT(
#BIN_ERROR::NoVariantMatch {
@@ -69,7 +73,7 @@ fn generate_unit_enum_repr(repr: &TokenStream, variants: &[UnitEnumField]) -> To
}
}
-fn generate_unit_enum_magic(variants: &[UnitEnumField]) -> TokenStream {
+fn generate_unit_enum_magic(reader_var: &TokenStream, variants: &[UnitEnumField]) -> TokenStream {
// group fields by the type (Kind) of their magic value, preserve the order
let group_by_magic_type = variants.iter().fold(
Vec::new(),
@@ -117,7 +121,7 @@ fn generate_unit_enum_magic(variants: &[UnitEnumField]) -> TokenStream {
});
let body = quote! {
- match #amp #READ_METHOD(#READER, #OPT, ())? {
+ match #amp #READ_METHOD(#reader_var, #OPT, ())? {
#(#matches,)*
_ => Err(#BIN_ERROR::NoVariantMatch { pos: #POS })
}
@@ -132,7 +136,7 @@ fn generate_unit_enum_magic(variants: &[UnitEnumField]) -> TokenStream {
return #TEMP;
}
- #SEEK_TRAIT::seek(#READER, #SEEK_FROM::Start(#POS))?;
+ #SEEK_TRAIT::seek(#reader_var, #SEEK_FROM::Start(#POS))?;
}
});
@@ -182,6 +186,8 @@ pub(super) fn generate_data_enum(input: &Input, name: Option<&Ident>, en: &Enum)
.reset_position_after_magic()
.finish();
+ let reader_var = input.stream_ident_or(READER);
+
let try_each_variant = en.variants.iter().map(|variant| {
let body = generate_variant_impl(en, variant);
@@ -203,7 +209,7 @@ pub(super) fn generate_data_enum(input: &Input, name: Option<&Ident>, en: &Enum)
return #TEMP;
} else {
#handle_error
- #SEEK_TRAIT::seek(#READER, #SEEK_FROM::Start(#POS))?;
+ #SEEK_TRAIT::seek(#reader_var, #SEEK_FROM::Start(#POS))?;
}
}
});
diff --git a/binrw_derive/src/binrw/codegen/read_options/map.rs b/binrw_derive/src/binrw/codegen/read_options/map.rs
index 50c52a2c..3883e9da 100644
--- a/binrw_derive/src/binrw/codegen/read_options/map.rs
+++ b/binrw_derive/src/binrw/codegen/read_options/map.rs
@@ -19,13 +19,14 @@ pub(crate) fn generate_map(input: &Input, name: Option<&Ident>, map: &TokenStrea
let destructure_ref = destructure_ref(input);
let assertions = field_asserts(input).chain(get_assertions(input.assertions()));
+ let reader_var = input.stream_ident_or(READER);
// TODO: replace args with top-level arguments and only
// use `()` as a default
quote! {
#prelude
- #READ_METHOD(#READER, #OPT, ())
+ #READ_METHOD(#reader_var, #OPT, ())
.map(#map)
.and_then(|this| {
#destructure_ref
@@ -55,13 +56,14 @@ pub(crate) fn generate_try_map(
let destructure_ref = destructure_ref(input);
let assertions = field_asserts(input).chain(get_assertions(input.assertions()));
+ let reader_var = input.stream_ident_or(READER);
// TODO: replace args with top-level arguments and only
// use `()` as a default
quote! {
#prelude
- #READ_METHOD(#READER, #OPT, #ARGS).and_then(|value| {
+ #READ_METHOD(#reader_var, #OPT, #ARGS).and_then(|value| {
(#map)(value)#map_err
})
.and_then(|this| {
diff --git a/binrw_derive/src/binrw/codegen/read_options/struct.rs b/binrw_derive/src/binrw/codegen/read_options/struct.rs
index 6880ac76..da064470 100644
--- a/binrw_derive/src/binrw/codegen/read_options/struct.rs
+++ b/binrw_derive/src/binrw/codegen/read_options/struct.rs
@@ -7,9 +7,9 @@ use crate::{
get_assertions, get_endian, get_passed_args,
sanitization::{
make_ident, AFTER_PARSE, ARGS_MACRO, ARGS_TYPE_HINT, BACKTRACE_FRAME,
- BINREAD_TRAIT, COERCE_FN, DBG_EPRINTLN, MAP_ARGS_TYPE_HINT, OPT,
- PARSE_FN_TYPE_HINT, POS, READER, READ_FUNCTION, READ_METHOD, REQUIRED_ARG_TRAIT,
- SAVED_POSITION, SEEK_FROM, SEEK_TRAIT, TEMP, WITH_CONTEXT,
+ BINREAD_TRAIT, COERCE_FN, DBG_EPRINTLN, MAP_ARGS_TYPE_HINT, MAP_READER_TYPE_HINT,
+ OPT, PARSE_FN_TYPE_HINT, POS, READER, READ_FUNCTION, READ_METHOD,
+ REQUIRED_ARG_TRAIT, SAVED_POSITION, SEEK_FROM, SEEK_TRAIT, TEMP, WITH_CONTEXT,
},
},
parser::{ErrContext, FieldMode, Input, Map, Struct, StructField},
@@ -81,10 +81,17 @@ impl<'input> StructGenerator<'input> {
.st
.fields
.iter()
- .map(|field| generate_field(field, name, variant_name));
+ .map(|field| generate_field(self.input, field, name, variant_name));
let after_parse = {
- let after_parse = self.st.fields.iter().map(generate_after_parse);
- wrap_save_restore(quote!(#(#after_parse)*))
+ let after_parse = self
+ .st
+ .fields
+ .iter()
+ .map(|field| generate_after_parse(self.input, field));
+ wrap_save_restore(
+ &self.input.stream_ident_or(READER),
+ quote!(#(#after_parse)*),
+ )
};
self.out = quote! {
#prelude
@@ -114,13 +121,13 @@ impl<'input> StructGenerator<'input> {
}
}
-fn generate_after_parse(field: &StructField) -> Option {
+fn generate_after_parse(input: &Input, field: &StructField) -> Option {
if field.deref_now.is_none() {
get_after_parse_handler(field).map(|after_parse_fn| {
- let (args_var, endian_var) = make_field_vars(field);
+ let (reader_var, endian_var, args_var) = make_field_vars(input, field);
AfterParseCallGenerator::new(field)
.get_value_from_ident()
- .call_after_parse(after_parse_fn, &endian_var, &args_var)
+ .call_after_parse(after_parse_fn, &reader_var, &endian_var, &args_var)
.finish()
})
} else {
@@ -129,6 +136,7 @@ fn generate_after_parse(field: &StructField) -> Option {
}
fn generate_field(
+ input: &Input,
field: &StructField,
name: Option<&Ident>,
variant_name: Option<&str>,
@@ -138,7 +146,7 @@ fn generate_field(
return TokenStream::new();
}
- FieldGenerator::new(field)
+ FieldGenerator::new(input, field)
.read_value()
.try_conversion(name, variant_name)
.map_value()
@@ -153,6 +161,7 @@ fn generate_field(
.prefix_args_and_options()
.prefix_map_function()
.prefix_read_function()
+ .prefix_map_stream()
.finish()
}
@@ -172,6 +181,7 @@ impl<'field> AfterParseCallGenerator<'field> {
fn call_after_parse(
mut self,
after_parse_fn: IdentStr,
+ reader_var: &TokenStream,
endian_var: &TokenStream,
args_var: &Option,
) -> Self {
@@ -194,7 +204,7 @@ impl<'field> AfterParseCallGenerator<'field> {
};
self.out = quote! {
- #after_parse_fn(#value, #READER, #endian_var, #args_arg)?;
+ #after_parse_fn(#value, #reader_var, #endian_var, #args_arg)?;
};
self
@@ -221,25 +231,30 @@ impl<'field> AfterParseCallGenerator<'field> {
struct FieldGenerator<'field> {
field: &'field StructField,
out: TokenStream,
- args_var: Option,
+ outer_reader_var: TokenStream,
+ reader_var: TokenStream,
endian_var: TokenStream,
+ args_var: Option,
}
impl<'field> FieldGenerator<'field> {
- fn new(field: &'field StructField) -> Self {
- let (args_var, endian_var) = make_field_vars(field);
+ fn new(input: &Input, field: &'field StructField) -> Self {
+ let (reader_var, endian_var, args_var) = make_field_vars(input, field);
Self {
field,
out: TokenStream::new(),
- args_var,
+ outer_reader_var: input.stream_ident_or(READER),
+ reader_var,
endian_var,
+ args_var,
}
}
fn append_debug(mut self) -> Self {
if self.field.debug.is_some() {
let head = self.out;
+ let reader_var = &self.reader_var;
let ident = &self.field.ident;
let at = if ident.span().start().line == 0 {
quote!(::core::line!())
@@ -248,7 +263,7 @@ impl<'field> FieldGenerator<'field> {
};
self.out = quote! {
- let #SAVED_POSITION = #SEEK_TRAIT::seek(#READER, #SEEK_FROM::Current(0))?;
+ let #SAVED_POSITION = #SEEK_TRAIT::seek(#reader_var, #SEEK_FROM::Current(0))?;
#head
@@ -290,7 +305,12 @@ impl<'field> FieldGenerator<'field> {
if let Some(after_parse) = get_after_parse_handler(self.field) {
let after_parse = AfterParseCallGenerator::new(self.field)
.get_value_from_temp()
- .call_after_parse(after_parse, &self.endian_var, &self.args_var)
+ .call_after_parse(
+ after_parse,
+ &self.reader_var,
+ &self.endian_var,
+ &self.args_var,
+ )
.finish();
let value = self.out;
@@ -321,8 +341,9 @@ impl<'field> FieldGenerator<'field> {
// TODO: Position should always just be saved once for a field if used
let value = self.out;
let map_err = super::get_map_err(SAVED_POSITION);
+ let reader_var = &self.reader_var;
quote! {{
- let #SAVED_POSITION = #SEEK_TRAIT::stream_position(#READER)?;
+ let #SAVED_POSITION = #SEEK_TRAIT::stream_position(#reader_var)?;
#map_func(#value)#map_err?
}}
@@ -368,6 +389,20 @@ impl<'field> FieldGenerator<'field> {
self
}
+ fn prefix_map_stream(mut self) -> Self {
+ if let Some(map_stream) = &self.field.map_stream {
+ let rest = self.out;
+ let reader_var = &self.reader_var;
+ let outer_reader_var = &self.outer_reader_var;
+ self.out = quote_spanned_any! { map_stream.span()=>
+ let #reader_var = &mut #MAP_READER_TYPE_HINT::(#map_stream)(#outer_reader_var);
+ #rest
+ };
+ }
+
+ self
+ }
+
fn prefix_read_function(mut self) -> Self {
let read_function = match &self.field.read_mode {
FieldMode::Function(parser) => {
@@ -434,7 +469,7 @@ impl<'field> FieldGenerator<'field> {
}
fn prefix_magic(mut self) -> Self {
- if let Some(magic) = get_magic(&self.field.magic, &self.endian_var) {
+ if let Some(magic) = get_magic(&self.field.magic, &self.reader_var, &self.endian_var) {
let tail = self.out;
self.out = quote! {
#magic
@@ -451,6 +486,7 @@ impl<'field> FieldGenerator<'field> {
FieldMode::Calc(calc) => quote! { #calc },
read_mode @ (FieldMode::Normal | FieldMode::Function(_)) => {
let args_arg = get_args_argument(self.field, &self.args_var);
+ let reader_var = &self.reader_var;
let endian_var = &self.endian_var;
if let FieldMode::Function(f) = read_mode {
@@ -461,12 +497,12 @@ impl<'field> FieldGenerator<'field> {
// here as a mismatched type instead of later as a
// try-conversion error
quote_spanned_any! { f.span()=>
- (|| #READ_FUNCTION)()(#READER, #endian_var, #args_arg)
+ (|| #READ_FUNCTION)()(#reader_var, #endian_var, #args_arg)
.map(|v| -> #ty { v })
}
} else {
quote! {
- #READ_FUNCTION(#READER, #endian_var, #args_arg)
+ #READ_FUNCTION(#reader_var, #endian_var, #args_arg)
}
}
}
@@ -511,15 +547,15 @@ impl<'field> FieldGenerator<'field> {
fn wrap_restore_position(mut self) -> Self {
if self.field.restore_position.is_some() {
- self.out = wrap_save_restore(self.out);
+ self.out = wrap_save_restore(&self.reader_var, self.out);
}
self
}
fn wrap_seek(mut self) -> Self {
- let seek_before = generate_seek_before(self.field);
- let seek_after = generate_seek_after(self.field);
+ let seek_before = generate_seek_before(&self.reader_var, self.field);
+ let seek_after = generate_seek_after(&self.reader_var, self.field);
if !seek_before.is_empty() || !seek_after.is_empty() {
let value = self.out;
self.out = quote! {{
@@ -603,21 +639,28 @@ fn get_prelude(input: &Input, name: Option<&Ident>) -> TokenStream {
.add_imports(name)
.add_endian()
.add_magic_pre_assertion()
+ .add_map_stream()
.finish()
}
-fn generate_seek_after(field: &StructField) -> TokenStream {
+fn generate_seek_after(reader_var: &TokenStream, field: &StructField) -> TokenStream {
let pad_size_to = field.pad_size_to.as_ref().map(|pad| {
quote! {{
let pad = (#pad) as i64;
- let size = (#SEEK_TRAIT::stream_position(#READER)? - #POS) as i64;
+ let size = (#SEEK_TRAIT::stream_position(#reader_var)? - #POS) as i64;
if size < pad {
- #SEEK_TRAIT::seek(#READER, #SEEK_FROM::Current(pad - size))?;
+ #SEEK_TRAIT::seek(#reader_var, #SEEK_FROM::Current(pad - size))?;
}
}}
});
- let pad_after = field.pad_after.as_ref().map(map_pad);
- let align_after = field.align_after.as_ref().map(map_align);
+ let pad_after = field
+ .pad_after
+ .as_ref()
+ .map(|value| map_pad(reader_var, value));
+ let align_after = field
+ .align_after
+ .as_ref()
+ .map(|value| map_align(reader_var, value));
quote! {
#pad_size_to
@@ -626,17 +669,23 @@ fn generate_seek_after(field: &StructField) -> TokenStream {
}
}
-fn generate_seek_before(field: &StructField) -> TokenStream {
+fn generate_seek_before(reader_var: &TokenStream, field: &StructField) -> TokenStream {
let seek_before = field.seek_before.as_ref().map(|seek| {
quote! {
- #SEEK_TRAIT::seek(#READER, #seek)?;
+ #SEEK_TRAIT::seek(#reader_var, #seek)?;
}
});
- let pad_before = field.pad_before.as_ref().map(map_pad);
- let align_before = field.align_before.as_ref().map(map_align);
+ let pad_before = field
+ .pad_before
+ .as_ref()
+ .map(|value| map_pad(reader_var, value));
+ let align_before = field
+ .align_before
+ .as_ref()
+ .map(|value| map_align(reader_var, value));
let pad_size_to_before = field.pad_size_to.as_ref().map(|_| {
quote! {
- let #POS = #SEEK_TRAIT::stream_position(#READER)?;
+ let #POS = #SEEK_TRAIT::stream_position(#reader_var)?;
}
});
@@ -656,11 +705,14 @@ fn get_return_type(variant_ident: Option<&Ident>) -> TokenStream {
variant_ident.map_or_else(|| quote! { Self }, |ident| quote! { Self::#ident })
}
-fn make_field_vars(field: &StructField) -> (Option, TokenStream) {
- let args_var = if field.needs_args() {
- Some(make_ident(&field.ident, "args"))
+fn make_field_vars(
+ input: &Input,
+ field: &StructField,
+) -> (TokenStream, TokenStream, Option) {
+ let reader_var = if field.map_stream.is_some() {
+ make_ident(&field.ident, "reader").into_token_stream()
} else {
- None
+ input.stream_ident_or(READER)
};
let endian_var = if field.needs_endian() {
@@ -669,31 +721,37 @@ fn make_field_vars(field: &StructField) -> (Option, TokenStream) {
OPT.to_token_stream()
};
- (args_var, endian_var)
+ let args_var = if field.needs_args() {
+ Some(make_ident(&field.ident, "args"))
+ } else {
+ None
+ };
+
+ (reader_var, endian_var, args_var)
}
-fn map_align(align: &TokenStream) -> TokenStream {
+fn map_align(reader_var: &TokenStream, align: &TokenStream) -> TokenStream {
quote! {{
let align = (#align) as i64;
- let pos = #SEEK_TRAIT::stream_position(#READER)? as i64;
- #SEEK_TRAIT::seek(#READER, #SEEK_FROM::Current((align - (pos % align)) % align))?;
+ let pos = #SEEK_TRAIT::stream_position(#reader_var)? as i64;
+ #SEEK_TRAIT::seek(#reader_var, #SEEK_FROM::Current((align - (pos % align)) % align))?;
}}
}
-fn map_pad(pad: &TokenStream) -> TokenStream {
+fn map_pad(reader_var: &TokenStream, pad: &TokenStream) -> TokenStream {
quote! {
- #SEEK_TRAIT::seek(#READER, #SEEK_FROM::Current((#pad) as i64))?;
+ #SEEK_TRAIT::seek(#reader_var, #SEEK_FROM::Current((#pad) as i64))?;
}
}
-fn wrap_save_restore(value: TokenStream) -> TokenStream {
+fn wrap_save_restore(reader_var: &TokenStream, value: TokenStream) -> TokenStream {
if value.is_empty() {
value
} else {
quote! {
- let #SAVED_POSITION = #SEEK_TRAIT::stream_position(#READER)?;
+ let #SAVED_POSITION = #SEEK_TRAIT::stream_position(#reader_var)?;
#value
- #SEEK_TRAIT::seek(#READER, #SEEK_FROM::Start(#SAVED_POSITION))?;
+ #SEEK_TRAIT::seek(#reader_var, #SEEK_FROM::Start(#SAVED_POSITION))?;
}
}
}
diff --git a/binrw_derive/src/binrw/codegen/sanitization.rs b/binrw_derive/src/binrw/codegen/sanitization.rs
index 97fcd167..018af03d 100644
--- a/binrw_derive/src/binrw/codegen/sanitization.rs
+++ b/binrw_derive/src/binrw/codegen/sanitization.rs
@@ -45,6 +45,8 @@ ident_str! {
pub(crate) ARGS_TYPE_HINT = from_crate!(__private::parse_function_args_type_hint);
pub(crate) MAP_ARGS_TYPE_HINT = from_crate!(__private::map_args_type_hint);
pub(crate) REQUIRED_ARG_TRAIT = from_crate!(__private::Required);
+ pub(crate) MAP_READER_TYPE_HINT = from_crate!(__private::map_reader_type_hint);
+ pub(crate) MAP_WRITER_TYPE_HINT = from_crate!(__private::map_writer_type_hint);
pub(crate) PARSE_FN_TYPE_HINT = from_crate!(__private::parse_fn_type_hint);
pub(crate) WRITE_FN_TYPE_HINT = from_crate!(__private::write_fn_type_hint);
pub(crate) WRITE_ARGS_TYPE_HINT = from_crate!(__private::write_function_args_type_hint);
diff --git a/binrw_derive/src/binrw/codegen/write_options.rs b/binrw_derive/src/binrw/codegen/write_options.rs
index ee946438..4ab695ee 100644
--- a/binrw_derive/src/binrw/codegen/write_options.rs
+++ b/binrw_derive/src/binrw/codegen/write_options.rs
@@ -31,8 +31,11 @@ pub(crate) fn generate(input: &Input, derive_input: &syn::DeriveInput) -> TokenS
},
};
+ let writer_var = input.stream_ident_or(WRITER);
+
quote! {
- let #POS = #SEEK_TRAIT::stream_position(#WRITER)?;
+ let #writer_var = #WRITER;
+ let #POS = #SEEK_TRAIT::stream_position(#writer_var)?;
#inner
Ok(())
@@ -49,10 +52,11 @@ fn generate_map(input: &Input, name: Option<&Ident>, map: &TokenStream) -> Token
} else {
map.clone()
};
+ let writer_var = input.stream_ident_or(WRITER);
let write_data = quote! {
#WRITE_METHOD(
&((#map)(self) #map_try),
- #WRITER,
+ #writer_var,
#OPT,
()
)?;
@@ -60,7 +64,7 @@ fn generate_map(input: &Input, name: Option<&Ident>, map: &TokenStream) -> Token
let magic = input.magic();
let endian = input.endian();
- prelude::PreludeGenerator::new(write_data, Some(input), name)
+ prelude::PreludeGenerator::new(write_data, Some(input), name, &writer_var)
.prefix_magic(magic)
.prefix_endian(endian)
.prefix_imports()
diff --git a/binrw_derive/src/binrw/codegen/write_options/enum.rs b/binrw_derive/src/binrw/codegen/write_options/enum.rs
index bd5c8ecb..29b21c42 100644
--- a/binrw_derive/src/binrw/codegen/write_options/enum.rs
+++ b/binrw_derive/src/binrw/codegen/write_options/enum.rs
@@ -11,20 +11,22 @@ pub(crate) fn generate_unit_enum(
name: Option<&Ident>,
en: &UnitOnlyEnum,
) -> TokenStream {
+ let writer_var = input.stream_ident_or(WRITER);
let write = match en.map.as_repr() {
- Some(repr) => generate_unit_enum_repr(repr, &en.fields),
- None => generate_unit_enum_magic(&en.fields),
+ Some(repr) => generate_unit_enum_repr(&writer_var, repr, &en.fields),
+ None => generate_unit_enum_magic(&writer_var, &en.fields),
};
- PreludeGenerator::new(write, Some(input), name)
+ PreludeGenerator::new(write, Some(input), name, &writer_var)
.prefix_magic(&en.magic)
.prefix_endian(&en.endian)
.prefix_imports()
+ .prefix_map_stream()
.finish()
}
pub(crate) fn generate_data_enum(input: &Input, name: Option<&Ident>, en: &Enum) -> TokenStream {
- EnumGenerator::new(input, name, en)
+ EnumGenerator::new(input, name, en, input.stream_ident_or(WRITER))
.write_variants()
.prefix_prelude()
.finish()
@@ -34,15 +36,22 @@ struct EnumGenerator<'a> {
en: &'a Enum,
input: &'a Input,
name: Option<&'a Ident>,
+ writer_var: TokenStream,
out: TokenStream,
}
impl<'a> EnumGenerator<'a> {
- fn new(input: &'a Input, name: Option<&'a Ident>, en: &'a Enum) -> Self {
+ fn new(
+ input: &'a Input,
+ name: Option<&'a Ident>,
+ en: &'a Enum,
+ writer_var: TokenStream,
+ ) -> Self {
Self {
input,
name,
en,
+ writer_var,
out: TokenStream::new(),
}
}
@@ -55,11 +64,14 @@ impl<'a> EnumGenerator<'a> {
EnumVariant::Unit(_) => None,
};
+ let writer_var = &self.writer_var;
let writing = match variant {
- EnumVariant::Variant { options, .. } => StructGenerator::new(None, options, None)
- .write_fields()
- .prefix_prelude()
- .finish(),
+ EnumVariant::Variant { options, .. } => {
+ StructGenerator::new(None, options, None, &self.writer_var)
+ .write_fields()
+ .prefix_prelude()
+ .finish()
+ }
EnumVariant::Unit(variant) => variant
.magic
.as_ref()
@@ -68,7 +80,7 @@ impl<'a> EnumGenerator<'a> {
quote! {
#WRITE_METHOD (
magic,
- #WRITER,
+ #writer_var,
#OPT,
()
)?;
@@ -96,10 +108,11 @@ impl<'a> EnumGenerator<'a> {
fn prefix_prelude(mut self) -> Self {
let out = self.out;
- self.out = PreludeGenerator::new(out, Some(self.input), self.name)
+ self.out = PreludeGenerator::new(out, Some(self.input), self.name, &self.writer_var)
.prefix_magic(&self.en.magic)
.prefix_endian(&self.en.endian)
.prefix_imports()
+ .prefix_map_stream()
.finish();
self
@@ -110,7 +123,11 @@ impl<'a> EnumGenerator<'a> {
}
}
-fn generate_unit_enum_repr(repr: &TokenStream, variants: &[UnitEnumField]) -> TokenStream {
+fn generate_unit_enum_repr(
+ writer_var: &TokenStream,
+ repr: &TokenStream,
+ variants: &[UnitEnumField],
+) -> TokenStream {
let branches = variants.iter().map(|variant| {
let name = &variant.ident;
quote! {
@@ -123,14 +140,14 @@ fn generate_unit_enum_repr(repr: &TokenStream, variants: &[UnitEnumField]) -> To
&(match self {
#(#branches),*
} as #repr),
- #WRITER,
+ #writer_var,
#OPT,
(),
)?;
}
}
-fn generate_unit_enum_magic(variants: &[UnitEnumField]) -> TokenStream {
+fn generate_unit_enum_magic(writer_var: &TokenStream, variants: &[UnitEnumField]) -> TokenStream {
let branches = variants.iter().map(|variant| {
let name = &variant.ident;
let magic = variant.magic.as_ref().map(|magic| {
@@ -139,7 +156,7 @@ fn generate_unit_enum_magic(variants: &[UnitEnumField]) -> TokenStream {
quote! {
#WRITE_METHOD (
magic,
- #WRITER,
+ #writer_var,
#OPT,
(),
)?;
diff --git a/binrw_derive/src/binrw/codegen/write_options/prelude.rs b/binrw_derive/src/binrw/codegen/write_options/prelude.rs
index dbb1391b..8196e497 100644
--- a/binrw_derive/src/binrw/codegen/write_options/prelude.rs
+++ b/binrw_derive/src/binrw/codegen/write_options/prelude.rs
@@ -1,22 +1,37 @@
-use crate::binrw::{
- codegen::{
- get_destructured_imports, get_endian,
- sanitization::{ARGS, OPT, WRITER, WRITE_METHOD},
+use crate::{
+ binrw::{
+ codegen::{
+ get_destructured_imports, get_endian,
+ sanitization::{ARGS, MAP_WRITER_TYPE_HINT, OPT, WRITER, WRITE_METHOD},
+ },
+ parser::{CondEndian, Input, Magic},
},
- parser::{CondEndian, Input, Magic},
+ util::quote_spanned_any,
};
use proc_macro2::{Ident, TokenStream};
use quote::quote;
+use syn::spanned::Spanned;
pub(crate) struct PreludeGenerator<'a> {
out: TokenStream,
input: Option<&'a Input>,
name: Option<&'a Ident>,
+ writer_var: &'a TokenStream,
}
impl<'a> PreludeGenerator<'a> {
- pub(crate) fn new(out: TokenStream, input: Option<&'a Input>, name: Option<&'a Ident>) -> Self {
- Self { out, input, name }
+ pub(crate) fn new(
+ out: TokenStream,
+ input: Option<&'a Input>,
+ name: Option<&'a Ident>,
+ writer_var: &'a TokenStream,
+ ) -> Self {
+ Self {
+ out,
+ input,
+ name,
+ writer_var,
+ }
}
pub(crate) fn prefix_imports(mut self) -> Self {
@@ -36,12 +51,13 @@ impl<'a> PreludeGenerator<'a> {
pub(crate) fn prefix_magic(mut self, magic: &Magic) -> Self {
if let Some(magic) = magic {
+ let writer_var = &self.writer_var;
let magic = magic.match_value();
let out = self.out;
self.out = quote! {
#WRITE_METHOD (
magic,
- #WRITER,
+ #writer_var,
#OPT,
()
)?;
@@ -64,6 +80,22 @@ impl<'a> PreludeGenerator<'a> {
self
}
+ pub(crate) fn prefix_map_stream(mut self) -> Self {
+ if let Some(input) = self.input {
+ if let Some(map_stream) = input.map_stream() {
+ let outer_writer = input.stream_ident_or(WRITER);
+ let inner_writer = &self.writer_var;
+ let tail = self.out;
+ self.out = quote_spanned_any! { map_stream.span()=>
+ let #inner_writer = &mut #MAP_WRITER_TYPE_HINT::(#map_stream)(#outer_writer);
+ #tail
+ };
+ }
+ }
+
+ self
+ }
+
pub(crate) fn finish(self) -> TokenStream {
self.out
}
diff --git a/binrw_derive/src/binrw/codegen/write_options/struct.rs b/binrw_derive/src/binrw/codegen/write_options/struct.rs
index 1672331a..c5c04a09 100644
--- a/binrw_derive/src/binrw/codegen/write_options/struct.rs
+++ b/binrw_derive/src/binrw/codegen/write_options/struct.rs
@@ -1,6 +1,6 @@
use super::{prelude::PreludeGenerator, struct_field::write_field};
use crate::binrw::{
- codegen::get_assertions,
+ codegen::{get_assertions, sanitization::WRITER},
parser::{Input, Struct},
};
use proc_macro2::TokenStream;
@@ -8,7 +8,7 @@ use quote::quote;
use syn::Ident;
pub(super) fn generate_struct(input: &Input, name: Option<&Ident>, st: &Struct) -> TokenStream {
- StructGenerator::new(Some(input), st, name)
+ StructGenerator::new(Some(input), st, name, &input.stream_ident_or(WRITER))
.write_fields()
.prefix_assertions()
.prefix_prelude()
@@ -20,6 +20,7 @@ pub(crate) struct StructGenerator<'input> {
input: Option<&'input Input>,
st: &'input Struct,
name: Option<&'input Ident>,
+ writer_var: &'input TokenStream,
out: TokenStream,
}
@@ -28,20 +29,23 @@ impl<'input> StructGenerator<'input> {
input: Option<&'input Input>,
st: &'input Struct,
name: Option<&'input Ident>,
+ writer_var: &'input TokenStream,
) -> Self {
Self {
input,
st,
name,
+ writer_var,
out: TokenStream::new(),
}
}
pub(crate) fn prefix_prelude(mut self) -> Self {
- self.out = PreludeGenerator::new(self.out, self.input, self.name)
+ self.out = PreludeGenerator::new(self.out, self.input, self.name, self.writer_var)
.prefix_magic(&self.st.magic)
.prefix_endian(&self.st.endian)
.prefix_imports()
+ .prefix_map_stream()
.finish();
self
@@ -60,7 +64,11 @@ impl<'input> StructGenerator<'input> {
}
pub(crate) fn write_fields(mut self) -> Self {
- let write_fields = self.st.fields.iter().map(write_field);
+ let write_fields = self
+ .st
+ .fields
+ .iter()
+ .map(|field| write_field(self.writer_var, field));
self.out = quote! {
#(#write_fields)*
diff --git a/binrw_derive/src/binrw/codegen/write_options/struct_field.rs b/binrw_derive/src/binrw/codegen/write_options/struct_field.rs
index 116e8046..53bfbc78 100644
--- a/binrw_derive/src/binrw/codegen/write_options/struct_field.rs
+++ b/binrw_derive/src/binrw/codegen/write_options/struct_field.rs
@@ -1,22 +1,27 @@
-use crate::binrw::{
- codegen::{
- get_assertions, get_endian, get_passed_args,
- sanitization::{
- make_ident, BEFORE_POS, BINWRITE_TRAIT, SAVED_POSITION, SEEK_FROM, SEEK_TRAIT, WRITER,
- WRITE_ARGS_TYPE_HINT, WRITE_FN_MAP_OUTPUT_TYPE_HINT, WRITE_FN_TRY_MAP_OUTPUT_TYPE_HINT,
- WRITE_FN_TYPE_HINT, WRITE_FUNCTION, WRITE_MAP_ARGS_TYPE_HINT,
- WRITE_MAP_INPUT_TYPE_HINT, WRITE_METHOD, WRITE_TRY_MAP_ARGS_TYPE_HINT, WRITE_ZEROES,
+use crate::{
+ binrw::{
+ codegen::{
+ get_assertions, get_endian, get_passed_args,
+ sanitization::{
+ make_ident, BEFORE_POS, BINWRITE_TRAIT, MAP_WRITER_TYPE_HINT, SAVED_POSITION,
+ SEEK_FROM, SEEK_TRAIT, WRITE_ARGS_TYPE_HINT, WRITE_FN_MAP_OUTPUT_TYPE_HINT,
+ WRITE_FN_TRY_MAP_OUTPUT_TYPE_HINT, WRITE_FN_TYPE_HINT, WRITE_FUNCTION,
+ WRITE_MAP_ARGS_TYPE_HINT, WRITE_MAP_INPUT_TYPE_HINT, WRITE_METHOD,
+ WRITE_TRY_MAP_ARGS_TYPE_HINT, WRITE_ZEROES,
+ },
},
+ parser::{FieldMode, Map, StructField},
},
- parser::{FieldMode, Map, StructField},
+ util::quote_spanned_any,
};
+use alloc::borrow::Cow;
use core::ops::Not;
use proc_macro2::TokenStream;
-use quote::quote;
-use syn::Ident;
+use quote::{quote, ToTokens};
+use syn::{spanned::Spanned, Ident};
-pub(crate) fn write_field(field: &StructField) -> TokenStream {
- StructFieldGenerator::new(field)
+pub(crate) fn write_field(writer_var: &TokenStream, field: &StructField) -> TokenStream {
+ StructFieldGenerator::new(field, writer_var)
.write_field()
.wrap_padding()
.prefix_args()
@@ -25,18 +30,27 @@ pub(crate) fn write_field(field: &StructField) -> TokenStream {
.prefix_magic()
.wrap_condition()
.prefix_assertions()
+ .prefix_map_stream()
.finish()
}
struct StructFieldGenerator<'input> {
field: &'input StructField,
+ outer_writer_var: &'input TokenStream,
+ writer_var: Cow<'input, TokenStream>,
out: TokenStream,
}
impl<'a> StructFieldGenerator<'a> {
- fn new(field: &'a StructField) -> Self {
+ fn new(field: &'a StructField, outer_writer_var: &'a TokenStream) -> Self {
Self {
field,
+ outer_writer_var,
+ writer_var: if field.map_stream.is_some() {
+ Cow::Owned(make_ident(&field.ident, "reader").into_token_stream())
+ } else {
+ Cow::Borrowed(outer_writer_var)
+ },
out: TokenStream::new(),
}
}
@@ -53,6 +67,20 @@ impl<'a> StructFieldGenerator<'a> {
self
}
+ fn prefix_map_stream(mut self) -> Self {
+ if let Some(map_stream) = &self.field.map_stream {
+ let rest = self.out;
+ let writer_var = &self.writer_var;
+ let outer_writer_var = &self.outer_writer_var;
+ self.out = quote_spanned_any! { map_stream.span()=>
+ let #writer_var = &mut #MAP_WRITER_TYPE_HINT::(#map_stream)(#outer_writer_var);
+ #rest
+ };
+ }
+
+ self
+ }
+
fn prefix_write_fn(mut self) -> Self {
if !self.field.is_written() {
return self;
@@ -75,7 +103,7 @@ impl<'a> StructFieldGenerator<'a> {
quote! { #WRITE_FN_TYPE_HINT(#write_fn) }
};
- let out = &self.out;
+ let out = self.out;
self.out = quote! {
let #WRITE_FUNCTION = #write_fn;
#out
@@ -108,6 +136,7 @@ impl<'a> StructFieldGenerator<'a> {
let name = &self.field.ident;
let args = args_ident(name);
let endian = get_endian(&self.field.endian);
+ let writer_var = &self.writer_var;
let initialize = match &self.field.read_mode {
FieldMode::Calc(expr) => Some({
@@ -128,7 +157,7 @@ impl<'a> StructFieldGenerator<'a> {
});
let store_position = quote! {
- let #SAVED_POSITION = #SEEK_TRAIT::stream_position(#WRITER)?;
+ let #SAVED_POSITION = #SEEK_TRAIT::stream_position(#writer_var)?;
};
let name = self
@@ -154,7 +183,7 @@ impl<'a> StructFieldGenerator<'a> {
#WRITE_FUNCTION (
{ #store_position &(#map_fn (#name) #map_try) },
- #WRITER,
+ #writer_var,
#endian,
#args
)?;
@@ -180,10 +209,10 @@ impl<'a> StructFieldGenerator<'a> {
}
fn wrap_padding(mut self) -> Self {
- let out = &self.out;
+ let out = self.out;
- let pad_before = pad_before(self.field);
- let pad_after = pad_after(self.field);
+ let pad_before = pad_before(&self.writer_var, self.field);
+ let pad_after = pad_after(&self.writer_var, self.field);
self.out = quote! {
#pad_before
#out
@@ -207,7 +236,7 @@ impl<'a> StructFieldGenerator<'a> {
};
let map_fn = map_fn_ident(&self.field.ident);
- let out = &self.out;
+ let out = self.out;
self.out = match self.field.read_mode {
FieldMode::Normal => match &self.field.map {
Map::Map(_) => quote! {
@@ -249,11 +278,12 @@ impl<'a> StructFieldGenerator<'a> {
if let Some(magic) = &self.field.magic {
let magic = magic.match_value();
let endian = get_endian(&self.field.endian);
+ let writer_var = &self.writer_var;
let out = self.out;
self.out = quote! {
#WRITE_METHOD (
magic,
- #WRITER,
+ #writer_var,
#endian,
()
)?;
@@ -286,36 +316,36 @@ fn map_fn_ident(ident: &Ident) -> Ident {
make_ident(ident, "map_fn")
}
-fn pad_after(field: &StructField) -> TokenStream {
+fn pad_after(writer_var: &TokenStream, field: &StructField) -> TokenStream {
let pad_size_to = field.pad_size_to.as_ref().map(|size| {
quote! {{
let pad_to_size = (#size) as u64;
- let after_pos = #SEEK_TRAIT::seek(#WRITER, #SEEK_FROM::Current(0))?;
+ let after_pos = #SEEK_TRAIT::seek(#writer_var, #SEEK_FROM::Current(0))?;
if let Some(size) = after_pos.checked_sub(#BEFORE_POS) {
if let Some(padding) = pad_to_size.checked_sub(size) {
- #WRITE_ZEROES(#WRITER, padding)?;
+ #WRITE_ZEROES(#writer_var, padding)?;
}
}
}}
});
let pad_after = field.pad_after.as_ref().map(|padding| {
quote! {
- #WRITE_ZEROES(#WRITER, (#padding) as u64)?;
+ #WRITE_ZEROES(#writer_var, (#padding) as u64)?;
}
});
let align_after = field.align_after.as_ref().map(|alignment| {
quote! {{
- let pos = #SEEK_TRAIT::seek(#WRITER, #SEEK_FROM::Current(0))?;
+ let pos = #SEEK_TRAIT::seek(#writer_var, #SEEK_FROM::Current(0))?;
let align = ((#alignment) as u64);
let rem = pos % align;
if rem != 0 {
- #WRITE_ZEROES(#WRITER, align - rem)?;
+ #WRITE_ZEROES(#writer_var, align - rem)?;
}
}}
});
let restore_position = field.restore_position.map(|_| {
quote! {
- #SEEK_TRAIT::seek(#WRITER, #SEEK_FROM::Start(#SAVED_POSITION))?;
+ #SEEK_TRAIT::seek(#writer_var, #SEEK_FROM::Start(#SAVED_POSITION))?;
}
});
@@ -327,38 +357,38 @@ fn pad_after(field: &StructField) -> TokenStream {
}
}
-fn pad_before(field: &StructField) -> TokenStream {
+fn pad_before(writer_var: &TokenStream, field: &StructField) -> TokenStream {
let seek_before = field.seek_before.as_ref().map(|seek| {
quote! {
#SEEK_TRAIT::seek(
- #WRITER,
+ #writer_var,
#seek,
)?;
}
});
let pad_before = field.pad_before.as_ref().map(|padding| {
quote! {
- #WRITE_ZEROES(#WRITER, (#padding) as u64)?;
+ #WRITE_ZEROES(#writer_var, (#padding) as u64)?;
}
});
let align_before = field.align_before.as_ref().map(|alignment| {
quote! {{
- let pos = #SEEK_TRAIT::seek(#WRITER, #SEEK_FROM::Current(0))?;
+ let pos = #SEEK_TRAIT::seek(#writer_var, #SEEK_FROM::Current(0))?;
let align = ((#alignment) as u64);
let rem = pos % align;
if rem != 0 {
- #WRITE_ZEROES(#WRITER, align - rem)?;
+ #WRITE_ZEROES(#writer_var, align - rem)?;
}
}}
});
let pad_size_to_before = field.pad_size_to.as_ref().map(|_| {
quote! {
- let #BEFORE_POS = #SEEK_TRAIT::seek(#WRITER, #SEEK_FROM::Current(0))?;
+ let #BEFORE_POS = #SEEK_TRAIT::seek(#writer_var, #SEEK_FROM::Current(0))?;
}
});
let store_position = field.restore_position.map(|_| {
quote! {
- let #SAVED_POSITION = #SEEK_TRAIT::seek(#WRITER, #SEEK_FROM::Current(0))?;
+ let #SAVED_POSITION = #SEEK_TRAIT::seek(#writer_var, #SEEK_FROM::Current(0))?;
}
});
diff --git a/binrw_derive/src/binrw/parser/attrs.rs b/binrw_derive/src/binrw/parser/attrs.rs
index 5c9ac040..85bcd2a5 100644
--- a/binrw_derive/src/binrw/parser/attrs.rs
+++ b/binrw_derive/src/binrw/parser/attrs.rs
@@ -1,7 +1,7 @@
use super::keywords as kw;
use crate::meta_types::{
- IdentPatType, IdentTypeMaybeDefault, MetaEnclosedList, MetaExpr, MetaList, MetaLit, MetaType,
- MetaValue, MetaVoid,
+ IdentPatType, IdentTypeMaybeDefault, MetaEnclosedList, MetaExpr, MetaIdent, MetaList, MetaLit,
+ MetaType, MetaValue, MetaVoid,
};
use syn::{Expr, FieldValue, Token};
@@ -27,6 +27,7 @@ pub(super) type IsLittle = MetaExpr;
pub(super) type Little = MetaVoid;
pub(super) type Magic = MetaLit;
pub(super) type Map = MetaExpr;
+pub(super) type MapStream = MetaExpr;
pub(super) type Offset = MetaExpr;
pub(super) type OffsetAfter = MetaExpr;
pub(super) type PadAfter = MetaExpr;
@@ -40,6 +41,7 @@ pub(super) type RestorePosition = MetaVoid;
pub(super) type ReturnAllErrors = MetaVoid;
pub(super) type ReturnUnexpectedError = MetaVoid;
pub(super) type SeekBefore = MetaExpr;
+pub(super) type Stream = MetaIdent;
pub(super) type Temp = MetaVoid;
pub(super) type Try = MetaVoid;
pub(super) type TryMap = MetaExpr;
diff --git a/binrw_derive/src/binrw/parser/field_level_attrs.rs b/binrw_derive/src/binrw/parser/field_level_attrs.rs
index c6041aa4..de919181 100644
--- a/binrw_derive/src/binrw/parser/field_level_attrs.rs
+++ b/binrw_derive/src/binrw/parser/field_level_attrs.rs
@@ -20,6 +20,8 @@ attr_struct! {
pub(crate) endian: CondEndian,
#[from(RW:Map, RW:TryMap, RW:Repr)]
pub(crate) map: Map,
+ #[from(RW:MapStream)]
+ pub(crate) map_stream: Option,
#[from(RW:Magic)]
pub(crate) magic: Magic,
#[from(RW:Args, RW:ArgsRaw)]
@@ -240,6 +242,7 @@ impl FromField for StructField {
field: field.clone(),
endian: <_>::default(),
map: <_>::default(),
+ map_stream: <_>::default(),
magic: <_>::default(),
args: <_>::default(),
read_mode: <_>::default(),
diff --git a/binrw_derive/src/binrw/parser/keywords.rs b/binrw_derive/src/binrw/parser/keywords.rs
index f8806db9..007db88a 100644
--- a/binrw_derive/src/binrw/parser/keywords.rs
+++ b/binrw_derive/src/binrw/parser/keywords.rs
@@ -32,6 +32,7 @@ define_keywords! {
little,
magic,
map,
+ map_stream,
offset,
offset_after,
pad_after,
@@ -45,6 +46,7 @@ define_keywords! {
return_all_errors,
return_unexpected_error,
seek_before,
+ stream,
temp,
try_map,
write_with,
diff --git a/binrw_derive/src/binrw/parser/top_level_attrs.rs b/binrw_derive/src/binrw/parser/top_level_attrs.rs
index 5ca86fe2..ab743391 100644
--- a/binrw_derive/src/binrw/parser/top_level_attrs.rs
+++ b/binrw_derive/src/binrw/parser/top_level_attrs.rs
@@ -5,7 +5,8 @@ use super::{
};
use crate::binrw::Options;
use proc_macro2::TokenStream;
-use syn::spanned::Spanned;
+use quote::ToTokens;
+use syn::{spanned::Spanned, Ident};
/// The parsed representation of binrw attributes on a data structure.
pub(crate) enum Input {
@@ -158,6 +159,14 @@ impl Input {
}
}
+ pub(crate) fn map_stream(&self) -> Option<&TokenStream> {
+ match self {
+ Input::Struct(s) | Input::UnitStruct(s) => s.map_stream.as_ref(),
+ Input::Enum(en) => en.map_stream.as_ref(),
+ Input::UnitOnlyEnum(en) => en.map_stream.as_ref(),
+ }
+ }
+
pub(crate) fn pre_assertions(&self) -> &[Assert] {
match self {
Input::Struct(s) | Input::UnitStruct(s) => &s.pre_assertions,
@@ -166,6 +175,19 @@ impl Input {
}
}
+ pub(crate) fn stream_ident(&self) -> Option<&Ident> {
+ match self {
+ Input::Struct(s) | Input::UnitStruct(s) => s.stream_ident.as_ref(),
+ Input::Enum(en) => en.stream_ident.as_ref(),
+ Input::UnitOnlyEnum(en) => en.stream_ident.as_ref(),
+ }
+ }
+
+ pub(crate) fn stream_ident_or(&self, or: impl ToTokens) -> TokenStream {
+ self.stream_ident()
+ .map_or_else(|| or.to_token_stream(), ToTokens::to_token_stream)
+ }
+
pub(crate) fn assertions(&self) -> &[Assert] {
match self {
Input::Struct(s) | Input::UnitStruct(s) => &s.assertions,
@@ -179,10 +201,14 @@ attr_struct! {
#[from(StructAttr)]
#[derive(Clone, Debug, Default)]
pub(crate) struct Struct {
+ #[from(RW:Stream)]
+ pub(crate) stream_ident: Option,
#[from(RW:Big, RW:Little, RW:IsBig, RW:IsLittle)]
pub(crate) endian: CondEndian,
#[from(RW:Map, RW:TryMap, RW:Repr)]
pub(crate) map: Map,
+ #[from(RW:MapStream)]
+ pub(crate) map_stream: Option,
#[from(RW:Magic)]
pub(crate) magic: Magic,
#[from(RW:Import, RW:ImportRaw)]
@@ -282,10 +308,14 @@ attr_struct! {
#[derive(Clone, Debug, Default)]
pub(crate) struct Enum {
pub(crate) ident: Option,
+ #[from(RW:Stream)]
+ pub(crate) stream_ident: Option,
#[from(RW:Big, RW:Little, RW:IsBig, RW:IsLittle)]
pub(crate) endian: CondEndian,
#[from(RW:Map, RW:TryMap, RW:Repr)]
pub(crate) map: Map,
+ #[from(RW:MapStream)]
+ pub(crate) map_stream: Option,
#[from(RW:Magic)]
pub(crate) magic: Magic,
#[from(RW:Import, RW:ImportRaw)]
@@ -330,10 +360,14 @@ attr_struct! {
#[from(UnitEnumAttr)]
#[derive(Clone, Debug, Default)]
pub(crate) struct UnitOnlyEnum {
+ #[from(RW:Stream)]
+ pub(crate) stream_ident: Option,
#[from(RW:Big, RW:Little, RW:IsBig, RW:IsLittle)]
pub(crate) endian: CondEndian,
#[from(RW:Map, RW:TryMap, RW:Repr)]
pub(crate) map: Map,
+ #[from(RW:MapStream)]
+ pub(crate) map_stream: Option,
#[from(RW:Magic)]
pub(crate) magic: Magic,
#[from(RW:Import, RW:ImportRaw)]
diff --git a/binrw_derive/src/meta_types.rs b/binrw_derive/src/meta_types.rs
index d0c3af2f..bc11ea4d 100644
--- a/binrw_derive/src/meta_types.rs
+++ b/binrw_derive/src/meta_types.rs
@@ -6,7 +6,7 @@ use syn::{
punctuated::Punctuated,
spanned::Spanned,
token::{self, Token},
- Expr, Lit, Token, Type,
+ Expr, Ident, Lit, Token, Type,
};
pub(crate) trait KeywordToken {
@@ -39,6 +39,13 @@ pub(crate) type MetaExpr = MetaValue;
/// both are always allowed
pub(crate) type MetaType = MetaValue;
+/// `MetaIdent` represents a key/ident pair
+/// Takes two forms:
+/// * ident(ident)
+/// * ident = ident
+/// both are always allowed
+pub(crate) type MetaIdent = MetaValue;
+
/// `MetaLit` represents a key/lit pair
/// Takes two forms:
/// * ident(lit)
@@ -82,6 +89,12 @@ impl From> for TokenStream {
}
}
+impl From> for Ident {
+ fn from(value: MetaValue) -> Self {
+ value.value
+ }
+}
+
impl ToTokens for MetaValue {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.value.to_tokens(tokens);