jam1garner / jam1garner/binrw

Add a `position_of!` macro to reference earlier fields position

Open
#161 0 comments 1 reaction 0 assignees View on GitHub
enhancement
Dominant language
Rust
Stars
853
Forks
56
PR merge metrics
No merged PRs in 30d

Description

This issue is a feature proposal for adding a `position_of!` macro in the generated code for reading/writing.
```rust
#[binrw]
struct Test {
a: u32,
b: u32,
#[br(calc = position_of!(b) + 1000]
c: u64,
}
```

## Main features

- it is defined in the `BinRead::read`/`BinWrite::write` function, so it is not visible from outside
- `position_of!(x)` gives the stream position at which the field `x` has been read

## Additional ideas

- to make look less like it's popping out of nowhere, add a struct-level `position_of` attribute such as:
- if it is absent, the macro is not added in the generated code
- `#[br(position_of)]` means we add it with it's default name
- `#[br(position_of = my_name)]` means we add it and name it `my_name`

## Concerns

- what to return when the field is not actually read/written:
- when it's conditionally read/written (with and `if` attribute for example), should it be:
- an `Option` which is `None` when the field is not read and `Some` when it is **(preferred)**
- a regular `u64` that gives the position of the stream where the field could've been read (introduce concerns with conditionally applied `seek`/`align` directives)
- when it's calculated with `calc`, should it be:
- not defined at all **(preferred)**
- a regular `u64` that gives the position of the stream at the time the field is calculated
- any other edge case?

## Implementation proposals

The main difficulty is that we need to define a macro that allows us to reference earlier fields only:
- we can redefine the macro each time a new field position is known, so for the given example you'd have:
```rust
macro_rules! position_of {
(a) => {
__binrw_generated_position_of_a
};
}
```
once the position of `a` is known, and as soon as the position of `b` is known, redefine it this way:
```rust
macro_rules! position_of {
(a) => {
__binrw_generated_position_of_a
};
(b) => {
__binrw_generated_position_of_b
};
}
```
- or we can define it once and make it craft new idents with something like [paste](https://crates.io/crates/paste)

Contributor guide

Open the contributing guide

Research direction

Start with the generated BinRead::read and BinWrite::write implementations, where the proposed macro would be defined and field positions become known. Resolve the conditional, calculated-field, naming, and earlier-field semantics, then validate that the generated code supports the issue's example for both reading and writing.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.