Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

kiutils-rs is a Rust workspace for lossless KiCad file parsing/editing.

Workspace layers

CrateRole
kiutils_sexprLossless S-expression CST parser/printer
kiutils_kicadTyped KiCad document layer
kiutils-rs (kiutils_rs)Stable public API for applications

Design goals

  • Lossless by default (WriteMode::Lossless)
  • Canonical output when requested (WriteMode::Canonical)
  • Forward-compatible unknown token capture (UnknownNode, UnknownField)
  • Typed mutation helpers for common edit flows

Version scope

  • Primary target: KiCad v10
  • Secondary target: KiCad v9

Install

cargo add kiutils-rs

Quickstart

Parse, edit, write

use kiutils_rs::PcbFile;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut doc = PcbFile::read("input.kicad_pcb")?;

    doc.set_version(20260101)
        .set_generator("kiutils")
        .set_generator_version("dev")
        .set_title("Demo Board")
        .upsert_property("Owner", "Milind")
        .remove_property("Obsolete");

    doc.write("output.kicad_pcb")?;
    Ok(())
}

Build + test

cargo test
cargo test -p kiutils-rs --features serde
cargo test -p kiutils-rs --features parallel

Inspect CLI (typed summary)

cargo run -p kiutils_kicad --bin kiutils-inspect -- \
  crates/kiutils_kicad/tests/fixtures/sample.kicad_pcb \
  --show-unknown --show-diagnostics --show-canonical

Supported Formats

Current v1 file support in public API:

FileType
.kicad_pcbPCB
.kicad_modFootprint
fp-lib-tableFootprint lib table
sym-lib-tableSymbol lib table
.kicad_druDesign rules
.kicad_proProject JSON

Additional parser support exists in implementation crates (for example schematic/symbol/worksheet), while the stable kiutils-rs public surface is intentionally narrow.

Write modes

ModeBehavior
WriteMode::LosslessPreserves unrelated formatting/tokens
WriteMode::CanonicalEmits normalized/canonical representation

Usage Patterns

This chapter is tuned for automation systems and code generators.

Pattern: safe typed mutation

  • Parse with *_File::read(...)
  • Update through document setters (set_*, upsert_*, remove_*)
  • Write with write(...) or write_mode(...)

Why: setter APIs reconcile AST/CST correctly.

Examples:

  • ProjectDocument::set_pinned_symbol_libs(...)
  • ProjectDocument::set_pinned_footprint_libs(...)
  • LibTableDocument::upsert_library_uri(...) (updates existing URI only; adds if missing)

Common pitfalls

PitfallWhat happensCorrect pattern
Mutating with ast_mut() then calling write()Validation error for non-reconciled stateUse setter/upsert/remove helpers
Assuming unknown tokens are droppedFuture syntax might be lost in other librarieskiutils-rs captures unknowns and round-trips them
Forcing canonical output alwaysNoisy diffs in VCSDefault to lossless; canonical only when required

Minimal code path

use kiutils_rs::{PcbFile, WriteMode};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut doc = PcbFile::read("input.kicad_pcb")?;
    doc.upsert_property("EditedBy", "agent");
    doc.write_mode("output.kicad_pcb", WriteMode::Lossless)?;
    Ok(())
}

Examples

Real example: PCB round-trip

Snippet included from source so docs stay synced:

fn main() -> Result<(), String> {
    let mut args = env::args().skip(1);
    let in_path = args.next().map(PathBuf::from).ok_or_else(usage)?;
    let out_path = args.next().map(PathBuf::from).ok_or_else(usage)?;

    let mut doc = PcbFile::read(&in_path).map_err(|e| e.to_string())?;
    doc.set_generator("kiutils")
        .set_generator_version("roundtrip-demo")
        .set_title("Roundtrip Demo")
        .upsert_property("EditedBy", "kiutils_kicad/examples/pcb_roundtrip.rs");

    doc.write(&out_path).map_err(|e| e.to_string())?;

    let reread = PcbFile::read(&out_path).map_err(|e| e.to_string())?;
    println!("input: {}", in_path.display());
    println!("output: {}", out_path.display());
    println!("version: {:?}", reread.ast().version);
    println!("generator: {:?}", reread.ast().generator);
    println!("properties: {}", reread.ast().property_count);
    println!("unknown_nodes: {}", reread.ast().unknown_nodes.len());
    println!("diagnostics: {}", reread.diagnostics().len());

    Ok(())
}

Run it:

cargo run -p kiutils_kicad --example pcb_roundtrip -- input.kicad_pcb output.kicad_pcb

Corpus-style examples

cargo run -p kiutils_kicad --example pcb_corpus_roundtrip -- <input_dir> crates/kiutils_kicad/examples/generated/pcbs
cargo run -p kiutils_kicad --example footprint_corpus_roundtrip -- <input_dir> crates/kiutils_kicad/examples/generated/footprints
cargo run -p kiutils_kicad --example schematic_corpus_roundtrip -- <input_dir> crates/kiutils_kicad/examples/generated/schematics
cargo run -p kiutils_kicad --example symbol_corpus_roundtrip -- <input_dir> crates/kiutils_kicad/examples/generated/symbols
cargo run -p kiutils_kicad --example symlib_corpus_roundtrip -- <input_dir> crates/kiutils_kicad/examples/generated/symlib
cargo run -p kiutils_kicad --example dru_corpus_roundtrip -- <input_dir> crates/kiutils_kicad/examples/generated/dru
cargo run -p kiutils_kicad --example worksheet_corpus_roundtrip -- <input_dir> crates/kiutils_kicad/examples/generated/worksheets

Verification

Evidence in repository:

  • Integration tests cover fixture-based round-trip behavior across supported document kinds.
  • Unknown syntax preservation tested explicitly.
  • kiutils-inspect JSON/text contract smoke tests present.

Test evidence snippet

#![allow(unused)]
fn main() {
fn pcb_fixture_roundtrip_lossless_and_unknown() {
    let src_path = fixture("sample.kicad_pcb");
    let src = fs::read_to_string(&src_path).expect("read fixture");

    let doc = PcbFile::read(&src_path).expect("parse");
    assert_eq!(doc.ast().unknown_nodes.len(), 1);

    let out = tmp_file("pcb", "kicad_pcb");
    doc.write(&out).expect("write");
    let got = fs::read_to_string(&out).expect("read out");
    assert_eq!(got, src);

    let _ = fs::remove_file(out);
}
}

Local gate

cargo fmt --all
cargo test
cargo clippy --all-targets --all-features -- -D warnings
mdbook build docs

API Reference

Primary API docs:

Use this book for workflows and conventions; use docs.rs for item-level API details.