spf 0.9.0

.spf (Simple Pixel Font) file parser
Documentation
# Tagging with `spf.rs`

### Synopsis
The `tagging` feature, which is enabled by default, records the exact byte and bit range of every field `spf.rs` writes or reads, tagged by what it is. This is useful for inspecting a `.spf` file's structure: which bytes belong to which table, record, or field, right down to individual bits.

### Turning tagging on
Construct a [`core::DeserializeEngine`] with a [`tagging::TagWriterImpl`] instead of the default no-op writer, using [`core::DeserializeEngine::from_data_and_tags`]:

```rs
use spf::core::*;
use spf::tagging::TagWriterImpl;

let mut engine = DeserializeEngine::from_data_and_tags(&buffer, TagWriterImpl::default());
deserialize_with_engine(&mut engine).unwrap();

println!("{}", engine.tags);
```

[`tagging::TagWriterImpl`] implements Rust's standard `Display` trait, printing one line per tag. Using the following format: [`tagging::TagKind`] and its `start_byte:start_bit - end_byte:end_bit` span. Serializing works the same way — [`core::SerializeEngine`] takes a [`tagging::TagWriter`] too.

### Reading the output
Here's real output from parsing `res/sampleToyFont.spf`, the file used in `spf.rs`'s own integration tests. Only the first few lines and a `CharacterTable` are shown:

```text
Signature 0:0 - 4:0
Version  4:0 - 5:0
CompactFlag  5:0 - 5:1
Reserved 5:1 - 6:0
Header 5:0 - 6:0
TableIdentifier  6:0 - 7:0
CharacterTableUseAdvanceX  7:0 - 7:1
CharacterTableUsePixmapIndex  7:1 - 7:2
CharacterTableUsePixmapTableIndex  7:2 - 7:3
Reserved 7:3 - 8:0
CharacterTableModifierFlags  7:0 - 8:0
...
CharacterCodePoints  13:0 - 15:0
CharacterRecord  13:0 - 15:0
...
CharacterTable  6:0 - 25:0
```

Look at the spans, not just the order. `Header 5:0 - 6:0` contains both `CompactFlag 5:0 - 5:1` and `Reserved 5:1 - 6:0`. Here, the flag bit and the reserved bits share the Header byte span. Further down, `CharacterTable 6:0 - 25:0` contains every tag between it and `TableIdentifier`, including `CharacterRecord 13:0 - 15:0`, which itself contains `CharacterCodePoints 13:0 - 15:0`. Tags nest by span containment: a table's span contains its records' spans, which contain their fields' spans, down to individual configuration bits.

Note that this isn't a tree in the data structure, [`tagging::TagWriterImpl::tags`] is a flat `Vec<`[`tagging::Tag`]`>` stored in write/read order.

### Finding a specific field
Since [`tagging::Tag`] just pairs a [`tagging::TagKind`] with a [`tagging::Span`], filtering for what you want is a normal iterator operation:

```rs
let widths: Vec<_> = engine.tags.tags.iter()
    .filter(|tag| matches!(tag.kind, spf::tagging::TagKind::PixmapCustomWidth { .. }))
    .collect();
```

### Cost when disabled
If you don't need tags, use [`core::DeserializeEngine::from_data`] instead as it defaults to [`tagging::TagWriterNoOp`], whose [`tagging::TagWriter`] methods are empty function bodies the compiler removes entirely. There's no runtime cost for tagging you don't use.