From 9f95b3ecb5fcb5d3521a4f870bfa202ea76f40f9 Mon Sep 17 00:00:00 2001 From: bbaldino Date: Tue, 29 Apr 2025 14:43:45 -0700 Subject: [PATCH 1/5] refactor: change ParselyWrite trait i found i had scenarios where i wanted to be able to use the ParselyRead/ParselyWrite traits but not necessarily with `BitBuf`/`BitBufMut`, so I wanted to be able to use them with different buffer types. In order to do that, I needed to move the buffer back to a generic on the ParselyWrite trait. Previously this was an issue with how I was coercing the result of expressions in attributes: I needed know generics there for it to work reasonably as a trait bound. But I found putting the buffer there should work alright: it makes sense to require we can get a type that can be written to the current buffer. --- Notes.md | 20 +++++++++++++++++ impl/src/code_gen/gen_write.rs | 12 +++++----- impl/src/error.rs | 18 ++++++++------- impl/src/model_types.rs | 10 ++++++--- impl/src/parsely_write.rs | 36 ++++++++++++++++++------------ tests/expand/alignment.expanded.rs | 10 +++------ tests/expand/assertion.expanded.rs | 10 +++------ tests/expand/map.expanded.rs | 17 +++++++------- tests/ui/pass/alignment.rs | 2 +- tests/ui/pass/basic_write.rs | 2 +- tests/ui/pass/map.rs | 2 +- 11 files changed, 82 insertions(+), 57 deletions(-) diff --git a/Notes.md b/Notes.md index 3eca824..dd53fd2 100644 --- a/Notes.md +++ b/Notes.md @@ -486,3 +486,23 @@ after doing the code I realized I had no good way to test it because the built-in buf doesn't provide anything that could be used with it, which makes me think putting it in parsely might not make sense. This use case _could_ have used the custom reader attribute, maybe? + +### Using ParselyRead/ParselyWrite with types other than BitBuf/BitBufMut + +For things like RTP parsing, we don't want to use the BitBuf/BitBufMut +abstractions because they limit the efficiency we can get when we use +Bits/BitsMut directly. It would be nice to be able to leverage +ParselyRead/ParselyWrite for other types as well: i.e. manually implementing it +for the RTP packet types. The problem is that the trait bound +(BitBuf/BitBufMut) is built into the trait, so we can't really do that. I +looked at changing the "buffer" type to be an associated type, but that has a +couple problems: + +* We can't do `type Buf = impl BitBuf` or `type Buf = dyn BitBUf` so we lose +the ability to say "some BitBuf type". We _could_ add another layer of +indirection here, but that's a bit of a bummer. +* Supporting multiple buffer types also makes me think that it'd be nice to be +able to provide different/optimized read/write impls for different types (e.g. +one for BitBuf and another when we know it's a Bits specifically or something) + +--> look into a refactoring of the read/write traits to accomplish this diff --git a/impl/src/code_gen/gen_write.rs b/impl/src/code_gen/gen_write.rs index 296c402..e6cee7c 100644 --- a/impl/src/code_gen/gen_write.rs +++ b/impl/src/code_gen/gen_write.rs @@ -60,18 +60,18 @@ fn generate_parsely_write_impl_struct( } else if f.ty.is_option() { field_write_output.extend(quote! { if let Some(ref v) = self.#field_name { - #write_type::write::<_, T>(v, buf, (#(#context_values,)*)).with_context(|| format!("Writing field '{}'", #field_name_string))?; + #write_type::write::(v, buf, (#(#context_values,)*)).with_context(|| format!("Writing field '{}'", #field_name_string))?; } }); } else if f.ty.is_collection() { field_write_output.extend(quote! { self.#field_name.iter().enumerate().map(|(idx, v)| { - #write_type::write::<_, T>(v, buf, (#(#context_values,)*)).with_context(|| format!("Index {idx}")) + #write_type::write::(v, buf, (#(#context_values,)*)).with_context(|| format!("Index {idx}")) }).collect::>>().with_context(|| format!("Writing field '{}'", #field_name_string))?; }); } else { field_write_output.extend(quote! { - #write_type::write::<_, T>(&self.#field_name, buf, (#(#context_values,)*)).with_context(|| format!("Writing field '{}'", #field_name_string))?; + #write_type::write::(&self.#field_name, buf, (#(#context_values,)*)).with_context(|| format!("Writing field '{}'", #field_name_string))?; }); } @@ -104,7 +104,7 @@ fn generate_parsely_write_impl_struct( let field_name_string = field_name.to_string(); if let Some(ref sync_expr) = f.sync_expr { quote! { - self.#field_name = (#sync_expr).into_parsely_result().with_context(|| format!("Syncing field '{}'", #field_name_string))?; + self.#field_name = (#sync_expr).into_parsely_result_read().with_context(|| format!("Syncing field '{}'", #field_name_string))?; } } else if f.sync_with.is_empty() && f.ty.is_wrapped() { // We'll allow this combination to skip a call to sync: for types like Option or @@ -142,9 +142,9 @@ fn generate_parsely_write_impl_struct( }; quote! { - impl ::#crate_name::ParselyWrite for #struct_name { + impl ::#crate_name::ParselyWrite for #struct_name { type Ctx = (#(#context_types,)*); - fn write( + fn write( &self, buf: &mut B, ctx: Self::Ctx, diff --git a/impl/src/error.rs b/impl/src/error.rs index 97ac835..7072c9f 100644 --- a/impl/src/error.rs +++ b/impl/src/error.rs @@ -5,25 +5,24 @@ pub type ParselyResult = anyhow::Result; /// Helper trait to coerce values of both `T: ParselyWrite` and `Result: E: /// Into` into `ParselyResult`. We need a trait specifically for writing because /// if we don't bound the impl for `T` in some way there's ambiguity: the compiler doesn't know if -/// we want `ParselyResult` or `ParselyResult>`. -pub trait IntoWritableParselyResult { - fn into_parsely_result(self) -> ParselyResult; +pub trait IntoWritableParselyResult { + fn into_writable_parsely_result(self) -> ParselyResult; } -impl IntoWritableParselyResult for T +impl IntoWritableParselyResult for T where - T: ParselyWrite, + T: ParselyWrite, { - fn into_parsely_result(self) -> ParselyResult { + fn into_writable_parsely_result(self) -> ParselyResult { Ok(self) } } -impl IntoWritableParselyResult for Result +impl IntoWritableParselyResult for Result where E: Into, { - fn into_parsely_result(self) -> ParselyResult { + fn into_writable_parsely_result(self) -> ParselyResult { self.map_err(Into::into) } } @@ -33,6 +32,9 @@ where /// concrete type and we can rely on type inference in order to figure out what that should be. /// Because of that we don't want/need the `ParselyWrite` trait bounds on the impl like we have /// above for the writable side, so we need a different trait here. +// TODO: remove the 'read' from these method calls, as they get used in places like context +// expression evaluation where the writable limitations also don't exist, but aren't exactly on the +// 'read path' (for example when syncing state) pub trait IntoParselyResult { fn into_parsely_result_read(self) -> ParselyResult; } diff --git a/impl/src/model_types.rs b/impl/src/model_types.rs index 403e841..ef49cc5 100644 --- a/impl/src/model_types.rs +++ b/impl/src/model_types.rs @@ -81,7 +81,7 @@ impl Context { .enumerate() .map(|(idx, e)| { syn::parse2(quote! { - (#e).into_parsely_result().with_context(|| format!("{}: expression {}", #context, #idx))? + (#e).into_parsely_result_read().with_context(|| format!("{}: expression {}", #context, #idx))? }) .unwrap() }) @@ -275,9 +275,13 @@ impl MapExpr { let map_expr = &self.0; tokens.extend(quote! { { - let mapped_value = (#map_expr)(&self.#field_name).into_parsely_result() + let mapped_value = (#map_expr)(&self.#field_name); + // Coerce the result of the mapping function into a ParselyResult where we know + // T is writable to the buffer. We need to use this syntax because otherwise the + // compiler gets caught up on trying to infer the buffer type. + let result = <_ as IntoWritableParselyResult<_, B>>::into_writable_parsely_result(mapped_value) .with_context(|| format!("Mapping raw value for field '{}'", #field_name_string))?; - ::#crate_name::ParselyWrite::write::(&mapped_value, buf, ()) + ::#crate_name::ParselyWrite::write::(&result, buf, ()) .with_context(|| format!("Writing mapped value for field '{}'", #field_name_string))?; } }) diff --git a/impl/src/parsely_write.rs b/impl/src/parsely_write.rs index b45ec11..1b0fc7b 100644 --- a/impl/src/parsely_write.rs +++ b/impl/src/parsely_write.rs @@ -22,21 +22,33 @@ macro_rules! impl_stateless_sync { }; } -pub trait ParselyWrite: StateSync + Sized { +/// A marker trait so it can be used as a trait bound +pub trait ParselyWritable { + #[doc(hidden)] + fn _dummy_write(buf: &mut B, order: O, ctx: C) + where + Self: ParselyWrite; +} + +impl ParselyWritable for T { + fn _dummy_write(_buf: &mut B, _order: O, _ctx: C) + where + T: ParselyWrite, + { + } +} + +pub trait ParselyWrite: StateSync + Sized { type Ctx; - fn write(&self, buf: &mut B, ctx: Self::Ctx) -> ParselyResult<()>; + fn write(&self, buf: &mut B, ctx: Self::Ctx) -> ParselyResult<()>; } macro_rules! impl_parsely_write_builtin { ($type:ty) => { - impl ParselyWrite for $type { + impl ParselyWrite for $type { type Ctx = (); - fn write( - &self, - buf: &mut B, - _: Self::Ctx, - ) -> ParselyResult<()> { + fn write(&self, buf: &mut B, _: Self::Ctx) -> ParselyResult<()> { ::paste::paste! { Ok(buf.[](*self)?) } @@ -47,13 +59,9 @@ macro_rules! impl_parsely_write_builtin { macro_rules! impl_parsely_write_builtin_bo { ($type:ty) => { - impl ParselyWrite for $type { + impl ParselyWrite for $type { type Ctx = (); - fn write( - &self, - buf: &mut B, - _: Self::Ctx, - ) -> ParselyResult<()> { + fn write(&self, buf: &mut B, _: Self::Ctx) -> ParselyResult<()> { ::paste::paste! { Ok(buf.[]::(*self)?) } diff --git a/tests/expand/alignment.expanded.rs b/tests/expand/alignment.expanded.rs index 3efff43..a6c5aa5 100644 --- a/tests/expand/alignment.expanded.rs +++ b/tests/expand/alignment.expanded.rs @@ -20,15 +20,11 @@ impl ::parsely_rs::ParselyRead for Foo { Ok(Self { one }) } } -impl ::parsely_rs::ParselyWrite for Foo { +impl ::parsely_rs::ParselyWrite for Foo { type Ctx = (); - fn write( - &self, - buf: &mut B, - ctx: Self::Ctx, - ) -> ParselyResult<()> { + fn write(&self, buf: &mut B, ctx: Self::Ctx) -> ParselyResult<()> { let __bytes_remaining_start = buf.remaining_mut_bytes(); - u8::write::<_, T>(&self.one, buf, ()) + u8::write::(&self.one, buf, ()) .with_context(|| ::alloc::__export::must_use({ let res = ::alloc::fmt::format( format_args!("Writing field \'{0}\'", "one"), diff --git a/tests/expand/assertion.expanded.rs b/tests/expand/assertion.expanded.rs index d0dcc10..d396ab8 100644 --- a/tests/expand/assertion.expanded.rs +++ b/tests/expand/assertion.expanded.rs @@ -33,13 +33,9 @@ impl ::parsely_rs::ParselyRead for Foo { Ok(Self { value }) } } -impl ::parsely_rs::ParselyWrite for Foo { +impl ::parsely_rs::ParselyWrite for Foo { type Ctx = (); - fn write( - &self, - buf: &mut B, - ctx: Self::Ctx, - ) -> ParselyResult<()> { + fn write(&self, buf: &mut B, ctx: Self::Ctx) -> ParselyResult<()> { let __value_assertion_func = |v: &u8| *v % 2 == 0; if !__value_assertion_func(&self.value) { return ::anyhow::__private::Err( @@ -56,7 +52,7 @@ impl ::parsely_rs::ParselyWrite for Foo { ), ); } - u8::write::<_, T>(&self.value, buf, ()) + u8::write::(&self.value, buf, ()) .with_context(|| ::alloc::__export::must_use({ let res = ::alloc::fmt::format( format_args!("Writing field \'{0}\'", "value"), diff --git a/tests/expand/map.expanded.rs b/tests/expand/map.expanded.rs index 17eacca..450d7ee 100644 --- a/tests/expand/map.expanded.rs +++ b/tests/expand/map.expanded.rs @@ -31,23 +31,22 @@ impl ::parsely_rs::ParselyRead for Foo { Ok(Self { value }) } } -impl ::parsely_rs::ParselyWrite for Foo { +impl ::parsely_rs::ParselyWrite for Foo { type Ctx = (); - fn write( - &self, - buf: &mut B, - ctx: Self::Ctx, - ) -> ParselyResult<()> { + fn write(&self, buf: &mut B, ctx: Self::Ctx) -> ParselyResult<()> { { - let mapped_value = (|v: &str| { v.parse::() })(&self.value) - .into_parsely_result() + let mapped_value = (|v: &str| { v.parse::() })(&self.value); + let result = <_ as IntoWritableParselyResult< + _, + B, + >>::into_writable_parsely_result(mapped_value) .with_context(|| ::alloc::__export::must_use({ let res = ::alloc::fmt::format( format_args!("Mapping raw value for field \'{0}\'", "value"), ); res }))?; - ::parsely_rs::ParselyWrite::write::(&mapped_value, buf, ()) + ::parsely_rs::ParselyWrite::write::(&result, buf, ()) .with_context(|| ::alloc::__export::must_use({ let res = ::alloc::fmt::format( format_args!("Writing mapped value for field \'{0}\'", "value"), diff --git a/tests/ui/pass/alignment.rs b/tests/ui/pass/alignment.rs index 8888a1c..26dc00c 100644 --- a/tests/ui/pass/alignment.rs +++ b/tests/ui/pass/alignment.rs @@ -15,6 +15,6 @@ fn main() { let mut bits_mut = BitsMut::new(); - Foo::write::<_, NetworkOrder>(&foo, &mut bits_mut, ()).unwrap(); + Foo::write::(&foo, &mut bits_mut, ()).unwrap(); assert_eq!(bits_mut.len_bytes(), 4); } diff --git a/tests/ui/pass/basic_write.rs b/tests/ui/pass/basic_write.rs index 25aeeeb..5c22057 100644 --- a/tests/ui/pass/basic_write.rs +++ b/tests/ui/pass/basic_write.rs @@ -13,5 +13,5 @@ fn main() { two: u3::new(4), }; - Foo::write::<_, NetworkOrder>(&foo, &mut bits_mut, ()).unwrap(); + Foo::write::(&foo, &mut bits_mut, ()).unwrap(); } diff --git a/tests/ui/pass/map.rs b/tests/ui/pass/map.rs index 58922e1..64c9ae5 100644 --- a/tests/ui/pass/map.rs +++ b/tests/ui/pass/map.rs @@ -21,7 +21,7 @@ fn main() { value: String::from("42"), }; - foo.write::<_, NetworkOrder>(&mut bits_mut, ()) + foo.write::(&mut bits_mut, ()) .expect("successful write"); let mut bits = bits_mut.freeze(); assert_eq!(bits.get_u8().unwrap(), 42); From ca0d1d9c336ad17cfc8bb0e090c89e2982250b89 Mon Sep 17 00:00:00 2001 From: bbaldino Date: Tue, 29 Apr 2025 14:52:39 -0700 Subject: [PATCH 2/5] refactor: rename IntoParselyResult trait method: into_parsely_result_read -> into_parsely_result --- impl/src/code_gen/gen_write.rs | 2 +- impl/src/error.rs | 6 +++--- impl/src/model_types.rs | 4 ++-- tests/expand/map.expanded.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/impl/src/code_gen/gen_write.rs b/impl/src/code_gen/gen_write.rs index e6cee7c..a05247b 100644 --- a/impl/src/code_gen/gen_write.rs +++ b/impl/src/code_gen/gen_write.rs @@ -104,7 +104,7 @@ fn generate_parsely_write_impl_struct( let field_name_string = field_name.to_string(); if let Some(ref sync_expr) = f.sync_expr { quote! { - self.#field_name = (#sync_expr).into_parsely_result_read().with_context(|| format!("Syncing field '{}'", #field_name_string))?; + self.#field_name = (#sync_expr).into_parsely_result().with_context(|| format!("Syncing field '{}'", #field_name_string))?; } } else if f.sync_with.is_empty() && f.ty.is_wrapped() { // We'll allow this combination to skip a call to sync: for types like Option or diff --git a/impl/src/error.rs b/impl/src/error.rs index 7072c9f..b48b39e 100644 --- a/impl/src/error.rs +++ b/impl/src/error.rs @@ -36,11 +36,11 @@ where // expression evaluation where the writable limitations also don't exist, but aren't exactly on the // 'read path' (for example when syncing state) pub trait IntoParselyResult { - fn into_parsely_result_read(self) -> ParselyResult; + fn into_parsely_result(self) -> ParselyResult; } impl IntoParselyResult for T { - fn into_parsely_result_read(self) -> ParselyResult { + fn into_parsely_result(self) -> ParselyResult { Ok(self) } } @@ -49,7 +49,7 @@ impl IntoParselyResult for Result where E: Into, { - fn into_parsely_result_read(self) -> ParselyResult { + fn into_parsely_result(self) -> ParselyResult { self.map_err(Into::into) } } diff --git a/impl/src/model_types.rs b/impl/src/model_types.rs index ef49cc5..b56fd69 100644 --- a/impl/src/model_types.rs +++ b/impl/src/model_types.rs @@ -81,7 +81,7 @@ impl Context { .enumerate() .map(|(idx, e)| { syn::parse2(quote! { - (#e).into_parsely_result_read().with_context(|| format!("{}: expression {}", #context, #idx))? + (#e).into_parsely_result().with_context(|| format!("{}: expression {}", #context, #idx))? }) .unwrap() }) @@ -263,7 +263,7 @@ impl MapExpr { { let original_value = ::#crate_name::ParselyRead::read::<_, T>(buf, ()) .with_context(|| format!("Reading raw value for field '{}'", #field_name_string))?; - (#map_expr)(original_value).into_parsely_result_read() + (#map_expr)(original_value).into_parsely_result() .with_context(|| format!("Mapping raw value for field '{}'", #field_name_string)) } }) diff --git a/tests/expand/map.expanded.rs b/tests/expand/map.expanded.rs index 450d7ee..e86116d 100644 --- a/tests/expand/map.expanded.rs +++ b/tests/expand/map.expanded.rs @@ -19,7 +19,7 @@ impl ::parsely_rs::ParselyRead for Foo { res }))?; (|v: u8| { v.to_string() })(original_value) - .into_parsely_result_read() + .into_parsely_result() .with_context(|| ::alloc::__export::must_use({ let res = ::alloc::fmt::format( format_args!("Mapping raw value for field \'{0}\'", "value"), From ddb8244095e72c08d5a7ef7b64563877694c3e03 Mon Sep 17 00:00:00 2001 From: bbaldino Date: Tue, 29 Apr 2025 15:01:03 -0700 Subject: [PATCH 3/5] refactor: change ParselyRead trait definition to match new ParselyWrite --- impl/src/code_gen/gen_read.rs | 6 +++--- impl/src/model_types.rs | 2 +- impl/src/parsely_read.rs | 16 ++++++++-------- impl/src/parsely_write.rs | 16 ---------------- tests/expand/alignment.expanded.rs | 6 +++--- tests/expand/assertion.expanded.rs | 6 +++--- tests/expand/map.expanded.rs | 6 +++--- tests/ui/pass/alignment.rs | 2 +- tests/ui/pass/basic_read.rs | 2 +- tests/ui/pass/context.rs | 2 +- tests/ui/pass/count.rs | 2 +- tests/ui/pass/map.rs | 2 +- tests/ui/pass/when.rs | 2 +- tests/ui/pass/while.rs | 2 +- 14 files changed, 28 insertions(+), 44 deletions(-) diff --git a/impl/src/code_gen/gen_read.rs b/impl/src/code_gen/gen_read.rs index 2f4a62d..483817f 100644 --- a/impl/src/code_gen/gen_read.rs +++ b/impl/src/code_gen/gen_read.rs @@ -24,7 +24,7 @@ pub fn generate_parsely_read_impl(data: ParselyReadData) -> TokenStream { fn generate_plain_read(ty: &syn::Type, context_values: &[syn::Expr]) -> TokenStream { quote! { - #ty::read::<_, T>(buf, (#(#context_values,)*)) + #ty::read::(buf, (#(#context_values,)*)) } } @@ -202,9 +202,9 @@ fn generate_parsely_read_impl_struct( }; quote! { - impl ::#crate_name::ParselyRead for #struct_name { + impl ::#crate_name::ParselyRead for #struct_name { type Ctx = (#(#context_types,)*); - fn read(buf: &mut B, #ctx_var: (#(#context_types,)*)) -> ::#crate_name::ParselyResult { + fn read(buf: &mut B, #ctx_var: (#(#context_types,)*)) -> ::#crate_name::ParselyResult { #(#context_assignments)* #body diff --git a/impl/src/model_types.rs b/impl/src/model_types.rs index b56fd69..952b6d8 100644 --- a/impl/src/model_types.rs +++ b/impl/src/model_types.rs @@ -261,7 +261,7 @@ impl MapExpr { // value? tokens.extend(quote! { { - let original_value = ::#crate_name::ParselyRead::read::<_, T>(buf, ()) + let original_value = ::#crate_name::ParselyRead::read::(buf, ()) .with_context(|| format!("Reading raw value for field '{}'", #field_name_string))?; (#map_expr)(original_value).into_parsely_result() .with_context(|| format!("Mapping raw value for field '{}'", #field_name_string)) diff --git a/impl/src/parsely_read.rs b/impl/src/parsely_read.rs index 1c06bc5..b2182ba 100644 --- a/impl/src/parsely_read.rs +++ b/impl/src/parsely_read.rs @@ -2,16 +2,16 @@ use bits_io::prelude::*; use crate::error::ParselyResult; -pub trait ParselyRead: Sized { +pub trait ParselyRead: Sized { type Ctx; - fn read(buf: &mut B, ctx: Self::Ctx) -> ParselyResult; + fn read(buf: &mut B, ctx: Self::Ctx) -> ParselyResult; } macro_rules! impl_parsely_read_builtin { ($type:ty) => { - impl ParselyRead for $type { + impl ParselyRead for $type { type Ctx = (); - fn read(buf: &mut B, _: Self::Ctx) -> ParselyResult { + fn read(buf: &mut B, _: Self::Ctx) -> ParselyResult { ::paste::paste! { Ok(buf.[]()?) } @@ -22,9 +22,9 @@ macro_rules! impl_parsely_read_builtin { macro_rules! impl_parsely_read_builtin_bo { ($type:ty) => { - impl ParselyRead for $type { + impl ParselyRead for $type { type Ctx = (); - fn read(buf: &mut B, _: Self::Ctx) -> ParselyResult { + fn read(buf: &mut B, _: Self::Ctx) -> ParselyResult { ::paste::paste! { Ok(buf.[]::()?) } @@ -33,9 +33,9 @@ macro_rules! impl_parsely_read_builtin_bo { }; } -impl ParselyRead for bool { +impl ParselyRead for bool { type Ctx = (); - fn read(buf: &mut B, _ctx: Self::Ctx) -> ParselyResult { + fn read(buf: &mut B, _ctx: Self::Ctx) -> ParselyResult { Ok(buf.get_bool()?) } } diff --git a/impl/src/parsely_write.rs b/impl/src/parsely_write.rs index 1b0fc7b..3393337 100644 --- a/impl/src/parsely_write.rs +++ b/impl/src/parsely_write.rs @@ -22,22 +22,6 @@ macro_rules! impl_stateless_sync { }; } -/// A marker trait so it can be used as a trait bound -pub trait ParselyWritable { - #[doc(hidden)] - fn _dummy_write(buf: &mut B, order: O, ctx: C) - where - Self: ParselyWrite; -} - -impl ParselyWritable for T { - fn _dummy_write(_buf: &mut B, _order: O, _ctx: C) - where - T: ParselyWrite, - { - } -} - pub trait ParselyWrite: StateSync + Sized { type Ctx; fn write(&self, buf: &mut B, ctx: Self::Ctx) -> ParselyResult<()>; diff --git a/tests/expand/alignment.expanded.rs b/tests/expand/alignment.expanded.rs index a6c5aa5..2e2d67e 100644 --- a/tests/expand/alignment.expanded.rs +++ b/tests/expand/alignment.expanded.rs @@ -3,14 +3,14 @@ use parsely_rs::*; struct Foo { one: u8, } -impl ::parsely_rs::ParselyRead for Foo { +impl ::parsely_rs::ParselyRead for Foo { type Ctx = (); - fn read( + fn read( buf: &mut B, _ctx: (), ) -> ::parsely_rs::ParselyResult { let __bytes_remaining_start = buf.remaining_bytes(); - let one = u8::read::<_, T>(buf, ()).with_context(|| "Reading field 'one'")?; + let one = u8::read::(buf, ()).with_context(|| "Reading field 'one'")?; let __bytes_remaining_end = buf.remaining_bytes(); let mut __amount_read = __bytes_remaining_start - __bytes_remaining_end; while __amount_read % 4usize != 0 { diff --git a/tests/expand/assertion.expanded.rs b/tests/expand/assertion.expanded.rs index d396ab8..de4eb2c 100644 --- a/tests/expand/assertion.expanded.rs +++ b/tests/expand/assertion.expanded.rs @@ -3,13 +3,13 @@ struct Foo { #[parsely(assertion = "|v: &u8| *v % 2 == 0")] value: u8, } -impl ::parsely_rs::ParselyRead for Foo { +impl ::parsely_rs::ParselyRead for Foo { type Ctx = (); - fn read( + fn read( buf: &mut B, _ctx: (), ) -> ::parsely_rs::ParselyResult { - let value = u8::read::<_, T>(buf, ()) + let value = u8::read::(buf, ()) .and_then(|read_value| { let assertion_func = |v: &u8| *v % 2 == 0; if !assertion_func(&read_value) { diff --git a/tests/expand/map.expanded.rs b/tests/expand/map.expanded.rs index e86116d..d672265 100644 --- a/tests/expand/map.expanded.rs +++ b/tests/expand/map.expanded.rs @@ -4,14 +4,14 @@ struct Foo { #[parsely_write(map = "|v: &str| { v.parse::() }")] value: String, } -impl ::parsely_rs::ParselyRead for Foo { +impl ::parsely_rs::ParselyRead for Foo { type Ctx = (); - fn read( + fn read( buf: &mut B, _ctx: (), ) -> ::parsely_rs::ParselyResult { let value = { - let original_value = ::parsely_rs::ParselyRead::read::<_, T>(buf, ()) + let original_value = ::parsely_rs::ParselyRead::read::(buf, ()) .with_context(|| ::alloc::__export::must_use({ let res = ::alloc::fmt::format( format_args!("Reading raw value for field \'{0}\'", "value"), diff --git a/tests/ui/pass/alignment.rs b/tests/ui/pass/alignment.rs index 26dc00c..ddecd9a 100644 --- a/tests/ui/pass/alignment.rs +++ b/tests/ui/pass/alignment.rs @@ -9,7 +9,7 @@ struct Foo { fn main() { let mut bits = Bits::from_static_bytes(&[42, 0, 0, 0]); - let foo = Foo::read::<_, NetworkOrder>(&mut bits, ()).unwrap(); + let foo = Foo::read::(&mut bits, ()).unwrap(); assert_eq!(foo.one, 42); assert_eq!(bits.remaining_bytes(), 0); diff --git a/tests/ui/pass/basic_read.rs b/tests/ui/pass/basic_read.rs index 9cf6ca0..cdc9e0c 100644 --- a/tests/ui/pass/basic_read.rs +++ b/tests/ui/pass/basic_read.rs @@ -8,6 +8,6 @@ struct Foo { fn main() { let mut cursor = Bits::from_static_bytes(&[0b10101010]); - let foo = Foo::read::<_, NetworkOrder>(&mut cursor, ()).expect("successful parse"); + let foo = Foo::read::(&mut cursor, ()).expect("successful parse"); assert!(foo.one); } diff --git a/tests/ui/pass/context.rs b/tests/ui/pass/context.rs index 416e081..67b35c8 100644 --- a/tests/ui/pass/context.rs +++ b/tests/ui/pass/context.rs @@ -20,7 +20,7 @@ struct Foo { fn main() { let mut bits = Bits::from_static_bytes(&[1, 2, 3, 4]); - let foo = Foo::read::<_, NetworkOrder>(&mut bits, (2,)).expect("successful parse"); + let foo = Foo::read::(&mut bits, (2,)).expect("successful parse"); // Should have only read 2 values assert_eq!(foo.data.len(), 2); diff --git a/tests/ui/pass/count.rs b/tests/ui/pass/count.rs index 83278fb..e56956e 100644 --- a/tests/ui/pass/count.rs +++ b/tests/ui/pass/count.rs @@ -10,6 +10,6 @@ struct Foo { fn main() { let mut bits = Bits::from_static_bytes(&[2, 1, 2, 3]); - let foo = Foo::read::<_, NetworkOrder>(&mut bits, ()).expect("successful parse"); + let foo = Foo::read::(&mut bits, ()).expect("successful parse"); assert_eq!(foo.data.len(), 2); } diff --git a/tests/ui/pass/map.rs b/tests/ui/pass/map.rs index 64c9ae5..21162e9 100644 --- a/tests/ui/pass/map.rs +++ b/tests/ui/pass/map.rs @@ -12,7 +12,7 @@ struct Foo { fn main() { let mut bits = Bits::from_static_bytes(&[42]); - let foo = Foo::read::<_, NetworkOrder>(&mut bits, ()).expect("successful parse"); + let foo = Foo::read::(&mut bits, ()).expect("successful parse"); assert_eq!(foo.value, "42"); let mut bits_mut = BitsMut::new(); diff --git a/tests/ui/pass/when.rs b/tests/ui/pass/when.rs index 81e4aec..41b3623 100644 --- a/tests/ui/pass/when.rs +++ b/tests/ui/pass/when.rs @@ -9,7 +9,7 @@ struct Foo { fn main() { let mut bits = Bits::from_static_bytes(&[0b1_0000001, 2]); - let foo = Foo::read::<_, NetworkOrder>(&mut bits, ()).expect("successful parse"); + let foo = Foo::read::(&mut bits, ()).expect("successful parse"); assert_eq!(foo.value, Some(u7::new(1))); } diff --git a/tests/ui/pass/while.rs b/tests/ui/pass/while.rs index 98e7c00..0473e68 100644 --- a/tests/ui/pass/while.rs +++ b/tests/ui/pass/while.rs @@ -9,6 +9,6 @@ struct Foo { fn main() { let mut bits = Bits::from_static_bytes(&[2, 1, 2, 3]); - let foo = Foo::read::<_, NetworkOrder>(&mut bits, ()).expect("successful parse"); + let foo = Foo::read::(&mut bits, ()).expect("successful parse"); assert_eq!(foo.data.len(), 4); } From 760a96fd050b0aab1d1786fda23a4ffff6372dc2 Mon Sep 17 00:00:00 2001 From: bbaldino Date: Wed, 30 Apr 2025 13:24:36 -0700 Subject: [PATCH 4/5] feat: upgrade bits-io version, re-export BitSliceUxExts --- Cargo.lock | 4 ++-- impl/Cargo.toml | 2 +- impl/src/lib.rs | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa4dd10..0384d5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ dependencies = [ [[package]] name = "bits-io" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "334d5023bf30d7edb7798b4dbbef2e2d3c121ecd885445cb1f6b0dbe879ab5e8" +checksum = "db8ee456cc74052abbbfc9e94cff1484c5b23b200aec976a47854078bc16a272" dependencies = [ "bitvec", "bytes", diff --git a/impl/Cargo.toml b/impl/Cargo.toml index f97d4c1..e024d42 100644 --- a/impl/Cargo.toml +++ b/impl/Cargo.toml @@ -7,7 +7,7 @@ description = "Macro-based struct serialization/deserialization" [dependencies] anyhow = "1" -bits-io = { version = "0.5.4" } +bits-io = { version = "0.5.5" } darling = "0.20.10" paste = "1" proc-macro2 = "1" diff --git a/impl/src/lib.rs b/impl/src/lib.rs index ed702ee..9670f45 100644 --- a/impl/src/lib.rs +++ b/impl/src/lib.rs @@ -17,6 +17,7 @@ pub use bits_io::{ }; pub mod nsw_types { + pub use bits_io::nsw_types::from_bitslice::BitSliceUxExts; pub use bits_io::nsw_types::*; } From 1abee7daacdc51fdcf9e49be68c4c1607c4842a3 Mon Sep 17 00:00:00 2001 From: bbaldino Date: Wed, 30 Apr 2025 13:31:29 -0700 Subject: [PATCH 5/5] refactor: simplify alignment logic --- impl/src/code_gen/gen_read.rs | 5 +---- impl/src/code_gen/gen_write.rs | 5 +---- tests/expand/alignment.expanded.rs | 10 ++-------- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/impl/src/code_gen/gen_read.rs b/impl/src/code_gen/gen_read.rs index 483817f..3cc2b73 100644 --- a/impl/src/code_gen/gen_read.rs +++ b/impl/src/code_gen/gen_read.rs @@ -182,11 +182,8 @@ fn generate_parsely_read_impl_struct( #(#field_reads)* - let __bytes_remaining_end = buf.remaining_bytes(); - let mut __amount_read = __bytes_remaining_start - __bytes_remaining_end; - while __amount_read % #alignment != 0 { + while (__bytes_remaining_start - buf.remaining_bytes()) % #alignment != 0 { buf.get_u8().context("padding")?; - __amount_read += 1; } } } else { diff --git a/impl/src/code_gen/gen_write.rs b/impl/src/code_gen/gen_write.rs index a05247b..eff0cc2 100644 --- a/impl/src/code_gen/gen_write.rs +++ b/impl/src/code_gen/gen_write.rs @@ -127,11 +127,8 @@ fn generate_parsely_write_impl_struct( #(#field_writes)* - let __bytes_remaining_end = buf.remaining_mut_bytes(); - let mut __amount_written = __bytes_remaining_start - __bytes_remaining_end; - while __amount_written % #alignment != 0 { + while (__bytes_remaining_start - buf.remaining_mut_bytes()) % #alignment != 0 { let _ = buf.put_u8(0).context("padding")?; - __amount_written += 1; } } diff --git a/tests/expand/alignment.expanded.rs b/tests/expand/alignment.expanded.rs index 2e2d67e..948c66b 100644 --- a/tests/expand/alignment.expanded.rs +++ b/tests/expand/alignment.expanded.rs @@ -11,11 +11,8 @@ impl ::parsely_rs::ParselyRead for Foo { ) -> ::parsely_rs::ParselyResult { let __bytes_remaining_start = buf.remaining_bytes(); let one = u8::read::(buf, ()).with_context(|| "Reading field 'one'")?; - let __bytes_remaining_end = buf.remaining_bytes(); - let mut __amount_read = __bytes_remaining_start - __bytes_remaining_end; - while __amount_read % 4usize != 0 { + while (__bytes_remaining_start - buf.remaining_bytes()) % 4usize != 0 { buf.get_u8().context("padding")?; - __amount_read += 1; } Ok(Self { one }) } @@ -31,11 +28,8 @@ impl ::parsely_rs::ParselyWrite for Foo { ); res }))?; - let __bytes_remaining_end = buf.remaining_mut_bytes(); - let mut __amount_written = __bytes_remaining_start - __bytes_remaining_end; - while __amount_written % 4usize != 0 { + while (__bytes_remaining_start - buf.remaining_mut_bytes()) % 4usize != 0 { let _ = buf.put_u8(0).context("padding")?; - __amount_written += 1; } Ok(()) }