#[derive(ULogData)]
struct UserStruct {}
↓
Implements ULogAccess for the struct `UserStruct`
↓
Requires generating:
├── Accessor struct (Self::Accessor)
│ - Named `UserStructAccessor`.
│ - Contains one `usize` field per struct field in `UserStruct`.
│ - Each field holds the **index** of the corresponding ULog field.
│ (based on its name, or overridden via `#[yule_log(field_name = "...")]`).
│
├── Accessor::from_format(&def::Format) -> Result<Accessor, ULogError>
│ - Takes a ULog schema definition (`def::Format`)
│ - Builds a map of field name → index from that schema.
│ - Constructs the accessor struct by looking up each expected field.
│ - Returns error if any field is missing.
│
├── Accessor::get_data(&inst::Format) -> Result<UserStruct, ULogError>
│ - Takes a **message instance** (same shape as the schema).
│ - Uses the precomputed indexes to grab each field.
│ - Calls `<FieldType as FromField>::from_field(...)` to extract each value.
│ - Constructs and returns the actual `UserStruct` by building a struct literal.
│
├── impl FromField for UserStruct
│ - Enables the struct to be parsed as a nested field withing a ULog message.
│ - Match pattern: `FieldValue::ScalarOther(inst_format)`.
│ - Delegates to the accessor: builds it using the format, then calls `get_data`.
│ - Fails with a `TypeMismatch` error if the field isn't a nested struct.
│
└── impl FromField for Vec<UserStruct>
- Enables deserialization from an array of nested structs within a ULOGMessage.
- Match pattern: `FieldValue::ArrayOther(Vec<InstFormat>)`.
- Builds one accessor (from first instance's format).
- Iterates over each element and uses the accessor to get data.
- Returns a `Vec<UserStruct>`, or an error if types mismatch.
#[derive(ULogMessages)]
enum LoggedMessages {
VehicleLocalPosition(VehicleLocalPosition),
Other(yule_log::model::msg::UlogMessage),
}
↓
Generates an `impl LoggedMessages` with a `.stream()` method:
└── LoggedMessages::stream(reader: impl Read) -> Result<impl Iterator<Item = Result<LoggedMessages, ULogError>>>
↓
Requires generating:
├── Internal accessor enum: __yule_log_derive_LoggedMessagesAccessor
│ - Enum with one variant per message type (excluding `forward_other`)
│ - Each variant wraps the corresponding struct's accessor (e.g. VehicleLocalPositionAccessor)
│ - Used at runtime to call `.get_data()` for each message
│
├── Internal iterator struct: __yule_log_derive_LoggedMessages<R>
│ - Holds the actual `ULogParser<R>` and a map: msg_id → accessor enum
│ - Implements Iterator<Item = Result<LoggedMessages, ULogError>>
│
├── impl Iterator for __yule_log_derive_LoggedMessages<R>
│ - Drives the message stream
│ - On `AddSubscription`:
│ - Matches the subscription’s `message_name` and `multi_id` against known enum variants
│ - If a match is found:
│ - Retrieves the schema format.
│ - Constructs an Accessor by calling <VariantType as ULogAccess>::from_format(def::Format).
│ - Stores Accessor instance in msg_id → accessor map.
│ - On `LoggedData`:
│ - Looks up the Accessor by `msg_id`.
│ - If found, uses the stored accessor to extract a value from the message.
│ - Wraps it in the corresponding enum variant and yields it.
│ - On unmatched messages:
│ - If a variant is marked #[yule_log(forward_other)]:
│ - Wraps the full UlogMessage in that variant and yields it.
│
├── Compile-time checks
- Only tuple-style enum variants supported (VariantName(Type)).
- No generics or where clauses allowed.
- Exactly one #[yule_log(forward_other)] allowed.
- Runtime message dispatch logic:
- Obtains `(message_name, multi_id)` pairs from the constants generated by the `ULogData` derive on each variant struct.
(e.g., `__YULE_LOG_SUBSCRIPTION`, `__YULE_LOG_MULTI_ID`)
- Matches these pairs against `AddSubscription` messages emitted by the parser at runtime.
- Generates a match arm to:
- Calls `<VariantType as ULogAccess>::from_format(def::Format)` to construct the accessor
- Stores the accessor keyed by `msg_id` for efficient lookup.
- On receiving `LoggedData` messages with a `msg_id`:
- Looks up the stored accessor by `msg_id`.
- Calls `Accessor.get_data()` to deserialize the payload into `UserStruct`.
- Wraps decoded data in the corresponding `LoggedMessages` enum variant.
- For messages with no matching subscription:
- If the enum contains a variant is marked `#[yule_log(forward_other)]`, wraps and yields the raw message in that variant.
Handoff between ULogData and ULogMessages derives:
- `ULogData` derives implement `ULogAccess` on individual structs.
- `ULogAccess` defines how to build accessors and decode data for that struct.
- `ULogMessages` derives expect enum variants to wrap structs implementing `ULogAccess`.
- At runtime, `ULogMessages`:
- Uses constants from `ULogData` (like `__YULE_LOG_SUBSCRIPTION`) to match subscription messages.
- Calls `<VariantType as ULogAccess>::from_format` to build accessors for variants.
- Stores accessors keyed by `msg_id`.
- Uses accessor's `get_data()` to decode logged data messages.
- Wraps decoded data in the enum variant for user consumption.