Skip to content

Commit 2ca7a86

Browse files
committed
docs: more readme improvements
1 parent a2809ba commit 2ca7a86

File tree

1 file changed

+86
-27
lines changed

1 file changed

+86
-27
lines changed

README.md

Lines changed: 86 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,28 @@ pub trait ParselyWrite<B>: StateSync + Sized {
8383
}
8484
```
8585

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+
86108
Sometimes serializing or deserializing a type requires additional data that may
87109
come from somewhere else. The `Ctx` generic can be defined as a tuple and the
88110
`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
162184
`#[parsely_read]` and `#[parsely_write]`
163185

164186
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>`.
167190

168191
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>`.
171195

172196
| Mode | Available |
173197
| --------- | -------- |
@@ -180,7 +204,7 @@ returns a `ParselyResult<U>` where U is `ParselyWrite`.
180204
<details>
181205
<summary>Click to expand</summary>
182206

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
184208
buffer and converts it. On write it does the opposite.
185209

186210
```rust
@@ -194,17 +218,26 @@ struct Foo {
194218
#[parsely_write(map = "|v: &str| { v.parse::<u8>() }")]
195219
value: String,
196220
}
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]));
197230
```
198231

199232
</details>
200233

201234
### Count
202235

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`
204237
attribute is used to describe how many elements should be read from the buffer.
205238

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.
208241

209242
| Mode | Available |
210243
| --------- | -------- |
@@ -217,8 +250,8 @@ range expression.
217250
<details>
218251
<summary>Click to expand</summary>
219252

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.
222255

223256
```rust
224257
use parsely_rs::*;
@@ -253,8 +286,8 @@ false means it will be skipped and set to `None`.
253286
<details>
254287
<summary>Click to expand</summary>
255288

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.
258291

259292
```rust
260293
use parsely_rs::*;
@@ -274,6 +307,42 @@ struct Foo {
274307

275308
### Assign from
276309

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+
277346
### Dependent fields
278347

279348
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.
283352
The `sync_args` attribute is used on a struct to define what external
284353
information is needed in order to sync its fields correctly.
285354

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.
288357

289358
The `sync_with` attribute is used to pass information to a field to synchronize
290359
it.
291360

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.
303362

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.
304365
| Mode | Available |
305366
| --------- | -------- |
306367
| `#[parsely]` | :x: |
@@ -414,7 +475,6 @@ fn run(buf: &mut Bits) {
414475
let foo_header = FooHeader::read::<NetworkOrder>(buf, ()).unwrap();
415476
// Pass the relevant field from header to the payload's read method
416477
let foo_payload = Foo::read::<NetworkOrder>(buf, (foo_header.payload_len,)).unwrap();
417-
418478
}
419479

420480

@@ -424,7 +484,6 @@ fn run(buf: &mut Bits) {
424484

425485
## TODO/Roadmap
426486

427-
* Unit/Newtype/Tuple struct and enum support
428487
* Probably need some more options around collections (e.g. `while`)
429488

430489
## Differences from Deku

0 commit comments

Comments
 (0)