Skip to content

Commit c3a2587

Browse files
authored
Add BinRead and BinWrite trait generation (#65)
1 parent 740c254 commit c3a2587

3 files changed

Lines changed: 92 additions & 0 deletions

File tree

src/attr.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ pub struct Params {
1515
pub into: Option<syn::Path>,
1616
pub from: Option<syn::Path>,
1717
pub bits: usize,
18+
pub binread: Enable,
19+
pub binwrite: Enable,
1820
pub new: Enable,
1921
pub clone: Enable,
2022
pub debug: Enable,
@@ -41,6 +43,8 @@ impl Parse for Params {
4143
into: None,
4244
from: None,
4345
bits,
46+
binread: Enable::No,
47+
binwrite: Enable::No,
4448
new: Enable::Yes,
4549
clone: Enable::Yes,
4650
debug: Enable::Yes,
@@ -93,6 +97,17 @@ impl Parse for Params {
9397
"conversion" => {
9498
ret.conversion = syn::LitBool::parse(input)?.value;
9599
}
100+
"binrw" => {
101+
let enable: Enable = input.parse()?;
102+
ret.binread = enable.clone();
103+
ret.binwrite = enable;
104+
}
105+
"binread" => {
106+
ret.binread = input.parse()?;
107+
}
108+
"binwrite" => {
109+
ret.binwrite = input.parse()?;
110+
}
96111
_ => return Err(s_err(ident.span(), "unknown argument")),
97112
};
98113
}

src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ fn s_err(span: proc_macro2::Span, msg: impl fmt::Display) -> syn::Error {
3030
/// - `from` to specify a conversion function from repr to the bitfield's integer type
3131
/// - `into` to specify a conversion function from the bitfield's integer type to repr
3232
/// - `new` to disable the `new` function generation
33+
/// - `binread` to enable the `BinRead` trait generation
34+
/// - `binwrite` to enable the `BinWrite` trait generation
35+
/// - `binrw` to enable both `BinRead` and `BinWrite` trait generation
3336
/// - `clone` to disable the `Clone` trait generation
3437
/// - `debug` to disable the `Debug` trait generation
3538
/// - `defmt` to enable the `defmt::Format` trait generation
@@ -64,6 +67,8 @@ fn bitfield_inner(args: TokenStream, input: TokenStream) -> syn::Result<TokenStr
6467
into,
6568
from,
6669
bits,
70+
binread,
71+
binwrite,
6772
new,
6873
clone,
6974
debug,
@@ -133,6 +138,12 @@ fn bitfield_inner(args: TokenStream, input: TokenStream) -> syn::Result<TokenStr
133138
if let Some(cfg) = hash.cfg() {
134139
impl_debug.extend(traits::hash(&name, &members, cfg));
135140
}
141+
if let Some(cfg) = binread.cfg() {
142+
impl_debug.extend(traits::binread(&name, &repr, cfg));
143+
}
144+
if let Some(cfg) = binwrite.cfg() {
145+
impl_debug.extend(traits::binwrite(&name, cfg));
146+
}
136147

137148
let defaults = members.iter().map(Member::default).collect::<Vec<_>>();
138149

src/traits.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,69 @@ pub fn hash(
131131
}
132132
}
133133
}
134+
135+
/// Implements the `binrw::BinWrite` trait for the given bitfield struct.
136+
pub fn binwrite(
137+
name: &syn::Ident,
138+
cfg: Option<TokenStream>,
139+
) -> TokenStream {
140+
let attr = cfg.map(|cfg| quote!(#[cfg(#cfg)]));
141+
142+
quote! {
143+
#attr
144+
impl binrw::BinWrite for #name {
145+
type Args<'a> = ();
146+
147+
fn write_options<W: binrw::io::Write + binrw::io::Seek>(
148+
&self,
149+
writer: &mut W,
150+
endian: binrw::Endian,
151+
args: Self::Args<'_>,
152+
) -> binrw::BinResult<()> {
153+
let raw = self.into_bits();
154+
155+
let bytes = match endian {
156+
binrw::Endian::Big => raw.to_be_bytes(),
157+
binrw::Endian::Little => raw.to_le_bytes(),
158+
};
159+
160+
writer.write_all(&bytes)?;
161+
162+
Ok(())
163+
}
164+
}
165+
}
166+
}
167+
168+
/// Implements the `binrw::BinRead` trait for the given bitfield struct.
169+
pub fn binread(
170+
name: &syn::Ident,
171+
repr: &syn::Type,
172+
cfg: Option<TokenStream>,
173+
) -> TokenStream {
174+
let attr = cfg.map(|cfg| quote!(#[cfg(#cfg)]));
175+
176+
quote! {
177+
#attr
178+
impl binrw::BinRead for #name {
179+
type Args<'a> = ();
180+
181+
fn read_options<R: binrw::io::Read + binrw::io::Seek>(
182+
reader: &mut R,
183+
endian: binrw::Endian,
184+
args: Self::Args<'_>,
185+
) -> binrw::BinResult<Self> {
186+
let mut buf = [0u8; core::mem::size_of::<#repr>()];
187+
188+
reader.read_exact(&mut buf)?;
189+
190+
let raw = match endian {
191+
binrw::Endian::Big => <#repr>::from_be_bytes(buf),
192+
binrw::Endian::Little => <#repr>::from_le_bytes(buf),
193+
};
194+
195+
Ok(Self::from_bits(raw))
196+
}
197+
}
198+
}
199+
}

0 commit comments

Comments
 (0)