oxiforge 0.1.0

YAML-to-Rust code generator for oxivgl LVGL UIs
Documentation

oxiforge

CI License: GPL-3.0-only

YAML-to-Rust code generator for oxivgl LVGL 9.3 UIs.

Describe your UI in YAML, oxiforge generates safe Rust code that calls oxivgl widget constructors at build time. Designed for no_std embedded targets (ESP32, etc.) but works on any platform oxivgl supports.

Concept

You describe your UI as a YAML file — screens, widgets, styles, layouts. During cargo build, oxiforge reads that YAML and generates Rust source code that creates the UI through oxivgl, a safe Rust binding layer for LVGL 9.3. The generated code is pulled into your crate via include!(), so it compiles as part of your application — no runtime YAML parsing, no interpreter overhead.

Under the hood, LVGL is a C library with its own object ownership: parents own their children, and widgets store raw pointers to styles. oxiforge bridges this gap by leaking style allocations (Box::leak) and forgetting widget wrappers (core::mem::forget) so that LVGL's C object tree stays intact while Rust's drop semantics don't interfere.

YAML format

A UI file has one top-level lvgl: key containing pages, style definitions, gradients, and an optional theme.

lvgl:
  default_font: montserrat_16

  style_definitions:
    - id: card
      bg_color: 0x1E1E1E
      radius: 8
      pad_all: 10

  pages:
    - id: dashboard
      bg_color: 0x121212
      layout: flex
      flex_flow: column
      widgets:
        - label:
            text: "Battery: 13.8V"
            styles: card
            text_color: 0x4CAF50
        - arc:
            id: soc
            width: 140
            height: 140
            min_value: 0.0
            max_value: 100.0
            value: 75.0
            arc_width: 12
            part_indicator:
              arc_color: 0x4CAF50

Supported widgets

obj, label, button, switch, checkbox, slider, arc, bar, dropdown, roller, scale, led, image, line

Layouts

  • Flexlayout: flex, with flex_flow, flex_align_main/cross/track, flex_grow
  • Gridlayout: grid, with grid_columns, grid_rows, grid_cell on children

Styles

  • Inline — set properties directly on any widget
  • Style definitions — reusable named batches, applied via styles: name or styles: [a, b]
  • Theme — default styles per widget type
  • State stylesstate_pressed:, state_checked:, etc.
  • Widget partspart_indicator:, part_knob:, etc.

Full property reference: docs/yaml-language-spec.md

Rust API

generate_from_str

Parse + validate + generate in memory. Returns generated Rust source as String.

let yaml = std::fs::read_to_string("ui.yaml").unwrap();
let code = oxiforge::generate_from_str(&yaml).unwrap();

generate

Parse + validate + generate from file, write .rs to output directory. Emits cargo::rerun-if-changed for build.rs use.

oxiforge::generate(
    std::path::Path::new("ui.yaml"),
    std::path::Path::new(&out_dir),
).unwrap();

Error reporting

Unknown YAML keys produce fuzzy "did you mean?" suggestions:

error: unknown property 'background_color'
  hint: did you mean 'bg_color'?

Project integration

1. Add dependencies

# Cargo.toml
[dependencies]
oxivgl = "0.1"

[build-dependencies]
oxiforge = "0.1"

Working inside a checkout of this repository (e.g. the bundled examples)? Replace each line with { path = "../oxiforge" } / { path = "../oxivgl" } as appropriate.

2. Create build.rs

fn main() {
    let yaml = std::path::Path::new("ui.yaml");
    let out_dir = std::env::var("OUT_DIR").unwrap();
    oxiforge::generate(yaml, std::path::Path::new(&out_dir))
        .expect("oxiforge codegen failed");
}

3. Include generated code

// main.rs
extern crate alloc;  // required for no_std targets

use oxivgl::*;

fn create_ui(screen: &Screen) {
    include!(concat!(env!("OUT_DIR"), "/ui.rs"));
}

Each YAML page becomes a create_<page_id>(screen) call inside the generated file.

Building for ESP32

cargo +esp -Zbuild-std=alloc,core run --release

See examples/arc_gauge/ for a minimal working example, or examples/meter_view/ for a more substantial alternator-regulator dashboard (arc + scale + mode pill + battery/engine/field panels) ported from a real firmware screen.

License

oxiforge is licensed under GPL-3.0-only. This is a deliberate choice and has consequences worth understanding before you adopt it.

oxiforge runs as a build-dependency: it reads your YAML at build time and emits Rust source that your crate include!()s. That generated source is then compiled and linked into your binary. Under the GPL, code derived from a GPL-licensed work is itself bound by the GPL — and the source oxiforge produces qualifies as a derived work of oxiforge. The practical consequence is that a crate which include!()s oxiforge-generated code, and the binary built from it, must also be distributed under a GPL-compatible license.

This is intentional. If you want to ship a closed-source product that uses oxiforge, that license is not available off-the-shelf; please get in touch.

The runtime library oxiforge generates code againstoxivgl — is dual-licensed MIT OR Apache-2.0 and is unaffected by this. You can use oxivgl directly (writing widget code by hand) in a proprietary project without GPL obligations; only the YAML-to-Rust code path through oxiforge is GPL.