SkillAgentSearch skills...

Piaf

A Rust library for decoding binary capability data into a clean, typed model, specialized for EDID.

Install / Use

/learn @DracoWhitefire/Piaf
About this skill

Quality Score

0/100

Supported Platforms

Zed

README

PIAF

CI crates.io docs.rs License: MPL-2.0 Rust 1.85+

A Rust library for decoding binary capability data into a clean, typed model, specialized for EDID.

PIAF reads raw EDID bytes — from a file, a kernel sysfs node, or a direct I²C read — and produces a DisplayCapabilities value with all the information a display or HDMI-adjacent application typically needs: identity, input type, supported modes, color characteristics, HDR metadata, audio capabilities, and more.

Decoding happens in two steps. parse_edid validates the raw bytes and returns a ParsedEdidRef<'_> — a zero-copy view that borrows the block structure directly from the input slice. capabilities_from_edid then runs the registered extension handlers over that structure and produces a DisplayCapabilities — the typed, stable model your application works with. Keeping these steps separate means you can inspect the raw parse result for debugging, or run multiple handler configurations over the same parsed data without re-parsing. Use parse_edid_owned to get an owned ParsedEdid that can outlive the input buffer.

use piaf::{parse_edid, capabilities_from_edid, ExtensionLibrary, ScreenSize};

let bytes = std::fs::read("/sys/class/drm/card0-HDMI-A-1/edid")?;
let library = ExtensionLibrary::with_standard_handlers();
let parsed = parse_edid(&bytes, &library)?;
let caps = capabilities_from_edid(&parsed, &library);

println!("Display: {}", caps.display_name.as_deref().unwrap_or("unknown"));
if let Some(ScreenSize::Physical { width_cm, height_cm }) = caps.screen_size {
    println!("{}×{} cm", width_cm, height_cm);
}
for mode in &caps.supported_modes {
    println!("  {}×{}@{}", mode.width, mode.height, mode.refresh_rate);
}

See examples/inspect_displays.rs for a more complete example.

flowchart LR
    bytes["&[u8]"]
    ref["ParsedEdidRef&lt;'_&gt;"]

    bytes -->|"parse_edid"| ref

    ref -->|"capabilities_from_edid\n+ ExtensionLibrary"| dc["DisplayCapabilities\nalloc / std"]
    ref -->|"capabilities_from_edid_static\n+ STANDARD_HANDLERS"| sc["StaticDisplayCapabilities&lt;N&gt;\nall tiers"]

Why PIAF

Complete extension coverage. Most EDID libraries decode the base block and stop. PIAF decodes 20+ CEA-861 data block types — HDR static and dynamic metadata, HDMI 1.x and HDMI Forum VSDBs, colorimetry, speaker allocation, video timing blocks, and the HDMI Forum Sink Capability block — and all 20 defined DisplayID 1.x block types, covering panel identity, color characteristics, device data, power sequencing, transfer characteristics, and every defined timing format. If the information is in the EDID, PIAF exposes it as typed fields rather than raw bytes.

Pluggable handlers. The extension handler system lets you register your own handler for any extension block tag — override either built-in handler (CEA-861 or DisplayID), add support for a proprietary block, or attach typed custom data to DisplayCapabilities for downstream consumers. Base block parsing is pluggable too.

Honest diagnostics. PIAF distinguishes between hard parse errors (invalid header, checksum failure, truncated input) and non-fatal warnings (unknown extension block, malformed data block, out-of-range manufacturer ID). You decide how strict to be; nothing is silently discarded.

no_std support. The library runs on bare metal. The static extension handler pipeline — capabilities_from_edid_static with STANDARD_HANDLERS — works at all build tiers, including bare no_std without an allocator. Custom handlers implement StaticExtensionHandler using static references instead of Box — see no_std builds below.

Stable consumer model. ParsedEdidRef and ParsedEdid preserve raw bytes; DisplayCapabilities is the typed, stable output. Both implement EdidSource and work directly with the capability pipelines. Parser improvements don't change the consumer-facing API.

Extension system

Cea861Handler covers the common case. Write your own handler to support a proprietary extension block, augment CEA-861 decoding with application-specific logic, or attach typed custom data to DisplayCapabilities for downstream consumers.

Dynamic handlers (std/alloc)

Register via ExtensionLibrary. Uses Box<dyn ExtensionHandler> internally, so requires heap allocation:

use piaf::{ExtensionHandler, DisplayCapabilities, ParseWarning, ExtensionLibrary};

#[derive(Debug)]
struct MyHandler;

impl ExtensionHandler for MyHandler {
    fn process(&self, blocks: &[&[u8; 128]], caps: &mut DisplayCapabilities, warnings: &mut Vec<ParseWarning>) {
        // inspect blocks, set fields on caps
    }
}

let mut library = ExtensionLibrary::new();
library.register(ExtensionMetadata {
    tag: 0xAB,
    display_name: String::from("My Extension"),
    handler: Some(Box::new(MyHandler)),
});

Typed data can be attached to DisplayCapabilities and retrieved by tag:

caps.set_extension_data(0xAB, MyCeaData { version: block[1] });

if let Some(data) = caps.get_extension_data::<MyCeaData>(0xAB) {
    println!("version: {}", data.version);
}

Static handlers (no-alloc)

Use StaticExtensionHandler when heap allocation is unavailable. Pass a static slice to capabilities_from_edid_static:

use piaf::{StaticExtensionHandler, ModeSink, StaticDisplayCapabilities, STANDARD_HANDLERS};

struct MyHandler;

impl StaticExtensionHandler for MyHandler {
    fn tag(&self) -> u8 { 0xAB }
    fn process(&self, blocks: &[&[u8; 128]], ctx: &mut StaticContext<'_>) {
        // push modes via ctx.push_mode(...) and warnings via ctx.push_warning(...)
    }
}

static MY_HANDLER: MyHandler = MyHandler;
static HANDLERS: &[&dyn StaticExtensionHandler] = &[STANDARD_HANDLERS[0], &MY_HANDLER];

let caps: StaticDisplayCapabilities<64> =
    piaf::capabilities_from_edid_static(&parsed, HANDLERS);

Static handlers extract modes only — audio, VSDB, colorimetry, and similar rich metadata require the dynamic pipeline.

Features

| Feature | Default | Description | |---------|---------|----------------------------------------------------------| | std | yes | Enables std-backed types and the full extension system | | alloc | no | Enables dynamic allocation without std | | serde | no | Derives Serialize/Deserialize on public types |

All output types (DisplayCapabilities, VideoMode, ManufacturerId, etc.) are defined in the display-types crate and re-exported from piaf. Importing from piaf::* is sufficient; adding display-types as a direct dependency is only needed if you want to use the types in an API shared between piaf and other crates.

no_std builds

Bare no_std (neither std nor alloc) is supported. The dynamic extension handler pipeline (ExtensionLibrary, capabilities_from_edid) requires alloc or std. The static pipeline (capabilities_from_edid_static) is available unconditionally.

In bare no_std, parse_edid returns a ParsedEdidRef<'_> that borrows extension blocks directly from the input slice — no allocator needed. Both base-block fields and extension-block modes are available through capabilities_from_edid_static at all build tiers.

parse_edid_owned returns a ParsedEdid that copies block bytes into owned storage; in bare no_std the extension block field is absent (alloc-gated), so prefer ParsedEdidRef from parse_edid when extension block access matters.

Fields in DisplayCapabilities available in all build configurations

| Field | Type | |-------------------------------------|-------------------------------------------------------------| | manufacturer | Option<ManufacturerId> | | manufacture_date | Option<ManufactureDate> | | edid_version | Option<EdidVersion> | | product_code | Option<u16> | | serial_number | Option<u32> | | serial_number_string | Option<MonitorString> | | display_name | Option<MonitorString> | | unspecified_text | [Option<MonitorString>; 4] | | white_points | [Option<WhitePoint>; 2] | | digital | bool | | color_bit_depth | Option<ColorBitDepth> | | video_interface | Option<VideoInterface> | | analog_sync_level | Option<AnalogSyncLevel> | | chromaticity | Chromaticity | | gamma | Option<DisplayGamma> | | display_features | `Optio

View on GitHub
GitHub Stars5
CategoryProduct
Updated8h ago
Forks0

Languages

Rust

Security Score

70/100

Audited on Apr 3, 2026

No findings