Skip to content

Commit 85326c9

Browse files
committed
Draft: Improve DekuUpdate
- Allow DekuUpdate::update to have ctx - Add call_update, to call `update()` on self. - Add update_custom to call custom function
1 parent 069549d commit 85326c9

File tree

6 files changed

+141
-21
lines changed

6 files changed

+141
-21
lines changed

deku-derive/src/lib.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ struct DekuData {
133133
/// default context passed to the field
134134
ctx_default: Option<Punctuated<syn::Expr, syn::token::Comma>>,
135135

136+
update_ctx: Option<syn::punctuated::Punctuated<syn::FnArg, syn::token::Comma>>,
137+
136138
/// A magic value that must appear at the start of this struct/enum's data
137139
magic: Option<syn::LitByteStr>,
138140

@@ -209,6 +211,7 @@ impl DekuData {
209211
endian: receiver.endian,
210212
ctx: receiver.ctx,
211213
ctx_default: receiver.ctx_default,
214+
update_ctx: receiver.update_ctx,
212215
magic: receiver.magic,
213216
id: receiver.id,
214217
id_type: receiver.id_type?,
@@ -455,6 +458,13 @@ struct FieldData {
455458
/// map field when updating struct
456459
update: Option<TokenStream>,
457460

461+
/// map field when updating struct
462+
update_custom: Option<TokenStream>,
463+
464+
update_ctx: Option<Punctuated<syn::Expr, syn::token::Comma>>,
465+
466+
call_update: bool,
467+
458468
/// custom field reader code
459469
reader: Option<TokenStream>,
460470

@@ -523,6 +533,12 @@ impl FieldData {
523533
.transpose()
524534
.map_err(|e| e.to_compile_error())?;
525535

536+
let update_ctx = receiver
537+
.update_ctx?
538+
.map(|s| s.parse_with(Punctuated::parse_terminated))
539+
.transpose()
540+
.map_err(|e| e.to_compile_error())?;
541+
526542
let data = Self {
527543
ident: receiver.ident,
528544
ty: receiver.ty,
@@ -539,6 +555,9 @@ impl FieldData {
539555
map: receiver.map?,
540556
ctx,
541557
update: receiver.update?,
558+
update_custom: receiver.update_custom?,
559+
update_ctx,
560+
call_update: receiver.call_update,
542561
reader: receiver.reader?,
543562
writer: receiver.writer?,
544563
skip: receiver.skip,
@@ -753,6 +772,9 @@ struct DekuReceiver {
753772
#[darling(default)]
754773
ctx_default: Option<syn::punctuated::Punctuated<syn::Expr, syn::token::Comma>>,
755774

775+
#[darling(default)]
776+
update_ctx: Option<syn::punctuated::Punctuated<syn::FnArg, syn::token::Comma>>,
777+
756778
/// A magic value that must appear at the start of this struct/enum's data
757779
#[darling(default)]
758780
magic: Option<syn::LitByteStr>,
@@ -928,6 +950,19 @@ struct DekuFieldReceiver {
928950
#[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
929951
update: Result<Option<TokenStream>, ReplacementError>,
930952

953+
/// map field when updating struct
954+
#[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
955+
update_custom: Result<Option<TokenStream>, ReplacementError>,
956+
957+
/// skip field reading/writing
958+
#[darling(default)]
959+
call_update: bool,
960+
961+
// TODO: The type of it should be `Punctuated<Expr, Comma>`
962+
// https://github.com/TedDriggs/darling/pull/98
963+
#[darling(default = "default_res_opt", map = "map_option_litstr")]
964+
update_ctx: Result<Option<syn::LitStr>, ReplacementError>,
965+
931966
/// custom field reader code
932967
#[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
933968
reader: Result<Option<TokenStream>, ReplacementError>,

deku-derive/src/macros/deku_write.rs

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
126126
}
127127

128128
let (ctx_types, ctx_arg) = gen_ctx_types_and_arg(input.ctx.as_ref())?;
129+
let (update_ctx_types, update_ctx_arg) = gen_ctx_types_and_arg(input.update_ctx.as_ref())?;
129130

130131
let write_body = quote! {
131132
match *self {
@@ -144,9 +145,8 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
144145

145146
tokens.extend(quote! {
146147
#[automatically_derived]
147-
impl #imp ::#crate_::DekuUpdate for #ident #wher {
148-
#[inline]
149-
fn update(&mut self) -> core::result::Result<(), ::#crate_::DekuError> {
148+
impl #imp ::#crate_::DekuUpdate<#update_ctx_types> for #ident #wher {
149+
fn update(&mut self, #update_ctx_arg) -> core::result::Result<(), ::#crate_::DekuError> {
150150
#update_use
151151
#(#field_updates)*
152152

@@ -339,6 +339,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
339339
}
340340

341341
let (ctx_types, ctx_arg) = gen_ctx_types_and_arg(input.ctx.as_ref())?;
342+
let (update_ctx_types, update_ctx_arg) = gen_ctx_types_and_arg(input.update_ctx.as_ref())?;
342343

343344
let write_body = quote! {
344345
#magic_write
@@ -355,9 +356,8 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
355356

356357
tokens.extend(quote! {
357358
#[automatically_derived]
358-
impl #imp ::#crate_::DekuUpdate for #ident #wher {
359-
#[inline]
360-
fn update(&mut self) -> core::result::Result<(), ::#crate_::DekuError> {
359+
impl #imp ::#crate_::DekuUpdate<#update_ctx_types> for #ident #wher {
360+
fn update(&mut self, #update_ctx_arg) -> core::result::Result<(), ::#crate_::DekuError> {
361361
#update_use
362362

363363
match self {
@@ -441,17 +441,34 @@ fn emit_field_update(
441441
return None;
442442
}
443443
let field_ident = f.get_ident(i, object_prefix.is_none());
444-
let deref = if object_prefix.is_none() {
445-
Some(quote! { * })
446-
} else {
447-
None
444+
let deref = match object_prefix.is_none() {
445+
true => Some(quote! { * }),
446+
false => None,
448447
};
449448

450-
f.update.as_ref().map(|field_update| {
451-
quote! {
452-
#deref #object_prefix #field_ident = (#field_update).try_into()?;
449+
if f.call_update {
450+
let ctx = match f.update_ctx.as_ref() {
451+
Some(ctx) => quote! {(#ctx)},
452+
None => quote! {()},
453+
};
454+
let a = quote! {
455+
#deref #object_prefix #field_ident.update(#ctx)?;
456+
};
457+
458+
Some(a)
459+
} else {
460+
if let Some(custom) = &f.update_custom {
461+
Some(quote! {
462+
#custom(&mut #object_prefix #field_ident)?;
463+
})
464+
} else {
465+
f.update.as_ref().map(|field_update| {
466+
quote! {
467+
#deref #object_prefix #field_ident = (#field_update).try_into()?;
468+
}
469+
})
453470
}
454-
})
471+
}
455472
}
456473

457474
fn emit_bit_byte_offsets(

examples/update.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use deku::prelude::*;
2+
3+
#[derive(Debug, DekuRead, DekuWrite, PartialEq)]
4+
pub struct Test {
5+
#[deku(call_update, update_ctx = "self.val.len() as u16, 0")]
6+
hdr: Hdr,
7+
8+
#[deku(count = "hdr.length")]
9+
val: Vec<u8>,
10+
11+
#[deku(call_update)]
12+
no_update_ctx: NoUpdateCtx,
13+
14+
#[deku(update_custom = "Self::custom")]
15+
num: u8,
16+
17+
#[deku(update_custom = "Self::other_custom")]
18+
other_num: (u8, u32),
19+
}
20+
21+
impl Test {
22+
fn custom(num: &mut u8) -> Result<(), DekuError> {
23+
*num = 1;
24+
25+
Ok(())
26+
}
27+
28+
fn other_custom(num: &mut (u8, u32)) -> Result<(), DekuError> {
29+
*num = (0xf0, 0x0f);
30+
31+
Ok(())
32+
}
33+
}
34+
35+
#[derive(Debug, DekuRead, DekuWrite, PartialEq)]
36+
#[deku(update_ctx = "val_len: u16, _na: u8")]
37+
struct Hdr {
38+
#[deku(update = "val_len")]
39+
length: u8,
40+
}
41+
42+
#[derive(Debug, DekuRead, DekuWrite, PartialEq)]
43+
struct NoUpdateCtx {
44+
#[deku(update = "0xff")]
45+
val: u8,
46+
}
47+
48+
fn main() {
49+
let mut test = Test {
50+
hdr: Hdr { length: 2 },
51+
val: vec![1, 2],
52+
no_update_ctx: NoUpdateCtx { val: 0 },
53+
num: 0,
54+
other_num: (0, 0),
55+
};
56+
57+
test.val = vec![1, 2, 3];
58+
test.update(()).unwrap();
59+
60+
let expected = Test {
61+
hdr: Hdr { length: 3 },
62+
val: test.val.clone(),
63+
no_update_ctx: NoUpdateCtx { val: 0xff },
64+
num: 1,
65+
other_num: (0xf0, 0x0f),
66+
};
67+
assert_eq!(expected, test);
68+
}

src/attributes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,7 @@ assert_eq!(
908908
value.items.push(0xFF);
909909
910910
// update it, this will update the `count` field
911-
value.update().unwrap();
911+
value.update(()).unwrap();
912912
913913
assert_eq!(
914914
DekuTest { count: 0x03, items: vec![0xAB, 0xCD, 0xFF] },

src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ let data_out = val.to_bytes().unwrap();
138138
assert_eq!(vec![0x02, 0xBE, 0xEF, 0xAA], data_out);
139139
140140
// Use `update` to update `count`
141-
val.update().unwrap();
141+
val.update(()).unwrap();
142142
143143
assert_eq!(DekuTest {
144144
count: 0x03,
@@ -591,9 +591,9 @@ pub trait DekuContainerWrite: DekuWriter<()> {
591591
}
592592

593593
/// "Updater" trait: apply mutations to a type
594-
pub trait DekuUpdate {
594+
pub trait DekuUpdate<Ctx = ()> {
595595
/// Apply updates
596-
fn update(&mut self) -> Result<(), DekuError>;
596+
fn update(&mut self, ctx: Ctx) -> Result<(), DekuError>;
597597
}
598598

599599
/// "Extended Enum" trait: obtain additional enum information

tests/test_attributes/test_update.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ fn test_update() {
1818
assert_eq!(TestStruct { field_a: 0x01 }, ret_read);
1919

2020
// `field_a` field should now be increased
21-
ret_read.update().unwrap();
21+
ret_read.update(()).unwrap();
2222
assert_eq!(0x05, ret_read.field_a);
2323

2424
let ret_write: Vec<u8> = ret_read.try_into().unwrap();
@@ -53,7 +53,7 @@ fn test_update_from_field() {
5353
ret_read.data.push(0xff);
5454

5555
// `count` field should now be increased
56-
ret_read.update().unwrap();
56+
ret_read.update(()).unwrap();
5757
assert_eq!(3, ret_read.count);
5858

5959
// Write
@@ -75,5 +75,5 @@ fn test_update_error() {
7575

7676
let mut val = TestStruct { count: 0x01 };
7777

78-
val.update().unwrap();
78+
val.update(()).unwrap();
7979
}

0 commit comments

Comments
 (0)