binrw icon indicating copy to clipboard operation
binrw copied to clipboard

Add serde-style `brw(with)` attribute

Open jam1garner opened this issue 2 years ago • 0 comments

General idea is providing a module containing both a custom parser and a custom writer function. This would also make it easier to make binrw compatibility crates by allowing you to two generic functions that are custom readers/writers for all types the crate supports to get around orphan rules:

In the compatibility crate (let's call it binrw_3d, similar to bevy-style 3rd party compatibility crates, basically a binrw crate with common 3d type parsers):

use binrw::{prelude::*, io::*};

trait Supported {
    type ReprType: Into<Self> + From<Self> + BinRead + BinWrite;
}

fn read_options<R: Read + Seek, T: Supported>(reader: &mut R, opts: &ReadOptions, args: <T::ReprType as BinRead>::Args) -> BinResult<T> {
    <T::ReprType as BinRead>::read_options(reader, opts, args)
}

fn write_options<W: Write + Seek, T: Supported>(
    val: &T,
    writer: &mut W,
    options: &WriteOptions,
    args: <T::ReprType as BinWrite>::Args,
) -> BinResult<()> {
    <T::ReprType as BinWrite>::write_options(&T::ReprType::from(val), writer, opts)
}

Then for each type that should be supported, make a new struct implement BinRead/BinWrite as normal, and implement conversion to and from the type itself. Then all that's needed is:

impl Supported for glam::Vec3 {
    type Repr = MyVec3f; // MyVec3f implements BinRead/BinWrite/From<glam::Vec3>/Into<glam::Vec3>
}

And while that's a bit of boilerplate just to make a binrw implementation for a foreign type, all that your crate user needs to do is:

use glam::Vec3;

#[binrw]
struct Vertex {
    #[brw(with = binrw_3d)]
    position: Vec3,

    #[brw(with = binrw_3d)]
    position: Quaternion,
}

and the foreign types "just work"

Open Design Questions

Should binrw provide a macro or two to make all the boilerplate above just a single line? Is encouraging 3rd-party utility types worthwhile enough for that? Maybe something like:

use binrw::*;

binrw::conversion_crate!(Supported);

#[binrw(Supported for glam::Vec3)]
struct Vec3 {
    x: f32,
    y: f32,
    z: f32,
}

// impl conversion traits here

jam1garner avatar Feb 19 '22 18:02 jam1garner