via is a Rust-native circuit authoring toolkit. It lets you describe a board
as normal Rust data, validate pin maps and nets, and export reviewable KiCad /
LCEDA artifacts.
- Reusable board modules instead of copy-pasted schematic fragments.
- Typed nets, rails, pins, footprints, and electrical classes.
- Validation before export, including pin maps, pad bindings, and connectivity.
- KiCad and LCEDA Pro export paths for review, hand layout, and iteration.
- Rust tests for hardware design assumptions.
Add via-pcb to your Rust project:
[dependencies]
via = { package = "via-pcb", version = "0.1.1" }The crates.io package is via-pcb, while the Rust crate name is still via:
use via::prelude::*;
pub fn board() -> Result<Board> {
let mut d = Design::new("demo")
.rules(Rules::new())
.units(Unit::Mm);
let signal = d.signal("SIGNAL", "3V3");
let v3v3 = d.rail("3V3").dc(3.3);
let ground = d.ground("GND");
let j1 = d.add(
part("J1", "3-pin header")
.footprint(fp::pin_1x03())
.symbol(sym::connector().left(["SIG", "3V3", "GND"]))
.pin(pin("SIG").logic("3V3").pad("1"))
.pin(pin("3V3").power("3V3").pad("2"))
.pin(pin("GND").ground().pad("3")),
)?;
d.connect(&signal, [j1.pin("SIG")]);
d.connect(&v3v3, [j1.pin("3V3")]);
d.connect(&ground, [j1.pin("GND")]);
d.check(CheckProfile::Prototype)?;
d.finish()
}For exporter workflows, install the CLI package. The package name is
via-pcb-cli, and the installed command is via:
cargo install via-pcb-cli
via init my-board
cd my-board
via check
via export kicadUse the Git repository directly:
[dependencies]
via = { package = "via-pcb", git = "https://github.com/jz315/via-rs.git" }For local development, use a path dependency:
[dependencies]
via = { package = "via-pcb", path = "../via-rs/crates/via" }-
via-pcb: user-facing package; its Rust crate name isviaand it providesvia::prelude::*. -
via-core: boards, modules, pins, nets, footprints, rules, and diagnostics. -
via-parts: generic reusable parts such as resistors and capacitors. -
via-footprint: high-level generated footprint builders. -
via-footprint-ir: low-level footprint geometry IR for custom generators. -
via-kicad: KiCad netlist, footprint, schematic, and PCB helpers. -
via-lceda-pro: LCEDA Pro package export. -
via-project:via.tomlproject loading and external design providers. -
via-examples: generic examples for tests and documentation snippets. -
via-pcb-cli(crates/via-cli): project-oriented command-line wrapper for checks, snapshots, BOMs, and export. It installs theviabinary.
use via::prelude::*;
pub fn board() -> Result<Board> {
let mut d = Design::new("modern_api_minimal")
.rules(Rules::new())
.units(Unit::Mm);
let signal = d.signal("SIGNAL", "3V3");
let v3v3 = d.rail("3V3").dc(3.3);
let ground = d.ground("GND");
let input = d.add(
part("J1", "External 3.3V signal input")
.footprint(fp::pin_1x03())
.symbol(sym::connector().left(["SIG", "3V3", "GND"]))
.pin(pin("SIG").logic("3V3").pad("1"))
.pin(pin("3V3").power("3V3").pad("2"))
.pin(pin("GND").ground().pad("3")),
)?;
let load = d.add(
part("U1", "Demo load")
.footprint(fp::pin_1x03())
.symbol(sym::module().left(["IN"]).right(["VCC", "GND"]))
.pin(pin("IN").logic("3V3").pad("1"))
.pin(pin("VCC").power("3V3").pad("2"))
.pin(pin("GND").ground().pad("3")),
)?;
d.connect(&signal, [input.pin("SIG"), load.pin("IN")]);
d.connect(&v3v3, [input.pin("3V3"), load.pin("VCC")]);
d.connect(&ground, [input.pin("GND"), load.pin("GND")]);
d.check(CheckProfile::Prototype)?;
d.finish()
}Part is the electrical object in the schematic model. It owns logical pins,
electrical classes, symbol placement hints, and the footprint reference.
Symbol is only the schematic drawing style. It decides where logical pins are
shown on the generated schematic. It does not create nets or hidden pins.
Footprint is the PCB-side physical model. It names or embeds pads and
geometry. A logical pin may map to a physical pad with .pad("1").
Design is the authoring surface. It creates nets, adds components, connects
pins, runs checks, and produces a checked Board.
Board is the read-only result used by exporters, snapshots, and tests.
Normal users should prefer parts that already carry footprints:
use via::prelude::*;
let r1 = design.add(parts::resistor("R1").value(1.kohm()).fp(fp::r0805()))?;
let c1 = design.add(parts::capacitor("C1").value(100.nf()).voltage(50.v()))?;For custom parts, attach either an embedded generated footprint or an external KiCad footprint name:
let j1 = design.add(
part("J1", "Debug header")
.footprint(fp::pin_1x04())
.symbol(sym::connector().left(["1", "2"]).right(["3", "4"]))
.pins(["1", "2", "3", "4"]),
)?;
let u1 = design.add(
part("U1", "Vendor module")
.footprint("Vendor_Module_From_KiCad_Library")
.pins(["VIN", "GND", "OUT"]),
)?;via-footprint contains only common, generic footprint builders:
- SMD passives:
R_0402,R_0603,R_0805,R_1206, and matching capacitors. - Radial capacitors: generic verify builders for D5.0/P2.0, D6.3/P2.50, D8.0/P3.5, and D10.0/P5.0. Downstream projects can bind specific KiCad official footprints through explicit footprint asset metadata.
- Pin headers and sockets:
Pin_1x*,Pin_2x*,Socket_2x08_R12.7. - Board connectors: terminal blocks, XH, PH.
- Debug and mechanical footprints: test pads, fiducials, mounting holes.
- Small semiconductors: LED, SOD, SOT, SOIC, TSSOP.
Measured dev boards, purchased modules, and product-specific connector drawings belong in downstream part crates.
A downstream project is driven by via.toml, not by compiled-in examples.
The CLI reads a design provider, receives stable BoardIr JSON, then runs
checks or exporters. Replace the placeholder values below in your project.
[project]
name = "<project-name>"
version = "<project-version>"
default-design = "<design-name>"
[designs."<design-name>"]
provider = "cargo"
package = "<provider-package>"
command = "<provider-command>"
[outputs.kicad]
dir = "<kicad-output-dir>"
project = "<kicad-project-name>"
footprint-library-name = "<kicad-footprint-library-name>"
footprint-library-path = "<kicad-footprint-library-path>"
footprint-output-dir = "<generated-footprint-output-dir>"
[kicad-footprints]
version = "10.0.4"
source = "github-release"The provider command prints the board IR:
use via::prelude::*;
fn main() -> Result<()> {
let board = project_design::board()?;
via::project::emit_ir(&board)
}Project commands:
cargo install via-pcb-cli
via init <project-dir>
via ir <design-name> --out <board-ir-json>
via check <design-name>
via check <design-name> --production
via snapshot <design-name> --out <snapshot-json>
via bom <design-name> --format csv --out <bom-csv>
via footprints status
via footprints import --version 10.0.4 --from "<KiCad footprints dir>"
via footprints fetch --url "<cache-bundle-url>" # experimental
via export kicad <design-name>
via export lceda-pro <design-name> --out <lceda-package>
via export pcb <design-name> --layout <layout-json> --out <kicad-pcb> # experimentalgit clone https://github.com/jz315/via-rs.git
cd via-rs
cargo fmt --check
cargo test --workspacevia-pcb is licensed under the Mozilla Public License 2.0.
Official KiCad footprint assets imported or fetched through
via-kicad-footprints remain under
CC-BY-SA-4.0 WITH KiCad-libraries-exception; see that crate's third-party
notices.
