Skip to content

Commit

Permalink
Implement with directive
Browse files Browse the repository at this point in the history
  • Loading branch information
csnover committed Nov 15, 2022
1 parent 418acd6 commit 392bb53
Show file tree
Hide file tree
Showing 20 changed files with 925 additions and 346 deletions.
22 changes: 11 additions & 11 deletions binrw/doc/attribute.md
Original file line number Diff line number Diff line change
Expand Up @@ -1162,11 +1162,11 @@ assert_eq!(output.into_inner(), b"\0\0\0\x01\0\x02\0\x03");
### Using `FilePtr::parse` to read a `NullString` without storing a `FilePtr`

```
# use binrw::{prelude::*, io::Cursor, FilePtr32, NullString};
# use binrw::{prelude::*, io::Cursor, FilePtr32, NullString, ReadFrom};
#[derive(BinRead)]
struct MyType {
#[br(parse_with = FilePtr32::parse)]
some_string: NullString,
#[br(parse_with = FilePtr32::parse_with(<_ as ReadFrom<NullString>>::read_from))]
some_string: String,
}
# let val: MyType = Cursor::new(b"\0\0\0\x04Test\0").read_be().unwrap();
Expand Down Expand Up @@ -1885,8 +1885,8 @@ referenced by the expressions in any of these directives.
# use binrw::{prelude::*, NullString, io::SeekFrom};
#[derive(BinRead)]
struct MyType {
#[br(align_before = 4, pad_after = 1, align_after = 4)]
str: NullString,
#[br(align_before = 4, pad_after = 1, align_after = 4, with(NullString))]
str: String,
#[br(pad_size_to = 0x10)]
test: u64,
Expand All @@ -1902,8 +1902,8 @@ struct MyType {
# use binrw::{prelude::*, NullString, io::SeekFrom};
#[derive(BinWrite)]
struct MyType {
#[bw(align_before = 4, pad_after = 1, align_after = 4)]
str: NullString,
#[bw(align_before = 4, pad_after = 1, align_after = 4, with(NullString))]
str: String,
#[bw(pad_size_to = 0x10)]
test: u64,
Expand Down Expand Up @@ -1952,12 +1952,12 @@ this to happen.
## Examples

```
# use binrw::{prelude::*, FilePtr32, NullString, io::Cursor};
# use binrw::{prelude::*, FilePtr32, FilePtrWith, NullString, io::Cursor};
#[derive(BinRead, Debug)]
#[br(big, magic = b"TEST")]
struct TestFile {
#[br(deref_now)]
ptr: FilePtr32<NullString>,
#[br(deref_now, with(FilePtrWith<NullString>))]
ptr: FilePtr32<String>,
value: i32,
Expand All @@ -1970,7 +1970,7 @@ struct TestFile {
# let test = Cursor::new(test_contents).read_be::<TestFile>().unwrap();
# assert_eq!(test.ptr_len, 11);
# assert_eq!(test.value, -1);
# assert_eq!(test.ptr.to_string(), "Test string");
# assert_eq!(*test.ptr, "Test string");
```
</div>

Expand Down
4 changes: 2 additions & 2 deletions binrw/doc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ struct Dog {
#[br(count = bone_pile_count)]
bone_piles: Vec<u16>,
#[br(align_before = 0xA)]
name: NullString
#[br(align_before = 0xA, with(NullString))]
name: String
}
let mut data = Cursor::new(b"DOG\x02\x00\x01\x00\x12\0\0Rudy\0");
Expand Down
277 changes: 277 additions & 0 deletions binrw/src/binread/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,167 @@ pub trait BinRead: Sized + 'static {
}
}

/// Extension methods for reading [`BinRead`] objects using a converter.
pub trait ReadWith {
/// Read `Self` from the reader using the given converter.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[inline]
fn read_with<T, R>(reader: &mut R) -> BinResult<Self>
where
Self: ReadFrom<T> + Sized,
R: Read + Seek,
T: ReadEndian,
<Self as ReadFrom<T>>::Args: Required,
{
Self::read_args_with::<T, _>(reader, <Self as ReadFrom<T>>::Args::args())
}

/// Read `Self` from the reader, using the given converter, assuming
/// big-endian byte order.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[inline]
fn read_be_with<T, R>(reader: &mut R) -> BinResult<Self>
where
Self: ReadFrom<T> + Sized,
R: Read + Seek,
<Self as ReadFrom<T>>::Args: Required,
{
Self::read_be_args_with::<T, _>(reader, <Self as ReadFrom<T>>::Args::args())
}

/// Read `Self` from the reader, using the given converter, assuming
/// little-endian byte order.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[inline]
fn read_le_with<T, R>(reader: &mut R) -> BinResult<Self>
where
Self: ReadFrom<T> + Sized,
R: Read + Seek,
<Self as ReadFrom<T>>::Args: Required,
{
Self::read_le_args_with::<T, _>(reader, <Self as ReadFrom<T>>::Args::args())
}

/// Read `Self` from the reader, using the given converter, assuming
/// native-endian byte order.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[inline]
fn read_ne_with<T, R>(reader: &mut R) -> BinResult<Self>
where
Self: ReadFrom<T> + Sized,
R: Read + Seek,
<Self as ReadFrom<T>>::Args: Required,
{
Self::read_ne_args_with::<T, _>(reader, <Self as ReadFrom<T>>::Args::args())
}

/// Read `Self` from the reader, using the given converter and arguments.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[inline]
fn read_args_with<T, R>(reader: &mut R, args: <Self as ReadFrom<T>>::Args) -> BinResult<Self>
where
Self: ReadFrom<T> + Sized,
R: Read + Seek,
T: ReadEndian,
{
Self::read_from(reader, Endian::Little, args)
}

/// Read `Self` from the reader, using the given converter and arguments,
/// assuming big-endian byte order.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[inline]
fn read_be_args_with<T, R>(reader: &mut R, args: <Self as ReadFrom<T>>::Args) -> BinResult<Self>
where
Self: ReadFrom<T> + Sized,
R: Read + Seek,
{
Self::read_from(reader, Endian::Big, args)
}

/// Read `Self` from the reader, using the given converter and arguments,
/// assuming little-endian byte order.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[inline]
fn read_le_args_with<T, R>(reader: &mut R, args: <Self as ReadFrom<T>>::Args) -> BinResult<Self>
where
Self: ReadFrom<T> + Sized,
R: Read + Seek,
{
Self::read_from(reader, Endian::Little, args)
}

/// Read `Self` from the reader, using the given converter and arguments,
/// assuming native-endian byte order.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[inline]
fn read_ne_args_with<T, R>(reader: &mut R, args: <Self as ReadFrom<T>>::Args) -> BinResult<Self>
where
Self: ReadFrom<T> + Sized,
R: Read + Seek,
{
Self::read_from(reader, Endian::NATIVE, args)
}
}

impl<T> ReadWith for T {}

/// The `ReadFrom` trait enables transparent deserialisation into a
/// non-[`BinRead`] type.
pub trait ReadFrom<T>: Sized {
/// The type used for the `args` parameter of [`read_from()`].
///
/// [`read_from()`]: Self::read_from
type Args: Clone;

/// Read `T` from the reader using the given arguments.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
fn read_from<R: Read + Seek>(
reader: &mut R,
endian: Endian,
args: Self::Args,
) -> BinResult<Self>;
}

impl<T: BinRead> ReadFrom<T> for T {
type Args = T::Args;

fn read_from<R: Read + Seek>(
reader: &mut R,
endian: Endian,
args: Self::Args,
) -> BinResult<Self> {
Self::read_options(reader, endian, args)
}
}

/// Extension methods for reading [`BinRead`] objects directly from a reader.
///
/// # Examples
Expand Down Expand Up @@ -289,6 +450,122 @@ pub trait BinReaderExt: Read + Seek + Sized {
fn read_ne_args<T: BinRead>(&mut self, args: T::Args) -> BinResult<T> {
self.read_type_args(Endian::NATIVE, args)
}

/// Read `T` from the reader using the given converter.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[inline]
fn read_with<C, T>(&mut self) -> BinResult<T>
where
T: ReadFrom<C>,
C: ReadEndian,
<T as ReadFrom<C>>::Args: Required,
{
self.read_args_with::<C, T>(<T as ReadFrom<C>>::Args::args())
}

/// Read `T` from the reader, using the given converter, assuming
/// big-endian byte order.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[inline]
fn read_be_with<C, T>(&mut self) -> BinResult<T>
where
T: ReadFrom<C>,
<T as ReadFrom<C>>::Args: Required,
{
self.read_be_args_with::<C, T>(<T as ReadFrom<C>>::Args::args())
}

/// Read `T` from the reader, using the given converter, assuming
/// little-endian byte order.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[inline]
fn read_le_with<C, T>(&mut self) -> BinResult<T>
where
T: ReadFrom<C>,
<T as ReadFrom<C>>::Args: Required,
{
self.read_le_args_with::<C, T>(<T as ReadFrom<C>>::Args::args())
}

/// Read `T` from the reader, using the given converter, assuming
/// native-endian byte order.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[inline]
fn read_ne_with<C, T>(&mut self) -> BinResult<T>
where
T: ReadFrom<C>,
<T as ReadFrom<C>>::Args: Required,
{
self.read_ne_args_with::<C, T>(<T as ReadFrom<C>>::Args::args())
}

/// Read `T` from the reader, using the given converter and arguments.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[inline]
fn read_args_with<C, T>(&mut self, args: <T as ReadFrom<C>>::Args) -> BinResult<T>
where
T: ReadFrom<C>,
C: ReadEndian,
{
T::read_from(self, Endian::Little, args)
}

/// Read `T` from the reader, using the given converter and arguments,
/// assuming big-endian byte order.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[inline]
fn read_be_args_with<C, T>(&mut self, args: <T as ReadFrom<C>>::Args) -> BinResult<T>
where
T: ReadFrom<C>,
{
T::read_from(self, Endian::Big, args)
}

/// Read `T` from the reader, using the given converter and arguments,
/// assuming little-endian byte order.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[inline]
fn read_le_args_with<C, T>(&mut self, args: <T as ReadFrom<C>>::Args) -> BinResult<T>
where
T: ReadFrom<C>,
{
T::read_from(self, Endian::Little, args)
}

/// Read `T` from the reader, using the given converter and arguments,
/// assuming native-endian byte order.
///
/// # Errors
///
/// If reading fails, an [`Error`](crate::Error) variant will be returned.
#[inline]
fn read_ne_args_with<C, T>(&mut self, args: <T as ReadFrom<C>>::Args) -> BinResult<T>
where
T: ReadFrom<C>,
{
T::read_from(self, Endian::NATIVE, args)
}
}

impl<R: Read + Seek + Sized> BinReaderExt for R {}
Loading

0 comments on commit 392bb53

Please sign in to comment.