Rust-native KiCad parser/formatter focused on lossless round-trips, typed APIs, and forward compatibility.
kiutils-rs is a Rust workspace with three crates:
kiutils_sexpr(Rust import:kiutils_sexpr): lossless S-expression CST parser/printer.kiutils_kicad(Rust import:kiutils_kicad): typed KiCad document APIs on top of the CST layer (implementation crate).kiutils-rs(Rust import:kiutils_rs): public-facing crate for the stable end-user API.
Default behavior is lossless read/modify/write so unrelated formatting is preserved unless canonical output is explicitly requested.
.kicad_pcb.kicad_modfp-lib-tablesym-lib-table.kicad_dru.kicad_pro
Compatibility target:
- Primary: KiCad v10
- Secondary: KiCad v9
Versioning policy:
- Public API (
kiutils-rs) follows SemVer.
kiutils-rs is still alpha. You may see missing_docs warnings in some crates while the public
surface is being trimmed and stabilized.
Current policy:
- Core user-facing APIs and workflows are documented first.
- Broader internal/public-by-default surfaces may stay partially undocumented temporarily.
- Coverage will tighten as API boundaries settle.
- Lossless output by default (
WriteMode::Lossless). - Optional canonical normalized output (
WriteMode::Canonical). - Unknown token/field capture for forward compatibility.
- Typed mutation helpers for common edits.
- Parser depth guard for deeply nested malformed input.
Book-style guide:
- Build locally:
mdbook build docs - Read online (after GitHub Pages deploy):
https://milind220.github.io/kiutils-rs/
cargo testFeature checks:
cargo test -p kiutils-rs --features serde
cargo test -p kiutils-rs --features paralleluse kiutils_rs::PcbFile;
let mut doc = PcbFile::read("input.kicad_pcb")?;
doc.set_version(20260101)
.set_generator("kiutils")
.set_generator_version("dev")
.set_paper_standard("A4", Some("portrait"))
.set_title("Demo Board")
.upsert_property("Owner", "Milind")
.remove_property("Obsolete");
doc.write("output.kicad_pcb")?;use kiutils_rs::ProjectFile;
let mut doc = ProjectFile::read("input.kicad_pro")?;
doc.set_pinned_symbol_libs(["MySymbols"])
.set_pinned_footprint_libs(["MyFootprints"]);
println!("meta.version = {:?}", doc.ast().meta_version);
doc.write("output.kicad_pro")?;- Use document setter APIs (
set_*,upsert_*,remove_*) for serializable edits. ast_mut()is read-side/debug convenience only. If you mutate viaast_mut(),write()returns a validation error because those changes are not auto-reconciled into CST. Calling setter APIs afterast_mut()does not clear this guard.LibTableDocument::upsert_library_uri(name, uri)updates only the target library URI when the library exists; if missing, it adds a new default KiCad library entry.
Inspect typed parse results quickly:
cargo run -p kiutils_kicad --bin kiutils-inspect -- <path>Common flags:
--type auto|pcb|footprint|schematic|sch|symbol|fplib|symlib|dru|project|worksheet|wks--json--show-cst--show-canonical--show-unknown--show-diagnostics
Example:
cargo run -p kiutils_kicad --bin kiutils-inspect -- \
crates/kiutils_kicad/tests/fixtures/sample.kicad_pcb \
--show-unknown --show-diagnostics --show-canonicalcargo run -p kiutils_kicad --example pcb_roundtrip -- input.kicad_pcb output.kicad_pcb
cargo run -p kiutils_kicad --example footprint_roundtrip -- input.kicad_mod output.kicad_mod
cargo run -p kiutils_kicad --example schematic_roundtrip -- input.kicad_sch output.kicad_sch
cargo run -p kiutils_kicad --example symbol_roundtrip -- input.kicad_sym output.kicad_sym
cargo run -p kiutils_kicad --example symlib_roundtrip -- sym-lib-table sym-lib-table.out
cargo run -p kiutils_kicad --example dru_roundtrip -- input.kicad_dru output.kicad_dru
cargo run -p kiutils_kicad --example worksheet_roundtrip -- input.kicad_wks output.kicad_wksCorpus-style examples (demo trees):
cargo run -p kiutils_kicad --example pcb_corpus_roundtrip -- ~/Engineering/demos crates/kiutils_kicad/examples/generated/pcbs
cargo run -p kiutils_kicad --example footprint_corpus_roundtrip -- ~/Engineering/demos crates/kiutils_kicad/examples/generated/footprints
cargo run -p kiutils_kicad --example schematic_corpus_roundtrip -- ~/Engineering/demos crates/kiutils_kicad/examples/generated/schematics
cargo run -p kiutils_kicad --example symbol_corpus_roundtrip -- ~/Engineering/demos crates/kiutils_kicad/examples/generated/symbols
cargo run -p kiutils_kicad --example symlib_corpus_roundtrip -- ~/Engineering/demos crates/kiutils_kicad/examples/generated/symlib
cargo run -p kiutils_kicad --example dru_corpus_roundtrip -- ~/Engineering/demos crates/kiutils_kicad/examples/generated/dru
cargo run -p kiutils_kicad --example worksheet_corpus_roundtrip -- ~/Engineering/demos crates/kiutils_kicad/examples/generated/worksheets- Format:
cargo fmt --all - Test:
cargo test - Optional lint:
cargo clippy --all-targets --all-features -- -D warnings
Open backlog moved from cleanup notes:
LibTableDocument::diagnostics()currently carries little signal and should be made useful or removed.property_countcan diverge from parsed property-vector length in some cases; normalize semantics.- Module scaffolding is duplicated across formats; reduce drift risk with shared abstractions where beneficial.
kiutils-inspecthas repetitive per-format code paths; refactor while keeping machine-readable schema stable.
- Generator token quoting differs across some document types; align serialization policy.
- Local helper duplication in
lib_tablevs sharedsexpr_utils. VersionPolicyis exported but not currently injectable into reader/diagnostic paths.
generated.members_countmay undercount alternate members syntax forms.- Escape handling for non-quote escapes may diverge from KiCad expectations.
parse_paperheuristic may misclassify malformed mixed forms.
MIT