tinyklv 0.1.0

The simplest Key-Length-Value (KLV) framework in Rust
Documentation
# Traits

`use tinyklv::prelude::*` brings every trait into scope so the method
calls work. This page documents the traits a user interacts with
directly.

## Decode traits

### `DecodeValue<S>`

```rust,ignore
pub trait DecodeValue<S>: Sized
where
    S: winnow::stream::Stream,
{
    fn decode_value(input: &mut S) -> crate::Result<Self>;
}
```

Parses a KLV triple stream until the container's field set is
satisfied (or the stream is exhausted, depending on
`deny_unknown_keys`). Generated by `#[derive(Klv)]` unless
`allow_unimplemented_decode` is set.

Types implementing `DecodeValue` also get
`Vec<T>::decode_value(&mut stream) -> Vec<T>` via a blanket impl for
unframed repeated decode. For sentinel-framed streams, use
`T::drain_frames(&mut stream)` instead (requires `DecodeFrame`). See
[Tutorial 13 - Repeated extraction](../tutorial/advanced/13-repeated-extraction.md).

### `DecodePartial<S>`

```rust,ignore
pub enum Packet<T, P>
where
    P: Partial<Final = T>,
{
    Ready(T),
    NeedMore(P),
}

pub trait DecodePartial<S>: Sized
where
    S: winnow::stream::Stream,
{
    type Partial: Default + Partial<Final = Self>;
    fn decode_partial(
        input: &mut S,
    ) -> Result<Packet<Self, Self::Partial>, &'static str>;
}
```

Streaming-aware companion to `DecodeValue`. Returns a `Packet`
enum wrapped in `Result`:

| Variant | Meaning |
|---------|---------|
| `Ready(t)` | Full value decoded; cursor advanced past consumed bytes. |
| `NeedMore(p)` | Not enough bytes yet; partial `p` carries every field decoded so far. Cursor rewound to allow retry with more bytes. |

The outer `Result` carries a `&'static str` label on `Err` - not a
`ContextError`. `Err(label)` means unrecoverable parse failure; the
`Decoder` wraps the label into a `ContextError` with position info.

`DecodeValue` and `DecodePartial` are both generated by
`#[derive(Klv)]`. `DecodeValue` is a thin wrapper that delegates to
`DecodePartial` and maps `NeedMore` to a truncation `Err`, because the
one-shot contract assumes the caller has already committed all bytes.

Typical direct use is rare - most callers reach for `Decoder` instead
(see below). Direct use is valuable when you know the input is exactly
one complete packet's body and want to inspect the result explicitly.
At end-of-input, `decode_partial` still returns `NeedMore(p)` unless a
break condition ends the packet; if the caller knows the body is
complete, it should call `p.finalize()`.

### `Partial`

```rust,ignore
pub trait Partial: Sized {
    type Final;
    fn finalize(self) -> Result<Self::Final, &'static str>;
}
```

The in-flight parse state that can be finalised into a complete value.
`#[derive(Klv)]` generates a mirror struct (e.g. `MyPacketPartialPacket`)
where every KLV field becomes `Option<T>`. `finalize()` validates that
all required fields are populated and yields the final struct.

`TryFrom<Partial> for T` is also generated, so
`partial.try_into()` works without importing the `Partial` trait
directly.

### `ResumePartial<S>` (internal)

Re-entry point for resuming a paused decode from an existing partial.
Not typically called directly - `Decoder` and `DecodePartial` use it
internally. Advanced callers who manage their own partial lifecycle
can call `T::resume_partial(&mut input, partial)` to continue decoding
from a saved `NeedMore` state.

### `Decoder<P, S>`

```rust,ignore
pub struct Decoder<P, S> { /* owned Vec<u8> buffer + optional partial */ }

impl<P, S> Decoder<P, S> {
    pub fn new() -> Self;
    pub fn with_capacity(cap: usize) -> Self;
    pub fn feed(&mut self, bytes: &[u8]);
    pub fn buffered(&self) -> &[u8];
    pub fn partial(&self) -> Option<&P>;
    pub fn into_partial(self) -> Option<P>;
    pub fn clear(&mut self);
    pub fn iter(&mut self) -> DecoderIter<'_, P, S>;
    pub fn next<T>(&mut self) -> Option<T>;
}

impl<P, T> Decoder<P, &[u8]>
where
    P: Partial<Final = T> + Default,
{
    pub fn finish(self) -> Result<T, DecodeIterError>;
}
```

User-facing buffered streaming API. Owns a byte buffer, accepts
incremental feeds from a socket / file / ring buffer, and yields
fully-decoded `T` values as enough bytes arrive.

Requires `T` to carry a `sentinel = ...` attribute - the decoder uses
the sentinel + declared packet length to locate each packet's
boundaries inside the buffer. Without that, there is no way to tell
one packet's bytes from the next inside a continuous stream.

Internally it has two modes:

- fresh mode: seek sentinel, read frame length, call `decode_partial` on the body
- resume mode: keep the in-flight partial and continue it with newly fed bytes

`next()` returns:

| Return | Meaning |
|--------|---------|
| `Some(t)` | Packet decoded; its framed bytes are drained from the internal buffer. |
| `None` | Need more bytes, a malformed packet was skipped, or no sentinel found yet. Call `feed` and retry. |

`finish(self) -> Result<T, DecodeIterError>` consumes the decoder and
force-finalises the in-flight partial. Use when the upstream signals
"no more bytes." `DecodeIterError` variants: `Eof`, `NeedMore`,
`Malformed(ContextError)`.

Typical usage:

```rust,ignore
let mut dec = Decoder::<MyPacketPartialPacket, &[u8]>::new();
let mut scratch = [0u8; 2048];
loop {
    let n = socket.recv(&mut scratch)?;
    dec.feed(&scratch[..n]);
    for pkt in dec.iter() {
        handle(pkt);
    }
}
```

See [Tutorial 15 - Streaming partial packets](../tutorial/advanced/15-streaming-decode.md).

### `DecodeFrame<S>`

```rust,ignore
pub trait DecodeFrame<S>: Sized
where
    S: winnow::stream::Stream,
{
    fn decode_frame(input: &mut S) -> crate::Result<Self>;
}
```

Seeks the sentinel, reads the frame length, subslices the stream to
exactly that many bytes, and calls `decode_value` on the subslice.

`DecodeFrame` is a blanket impl over `SeekSentinel + DecodeValue`.
The derive does not generate it directly - it generates `SeekSentinel`
(which scans `input` for the sentinel, consumes sentinel + length, and
returns the body as a sub-slice), and the blanket impl provides
`decode_frame` for free. Only available when `sentinel = ...` is
declared.

## Encode traits

### `EncodeValue<O>`

```rust,ignore
pub trait EncodeValue<O: EncodedOutput> {
    fn encode_value(&self) -> O;
}
```

Emits the KLV triples for this value (no sentinel, no frame length).
Generated by `#[derive(Klv)]` unless `allow_unimplemented_encode` is
set. `O` is typically `Vec<u8>`.

### `EncodeFrame<O>`

```rust,ignore
pub trait EncodeFrame<O: EncodedOutput> {
    fn encode_frame(&self) -> O;
}
```

Emits a full packet: `sentinel + length + <encode_value output>`.
Only generated when the container declares `sentinel = ...`.

### `EncodeAs`

Controls how the `&` sigil routes a field reference to an encoder.
Documented in full at
[Sigil coercion & `EncodeAs`](./sigil-coercion.md).

## Control traits

### `BreakCondition<S>`

```rust,ignore
pub trait BreakCondition<S> {
    fn break_condition<K, L>(
        decoded_key: K,
        decoded_len: L,
    ) -> BreakConditionType;
}
```

User-extensible stop predicate consulted inside the derive-generated
`decode_value` loop for each decoded `(key, len)`. A blanket impl
returning `Proceed` covers the common case. Override requires a manual
`DecodeValue` impl - the blanket impl wins over a derived one. See
[Tutorial 14 - Break conditions](../tutorial/advanced/14-break-condition.md).

`BreakConditionType` variants:

| Variant | Meaning |
|---------|---------|
| `Proceed` | Run the normal decode step for this key (default) |
| `Skip` | Consume the value bytes, continue the loop |
| `Done` | Stop looping, return accumulated fields |
| `Abort(ContextError)` | Stop looping, return `Err` |