@@ -83,6 +83,28 @@ pub trait ParselyWrite<B>: StateSync + Sized {
83
83
}
84
84
```
85
85
86
+ The ` StateSync ` trait is a required supertrait of ` ParselyWrite ` and enforces
87
+ synchronization of fields before writing.
88
+
89
+ ``` rust
90
+ use parsely_rs :: * ;
91
+
92
+ pub trait StateSync : Sized {
93
+ type SyncCtx ;
94
+
95
+ fn sync (& mut self , sync_ctx : Self :: SyncCtx ) -> ParselyResult <()> {
96
+ Ok (())
97
+ }
98
+ }
99
+ ```
100
+
101
+ When deriving ` ParselyWrite ` , a ` StateSync ` implementation will be generated as
102
+ well. See the [ dependent fields section] ( #dependent-fields ) for more
103
+ information on how attributes can be used to customize the behavior. If you
104
+ manually implement ` ParselyWrite ` yourself, you'll need to implement
105
+ ` StateSync ` as well. If the field requires no synchronization, you can use the
106
+ ` impl_stateless_sync ` macro to generate a default impl for your type.
107
+
86
108
Sometimes serializing or deserializing a type requires additional data that may
87
109
come from somewhere else. The ` Ctx ` generic can be defined as a tuple and the
88
110
` ctx ` argument can be used to pass additional values.
@@ -162,12 +184,14 @@ the map attribute must be applied independently for reading and writing via
162
184
` #[parsely_read] ` and ` #[parsely_write] `
163
185
164
186
When passed via ` #[parsely_read] ` , the argument must evaluate to a function
165
- which takes a type T by value, where T is ` ParselyRead ` and should return a
166
- ` ParselyResult<U> ` , where U matches the type of the field.
187
+ or a closure which takes a type ` T ` by value where ` T: ParselyRead ` and can
188
+ return either a type ` U ` or a ` Result<U, E> ` where ` U ` is the type of
189
+ the field and ` E: Into<anyhow::Error> ` .
167
190
168
191
When passed via ` #[parsely_write] ` , the argument must evaluate to a function
169
- which takes a reference to a type T, where T is the type of the field and
170
- returns a ` ParselyResult<U> ` where U is ` ParselyWrite ` .
192
+ or closure which takes a reference to a type ` T ` , where ` T ` is the type of
193
+ the field and returns either a type ` U ` or a ` Result<U, E> ` where
194
+ ` U: ParselyWrite ` and ` E: Into<anyhow::Error> ` .
171
195
172
196
| Mode | Available |
173
197
| --------- | -------- |
@@ -180,7 +204,7 @@ returns a `ParselyResult<U>` where U is `ParselyWrite`.
180
204
<details >
181
205
<summary >Click to expand</summary >
182
206
183
- This example has a String field but reads a u8 from the
207
+ This example has a ` String ` field but reads a ` u8 ` from the
184
208
buffer and converts it. On write it does the opposite.
185
209
186
210
``` rust
@@ -194,17 +218,26 @@ struct Foo {
194
218
#[parsely_write(map = " |v: &str| { v.parse::<u8>() }" )]
195
219
value : String ,
196
220
}
221
+
222
+ let mut bits = Bits :: from_static_bytes (& [42 ]);
223
+
224
+ let foo = Foo :: read :: <NetworkOrder >(& mut bits , ()). expect (" successful read" );
225
+ assert_eq! (foo . value, " 42" );
226
+
227
+ let mut bits_mut = BitsMut :: new ();
228
+ foo . write :: <NetworkOrder >(& mut bits_mut , ()). expect (" successful write" );
229
+ assert_eq! (bits_mut . freeze (), Bits :: from_static_bytes (& [42 ]));
197
230
```
198
231
199
232
</details >
200
233
201
234
### Count
202
235
203
- When reading a ` Vec<T>`` , we need to know how many elements to read. The ` count`
236
+ When reading a ` Vec<T> ` , we need to know how many elements to read. The ` count `
204
237
attribute is used to describe how many elements should be read from the buffer.
205
238
206
- Any expression can be passed that evaluates to a number that can be used in a
207
- range expression.
239
+ Any expression that evaluates to a number that can be used in a range
240
+ expression can be used .
208
241
209
242
| Mode | Available |
210
243
| --------- | -------- |
@@ -217,8 +250,8 @@ range expression.
217
250
<details >
218
251
<summary >Click to expand</summary >
219
252
220
- This (quite contrived) example has a boolean field but reads a u1 from the
221
- buffer and converts it. On write it does the opposite.
253
+ Here a ` u8 ` is read into the ` data_size ` field and the value of that field is
254
+ used to denote the number of elements.
222
255
223
256
``` rust
224
257
use parsely_rs :: * ;
@@ -253,8 +286,8 @@ false means it will be skipped and set to `None`.
253
286
<details >
254
287
<summary >Click to expand</summary >
255
288
256
- This (quite contrived) example has a boolean field but reads a u1 from the
257
- buffer and converts it. On write it does the opposite.
289
+ Here, a boolean value is read into the ` has_value ` field and whether a ` u32 ` is
290
+ read for ` value ` field is based on if ` has_value ` is true or false.
258
291
259
292
``` rust
260
293
use parsely_rs :: * ;
@@ -274,6 +307,42 @@ struct Foo {
274
307
275
308
### Assign from
276
309
310
+ Sometimes a field should be assigned to a value rather than read from the
311
+ buffer. Any expression evaluating to the type of the field can be passed.
312
+
313
+ | Mode | Available |
314
+ | --------- | -------- |
315
+ | ` #[parsely] ` | :x : |
316
+ | ` #[parsely_read] ` | :white_check_mark : |
317
+ | ` #[parsely_write] ` | :x : |
318
+
319
+ #### Examples
320
+
321
+ <details >
322
+ <summary >Click to expand</summary >
323
+
324
+ Here the ` header ` value has already been read and is passed in via context. It
325
+ is then assigned directly to the ` header ` field.
326
+
327
+ ``` rust
328
+ use parsely_rs :: * ;
329
+
330
+ #[derive(ParselyRead , ParselyWrite )]
331
+ struct Header {
332
+ payload_type : u8 ,
333
+ }
334
+
335
+ #[derive(ParselyRead , ParselyWrite )]
336
+ #[parsely_read(required_context(" header: Header" ))]
337
+ struct Packet {
338
+ #[parsely_read(assign_from = " header" )]
339
+ header : Header ,
340
+ other_field : u8 ,
341
+ }
342
+ ```
343
+
344
+ </details >
345
+
277
346
### Dependent fields
278
347
279
348
Often times packets will have fields whose values depend on other fields. A
@@ -283,24 +352,16 @@ header might have a length field that should reflect the size of a payload.
283
352
The ` sync_args ` attribute is used on a struct to define what external
284
353
information is needed in order to sync its fields correctly.
285
354
286
- The ` sync_func ` attribute is used on a specific field to define how it should
287
- use the args from ` sync_args ` in order to sync.
355
+ The ` sync_expr ` attribute is used on a specific field to define how it should
356
+ use the values from ` sync_args ` (or elsewhere) in order to sync.
288
357
289
358
The ` sync_with ` attribute is used to pass information to a field to synchronize
290
359
it.
291
360
292
- All types annotated with ` ParselyWrite ` have a ` sync ` method generated that
293
- looks like this:
294
-
295
- ``` ignore
296
- pub fn sync(&mut self, sync_args: ___) -> ParselyResult<()>;
297
- ```
298
-
299
- where ` sync_args ` is a tuple containing the types defined in the ` sync_args ` attribute.
300
-
301
- This sync function should be called explicitly before writing the type to a
302
- buffer to make sure all fields are consistent.
361
+ All types that implement ` ParselyWrite ` must also implement the ` StateSync ` trait.
303
362
363
+ The ` sync ` function from the ` StateSync ` trait should be called explicitly
364
+ before writing the type to a buffer to make sure all fields are consistent.
304
365
| Mode | Available |
305
366
| --------- | -------- |
306
367
| ` #[parsely] ` | :x : |
@@ -414,7 +475,6 @@ fn run(buf: &mut Bits) {
414
475
let foo_header = FooHeader :: read :: <NetworkOrder >(buf , ()). unwrap ();
415
476
// Pass the relevant field from header to the payload's read method
416
477
let foo_payload = Foo :: read :: <NetworkOrder >(buf , (foo_header . payload_len,)). unwrap ();
417
-
418
478
}
419
479
420
480
@@ -424,7 +484,6 @@ fn run(buf: &mut Bits) {
424
484
425
485
## TODO/Roadmap
426
486
427
- * Unit/Newtype/Tuple struct and enum support
428
487
* Probably need some more options around collections (e.g. ` while ` )
429
488
430
489
## Differences from Deku
0 commit comments