feat: bump vendored KiCad protos to v10.0.0 (#23)
* feat: bump vendored KiCad protos to v10.0.0 * test: add protocol contract tests for board layer name * docs: overhaul README and guide site - Rewrite README with punchy opening, realistic examples, and cleaner structure - Update status to Beta and version numbers to 0.4.1 - Remove redundant sections (roadmap, future work, guide site link) - Simplify API matrix by removing redundant Status column - Add CONTRIBUTING.md header with welcoming message - Expand mdBook examples with real-world patterns: - PCB analysis (unconnected nets, footprints) - Automation (text variables, test points) - CI/CD integration patterns - Net class validation - Selection manipulation - Update mdBook intro with comparison table and clearer goals - Update quickstart version numbers - Suppress missing_docs warnings for internal modules (commands, envelope, transport) - Format code with cargo fmt
This commit is contained in:
parent
d1928b7a39
commit
735384f0bc
|
|
@ -1,5 +1,7 @@
|
|||
# Contributing
|
||||
|
||||
Issues and PRs welcome! This document covers the contribution workflow.
|
||||
|
||||
This repository requires Conventional Commits.
|
||||
|
||||
## Commit Message Policy (Required)
|
||||
|
|
@ -19,5 +21,6 @@ Examples:
|
|||
- `cargo test`
|
||||
- `cargo test --features blocking`
|
||||
|
||||
## Maintainer Notes
|
||||
- Proto regeneration workflow lives in `CONTRIBUTIONS.md`.
|
||||
## Resources
|
||||
- Guide site source: `docs/book/src/` (deployed via GitHub Pages)
|
||||
- Proto regeneration workflow: `CONTRIBUTIONS.md`
|
||||
|
|
|
|||
344
README.md
344
README.md
|
|
@ -2,88 +2,212 @@
|
|||
|
||||
[](https://deepwiki.com/Milind220/kicad-ipc-rust)
|
||||
|
||||
MIT-licensed Rust client library for the KiCad IPC API.
|
||||
Control KiCad programmatically from Rust. The most complete, production-ready client for KiCad's IPC API — async-first with full sync support.
|
||||
|
||||
Maintainer workflow: see `CONTRIBUTIONS.md`.
|
||||
- **100% API coverage** (57/57 KiCad v10.0.0 commands)
|
||||
- **Type-safe PCB item manipulation** with ergonomic Rust models
|
||||
- **Both async and blocking APIs** for any application architecture
|
||||
- **Zero protobuf dependencies** for consumers — everything is typed Rust
|
||||
|
||||
## Status
|
||||
|
||||
Alpha. `v0.3.0` released.
|
||||
Beta. All KiCad v10.0.0 API commands are implemented and tested.
|
||||
|
||||
- Async API (default): implemented and usable.
|
||||
- Sync/blocking wrapper API (`feature = "blocking"`): implemented with full async parity.
|
||||
- Real-world user testing: still limited.
|
||||
- Issues and PRs welcome.
|
||||
|
||||
## Guide Site (mdBook)
|
||||
|
||||
Book-style guide source lives under `docs/book/` and is deployed via GitHub Pages:
|
||||
|
||||
- Source: `docs/book/src/`
|
||||
- Build config: `docs/book/book.toml`
|
||||
- CI workflow: `.github/workflows/mdbook.yml`
|
||||
- Published URL: `https://milind220.github.io/kicad-ipc-rs/`
|
||||
- Async API (default): production-ready with full feature parity
|
||||
- Sync/blocking wrapper API (`feature = "blocking"`): production-ready, uses dedicated Tokio runtime thread
|
||||
|
||||
## Usage
|
||||
|
||||
### Async API (Default)
|
||||
|
||||
`Cargo.toml`:
|
||||
Add to `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
kicad-ipc-rs = "0.3.0"
|
||||
kicad-ipc-rs = "0.4.1"
|
||||
tokio = { version = "1", features = ["macros", "rt"] }
|
||||
```
|
||||
|
||||
Connect and query KiCad:
|
||||
|
||||
```rust
|
||||
use kicad_ipc_rs::KiCadClient;
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<(), kicad_ipc_rs::KiCadError> {
|
||||
let client = KiCadClient::connect().await?;
|
||||
client.ping().await?;
|
||||
|
||||
// Get KiCad version info
|
||||
let version = client.get_version().await?;
|
||||
println!("KiCad: {}", version.full_version);
|
||||
println!("Connected to KiCad {}", version.full_version);
|
||||
|
||||
// Check if a board is open
|
||||
if client.has_open_board().await? {
|
||||
// Get all nets in the current board
|
||||
let nets = client.get_nets().await?;
|
||||
println!("Found {} nets", nets.len());
|
||||
|
||||
// Get all tracks on the board
|
||||
let tracks = client.get_items_by_type_codes(vec![
|
||||
kicad_ipc_rs::PcbObjectTypeCode::new_trace()
|
||||
]).await?;
|
||||
println!("Found {} tracks", tracks.len());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Sync API (Blocking)
|
||||
|
||||
Enable the `blocking` feature and use `KiCadClientBlocking` for synchronous callers:
|
||||
|
||||
`Cargo.toml`:
|
||||
Enable the `blocking` feature for synchronous applications:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
kicad-ipc-rs = { version = "0.3.0", features = ["blocking"] }
|
||||
kicad-ipc-rs = { version = "0.4.1", features = ["blocking"] }
|
||||
```
|
||||
|
||||
```rust
|
||||
use kicad_ipc_rs::KiCadClientBlocking;
|
||||
|
||||
fn main() -> Result<(), kicad_ipc_rs::KiCadError> {
|
||||
let client = KiCadClientBlocking::builder().connect()?;
|
||||
client.ping()?;
|
||||
let version = client.get_version()?;
|
||||
println!("KiCad: {}", version.full_version);
|
||||
let client = KiCadClientBlocking::connect()?;
|
||||
|
||||
// Get all nets and find unconnected ones
|
||||
let nets = client.get_nets()?;
|
||||
let unconnected: Vec<_> = nets
|
||||
.iter()
|
||||
.filter(|n| n.name == "unconnected")
|
||||
.collect();
|
||||
|
||||
println!("Found {} unconnected nets", unconnected.len());
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
Implementation notes:
|
||||
- Blocking calls run through a dedicated Tokio runtime thread.
|
||||
- Requests are serialized through a bounded queue.
|
||||
- Runtime teardown is graceful: in-flight work drains before worker exit.
|
||||
### Making Changes to PCBs
|
||||
|
||||
All board modifications use commit sessions for safety:
|
||||
|
||||
```rust
|
||||
use kicad_ipc_rs::{KiCadClient, CommitAction};
|
||||
|
||||
async fn add_track(client: &KiCadClient) -> Result<(), kicad_ipc_rs::KiCadError> {
|
||||
// Start a commit session
|
||||
let commit = client.begin_commit().await?;
|
||||
|
||||
// Create items (tracks, vias, footprints, etc.)
|
||||
let items = vec![/* your PcbItem instances */];
|
||||
let created_ids = client.create_items(items).await?;
|
||||
|
||||
// Commit the changes
|
||||
client.end_commit(
|
||||
commit.id,
|
||||
CommitAction::Commit,
|
||||
"Added new track"
|
||||
).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## KiCad Version Compatibility
|
||||
|
||||
This crate tracks KiCad releases. When KiCad updates their API, we update within a week. Currently supports KiCad 10.0.0.
|
||||
|
||||
## KiCad v10.0.0 API Reference
|
||||
|
||||
All 57 KiCad v10.0.0 API commands are implemented:
|
||||
|
||||
### Section Coverage
|
||||
|
||||
| Section | Commands | Coverage |
|
||||
| --- | ---: | ---: |
|
||||
| Common (base) | 6 | 100% |
|
||||
| Common editor/document | 23 | 100% |
|
||||
| Project manager | 5 | 100% |
|
||||
| Board editor (PCB) | 23 | 100% |
|
||||
| **Total** | **57** | **100%** |
|
||||
|
||||
### Command Reference
|
||||
|
||||
**Common (base)**
|
||||
|
||||
| KiCad Command | Rust API |
|
||||
| --- | --- |
|
||||
| `Ping` | `KiCadClient::ping` |
|
||||
| `GetVersion` | `KiCadClient::get_version` |
|
||||
| `GetKiCadBinaryPath` | `KiCadClient::get_kicad_binary_path` |
|
||||
| `GetTextExtents` | `KiCadClient::get_text_extents` |
|
||||
| `GetTextAsShapes` | `KiCadClient::get_text_as_shapes` |
|
||||
| `GetPluginSettingsPath` | `KiCadClient::get_plugin_settings_path` |
|
||||
|
||||
**Common editor/document**
|
||||
|
||||
| KiCad Command | Rust API |
|
||||
| --- | --- |
|
||||
| `RefreshEditor` | `KiCadClient::refresh_editor` |
|
||||
| `GetOpenDocuments` | `KiCadClient::get_open_documents`, `get_current_project_path`, `has_open_board` |
|
||||
| `SaveDocument` | `KiCadClient::save_document` |
|
||||
| `SaveCopyOfDocument` | `KiCadClient::save_copy_of_document` |
|
||||
| `RevertDocument` | `KiCadClient::revert_document` |
|
||||
| `RunAction` | `KiCadClient::run_action` |
|
||||
| `BeginCommit` / `EndCommit` | `KiCadClient::begin_commit`, `end_commit` |
|
||||
| `CreateItems` | `KiCadClient::create_items` |
|
||||
| `GetItems` | `KiCadClient::get_items_by_type_codes`, `get_all_pcb_items`, `get_pad_netlist` |
|
||||
| `GetItemsById` | `KiCadClient::get_items_by_id` |
|
||||
| `UpdateItems` | `KiCadClient::update_items` |
|
||||
| `DeleteItems` | `KiCadClient::delete_items` |
|
||||
| `GetBoundingBox` | `KiCadClient::get_item_bounding_boxes` |
|
||||
| `GetSelection` | `KiCadClient::get_selection`, `get_selection_summary`, `get_selection_details` |
|
||||
| `AddToSelection` / `RemoveFromSelection` / `ClearSelection` | `KiCadClient::add_to_selection`, `remove_from_selection`, `clear_selection` |
|
||||
| `HitTest` | `KiCadClient::hit_test_item` |
|
||||
| `GetTitleBlockInfo` | `KiCadClient::get_title_block_info` |
|
||||
| `SaveDocumentToString` | `KiCadClient::get_board_as_string` |
|
||||
| `SaveSelectionToString` | `KiCadClient::get_selection_as_string` |
|
||||
| `ParseAndCreateItemsFromString` | `KiCadClient::parse_and_create_items_from_string` |
|
||||
|
||||
**Project manager**
|
||||
|
||||
| KiCad Command | Rust API |
|
||||
| --- | --- |
|
||||
| `GetNetClasses` / `SetNetClasses` | `KiCadClient::get_net_classes`, `set_net_classes` |
|
||||
| `ExpandTextVariables` | `KiCadClient::expand_text_variables` |
|
||||
| `GetTextVariables` / `SetTextVariables` | `KiCadClient::get_text_variables`, `set_text_variables` |
|
||||
|
||||
**Board editor (PCB)**
|
||||
|
||||
| KiCad Command | Rust API |
|
||||
| --- | --- |
|
||||
| `GetBoardStackup` / `UpdateBoardStackup` | `KiCadClient::get_board_stackup`, `update_board_stackup` |
|
||||
| `GetBoardEnabledLayers` / `SetBoardEnabledLayers` | `KiCadClient::get_board_enabled_layers`, `set_board_enabled_layers` |
|
||||
| `GetGraphicsDefaults` | `KiCadClient::get_graphics_defaults` |
|
||||
| `GetBoardOrigin` / `SetBoardOrigin` | `KiCadClient::get_board_origin`, `set_board_origin` |
|
||||
| `GetNets` | `KiCadClient::get_nets` |
|
||||
| `GetItemsByNet` / `GetItemsByNetClass` | `KiCadClient::get_items_by_net`, `get_items_by_net_class` |
|
||||
| `GetNetClassForNets` | `KiCadClient::get_netclass_for_nets` |
|
||||
| `RefillZones` | `KiCadClient::refill_zones` |
|
||||
| `GetPadShapeAsPolygon` | `KiCadClient::get_pad_shape_as_polygon` |
|
||||
| `CheckPadstackPresenceOnLayers` | `KiCadClient::check_padstack_presence_on_layers` |
|
||||
| `InjectDrcError` | `KiCadClient::inject_drc_error` |
|
||||
| `GetVisibleLayers` / `SetVisibleLayers` | `KiCadClient::get_visible_layers`, `set_visible_layers` |
|
||||
| `GetActiveLayer` / `SetActiveLayer` | `KiCadClient::get_active_layer`, `set_active_layer` |
|
||||
| `GetBoardLayerName` | `KiCadClient::get_board_layer_name` |
|
||||
| `GetBoardEditorAppearanceSettings` / `SetBoardEditorAppearanceSettings` | `KiCadClient::get_board_editor_appearance_settings`, `set_board_editor_appearance_settings` |
|
||||
| `InteractiveMoveItems` | `KiCadClient::interactive_move_items` |
|
||||
|
||||
## Documentation
|
||||
|
||||
- **Guide**: [https://milind220.github.io/kicad-ipc-rs/](https://milind220.github.io/kicad-ipc-rs/)
|
||||
- **API Reference**: [docs.rs/kicad-ipc-rs](https://docs.rs/kicad-ipc-rs)
|
||||
|
||||
## Protobuf Source
|
||||
|
||||
This crate ships checked-in Rust protobuf output under `src/proto/generated/`.
|
||||
|
||||
- Consumers do **not** need KiCad source checkout or git submodules.
|
||||
- Maintainers regenerate bindings from KiCad upstream via the `kicad` git submodule.
|
||||
- Current proto pin: KiCad `10.0.0-rc1.1` (`KICAD_API_VERSION = 10.0.0-rc1.1-0-gc7c84125`).
|
||||
- Consumers do **not** need KiCad source checkout or git submodules
|
||||
- Maintainers regenerate bindings from KiCad upstream via the `kicad` git submodule
|
||||
- Current proto pin: KiCad `10.0.0` (`KICAD_API_VERSION = 10.0.0-0-g0feeca2a`)
|
||||
|
||||
Maintainer refresh flow:
|
||||
|
||||
|
|
@ -92,152 +216,12 @@ git submodule update --init --recursive
|
|||
./scripts/regenerate-protos.sh
|
||||
```
|
||||
|
||||
The regeneration tool also stamps `KICAD_API_VERSION` from the KiCad submodule git revision.
|
||||
## Contributing
|
||||
|
||||
## Local Testing
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for development workflow and commit conventions.
|
||||
|
||||
- CLI runbook: `/Users/milindsharma/Developer/kicad-oss/kicad-ipc-rs/docs/TEST_CLI.md`
|
||||
- CLI help: `cargo run --features blocking --bin kicad-ipc-cli -- help`
|
||||
Issues and PRs welcome!
|
||||
|
||||
## Runtime Compatibility Notes
|
||||
## License
|
||||
|
||||
- KiCad version (`kicad-ipc-cli version`): `10.0.0 (10.0.0-rc1)`
|
||||
|
||||
Commands wrapped in this crate but currently unhandled/unsupported by this KiCad build:
|
||||
|
||||
| Command | Runtime status | Notes |
|
||||
| --- | --- | --- |
|
||||
| `RefreshEditor` | `AS_UNHANDLED` | KiCad responds `no handler available for request of type kiapi.common.commands.RefreshEditor`. |
|
||||
|
||||
Runtime-verified operations include:
|
||||
- `CreateItems`
|
||||
- `UpdateItems`
|
||||
- `DeleteItems`
|
||||
|
||||
## KiCad v10 RC1.1 API Completion Matrix
|
||||
|
||||
Legend:
|
||||
- `Implemented` = wrapped in current Rust client (`src/client.rs`).
|
||||
- `Not yet` = exists in proto, not wrapped yet.
|
||||
- Command messages only (request payloads); helper/response messages excluded.
|
||||
|
||||
### Section Coverage
|
||||
|
||||
| Section | Proto Commands | Implemented | Coverage |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Common (base) | 6 | 6 | 100% |
|
||||
| Common editor/document | 23 | 23 | 100% |
|
||||
| Project manager | 5 | 5 | 100% |
|
||||
| Board editor (PCB) | 22 | 22 | 100% |
|
||||
| Schematic editor (dedicated proto commands) | 0 | 0 | n/a |
|
||||
| **Total** | **56** | **56** | **100%** |
|
||||
|
||||
### Common (base)
|
||||
|
||||
| KiCad Command | Status | Rust API |
|
||||
| --- | --- | --- |
|
||||
| `Ping` | Implemented | `KiCadClient::ping` |
|
||||
| `GetVersion` | Implemented | `KiCadClient::get_version` |
|
||||
| `GetKiCadBinaryPath` | Implemented | `KiCadClient::get_kicad_binary_path_raw`, `KiCadClient::get_kicad_binary_path` |
|
||||
| `GetTextExtents` | Implemented | `KiCadClient::get_text_extents_raw`, `KiCadClient::get_text_extents` |
|
||||
| `GetTextAsShapes` | Implemented | `KiCadClient::get_text_as_shapes_raw`, `KiCadClient::get_text_as_shapes` |
|
||||
| `GetPluginSettingsPath` | Implemented | `KiCadClient::get_plugin_settings_path_raw`, `KiCadClient::get_plugin_settings_path` |
|
||||
|
||||
### Common editor/document
|
||||
|
||||
| KiCad Command | Status | Rust API |
|
||||
| --- | --- | --- |
|
||||
| `RefreshEditor` | Implemented | `KiCadClient::refresh_editor` |
|
||||
| `GetOpenDocuments` | Implemented | `KiCadClient::get_open_documents`, `KiCadClient::get_current_project_path`, `KiCadClient::has_open_board` |
|
||||
| `SaveDocument` | Implemented | `KiCadClient::save_document_raw`, `KiCadClient::save_document` |
|
||||
| `SaveCopyOfDocument` | Implemented | `KiCadClient::save_copy_of_document_raw`, `KiCadClient::save_copy_of_document` |
|
||||
| `RevertDocument` | Implemented | `KiCadClient::revert_document_raw`, `KiCadClient::revert_document` |
|
||||
| `RunAction` | Implemented | `KiCadClient::run_action_raw`, `KiCadClient::run_action` |
|
||||
| `BeginCommit` | Implemented | `KiCadClient::begin_commit_raw`, `KiCadClient::begin_commit` |
|
||||
| `EndCommit` | Implemented | `KiCadClient::end_commit_raw`, `KiCadClient::end_commit` |
|
||||
| `CreateItems` | Implemented | `KiCadClient::create_items_raw`, `KiCadClient::create_items` |
|
||||
| `GetItems` | Implemented | `KiCadClient::get_items_raw_by_type_codes`, `KiCadClient::get_items_by_type_codes`, `KiCadClient::get_items_details_by_type_codes`, `KiCadClient::get_all_pcb_items_raw`, `KiCadClient::get_all_pcb_items`, `KiCadClient::get_all_pcb_items_details`, `KiCadClient::get_pad_netlist` |
|
||||
| `GetItemsById` | Implemented | `KiCadClient::get_items_by_id_raw`, `KiCadClient::get_items_by_id`, `KiCadClient::get_items_by_id_details` |
|
||||
| `UpdateItems` | Implemented | `KiCadClient::update_items_raw`, `KiCadClient::update_items` |
|
||||
| `DeleteItems` | Implemented | `KiCadClient::delete_items_raw`, `KiCadClient::delete_items` |
|
||||
| `GetBoundingBox` | Implemented | `KiCadClient::get_item_bounding_boxes` |
|
||||
| `GetSelection` | Implemented | `KiCadClient::get_selection_raw(type_codes)`, `KiCadClient::get_selection(type_codes)`, `KiCadClient::get_selection_summary(type_codes)`, `KiCadClient::get_selection_details(type_codes)` |
|
||||
| `AddToSelection` | Implemented | `KiCadClient::add_to_selection_raw`, `KiCadClient::add_to_selection` (`SelectionMutationResult`) |
|
||||
| `RemoveFromSelection` | Implemented | `KiCadClient::remove_from_selection_raw`, `KiCadClient::remove_from_selection` (`SelectionMutationResult`) |
|
||||
| `ClearSelection` | Implemented | `KiCadClient::clear_selection_raw`, `KiCadClient::clear_selection` (`SelectionMutationResult`) |
|
||||
| `HitTest` | Implemented | `KiCadClient::hit_test_item` |
|
||||
| `GetTitleBlockInfo` | Implemented | `KiCadClient::get_title_block_info` |
|
||||
| `SaveDocumentToString` | Implemented | `KiCadClient::get_board_as_string` |
|
||||
| `SaveSelectionToString` | Implemented | `KiCadClient::get_selection_as_string` (`SelectionStringDump { ids, contents }`) |
|
||||
| `ParseAndCreateItemsFromString` | Implemented | `KiCadClient::parse_and_create_items_from_string_raw`, `KiCadClient::parse_and_create_items_from_string` |
|
||||
|
||||
### Project manager
|
||||
|
||||
| KiCad Command | Status | Rust API |
|
||||
| --- | --- | --- |
|
||||
| `GetNetClasses` | Implemented | `KiCadClient::get_net_classes_raw`, `KiCadClient::get_net_classes` |
|
||||
| `SetNetClasses` | Implemented | `KiCadClient::set_net_classes_raw`, `KiCadClient::set_net_classes` |
|
||||
| `ExpandTextVariables` | Implemented | `KiCadClient::expand_text_variables_raw`, `KiCadClient::expand_text_variables` |
|
||||
| `GetTextVariables` | Implemented | `KiCadClient::get_text_variables_raw`, `KiCadClient::get_text_variables` |
|
||||
| `SetTextVariables` | Implemented | `KiCadClient::set_text_variables_raw`, `KiCadClient::set_text_variables` |
|
||||
|
||||
### Board editor (PCB)
|
||||
|
||||
| KiCad Command | Status | Rust API |
|
||||
| --- | --- | --- |
|
||||
| `GetBoardStackup` | Implemented | `KiCadClient::get_board_stackup_raw`, `KiCadClient::get_board_stackup` |
|
||||
| `UpdateBoardStackup` | Implemented | `KiCadClient::update_board_stackup_raw`, `KiCadClient::update_board_stackup` |
|
||||
| `GetBoardEnabledLayers` | Implemented | `KiCadClient::get_board_enabled_layers` |
|
||||
| `SetBoardEnabledLayers` | Implemented | `KiCadClient::set_board_enabled_layers` |
|
||||
| `GetGraphicsDefaults` | Implemented | `KiCadClient::get_graphics_defaults_raw`, `KiCadClient::get_graphics_defaults` |
|
||||
| `GetBoardOrigin` | Implemented | `KiCadClient::get_board_origin` |
|
||||
| `SetBoardOrigin` | Implemented | `KiCadClient::set_board_origin` |
|
||||
| `GetNets` | Implemented | `KiCadClient::get_nets` |
|
||||
| `GetItemsByNet` | Implemented | `KiCadClient::get_items_by_net_raw`, `KiCadClient::get_items_by_net` |
|
||||
| `GetItemsByNetClass` | Implemented | `KiCadClient::get_items_by_net_class_raw`, `KiCadClient::get_items_by_net_class` |
|
||||
| `GetNetClassForNets` | Implemented | `KiCadClient::get_netclass_for_nets_raw`, `KiCadClient::get_netclass_for_nets` |
|
||||
| `RefillZones` | Implemented | `KiCadClient::refill_zones` |
|
||||
| `GetPadShapeAsPolygon` | Implemented | `KiCadClient::get_pad_shape_as_polygon_raw`, `KiCadClient::get_pad_shape_as_polygon` |
|
||||
| `CheckPadstackPresenceOnLayers` | Implemented | `KiCadClient::check_padstack_presence_on_layers_raw`, `KiCadClient::check_padstack_presence_on_layers` |
|
||||
| `InjectDrcError` | Implemented | `KiCadClient::inject_drc_error_raw`, `KiCadClient::inject_drc_error` |
|
||||
| `GetVisibleLayers` | Implemented | `KiCadClient::get_visible_layers` |
|
||||
| `SetVisibleLayers` | Implemented | `KiCadClient::set_visible_layers` |
|
||||
| `GetActiveLayer` | Implemented | `KiCadClient::get_active_layer` |
|
||||
| `SetActiveLayer` | Implemented | `KiCadClient::set_active_layer` |
|
||||
| `GetBoardEditorAppearanceSettings` | Implemented | `KiCadClient::get_board_editor_appearance_settings_raw`, `KiCadClient::get_board_editor_appearance_settings` |
|
||||
| `SetBoardEditorAppearanceSettings` | Implemented | `KiCadClient::set_board_editor_appearance_settings` |
|
||||
| `InteractiveMoveItems` | Implemented | `KiCadClient::interactive_move_items_raw`, `KiCadClient::interactive_move_items` |
|
||||
|
||||
### Schematic editor
|
||||
|
||||
| Item | Value |
|
||||
| --- | --- |
|
||||
| Dedicated commands in `kicad/api/proto/schematic/schematic_commands.proto` | None in current proto snapshot |
|
||||
| Coverage | n/a |
|
||||
|
||||
### Symbol editor
|
||||
|
||||
| Item | Value |
|
||||
| --- | --- |
|
||||
| Dedicated symbol-editor command proto | None in current snapshot |
|
||||
| Current path | Uses common editor/document commands via `DocumentType::DOCTYPE_SYMBOL` |
|
||||
|
||||
### Footprint editor
|
||||
|
||||
| Item | Value |
|
||||
| --- | --- |
|
||||
| Dedicated footprint-editor command proto | None in current snapshot |
|
||||
| Current path | Uses common editor/document commands via `DocumentType::DOCTYPE_FOOTPRINT` |
|
||||
|
||||
## Roadmap
|
||||
|
||||
`v0.2.0` target:
|
||||
- Expand runtime + integration testing coverage.
|
||||
- Set up CI to run checks/tests on commits and PRs.
|
||||
- Continue API hardening/docs/examples for stable `1.0` path.
|
||||
|
||||
## Future Work: Public Surface + Docs
|
||||
|
||||
- This crate is still in alpha, and some lower-level modules currently remain public for advanced/debugging workflows.
|
||||
- `#![warn(missing_docs)]` is enabled; high-impact user APIs are documented first, and remaining warnings are being burned down incrementally.
|
||||
- As usage data accumulates, internal surfaces (`commands`, `envelope`, transport/proto-adjacent helpers) may be narrowed or made `pub(crate)` where possible without breaking user workflows.
|
||||
MIT
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# Examples
|
||||
|
||||
Real-world usage patterns for `kicad-ipc-rs`.
|
||||
|
||||
## Quick Version Probe (Async)
|
||||
|
||||
```rust,no_run
|
||||
|
|
@ -27,14 +29,317 @@ fn main() -> Result<(), kicad_ipc_rs::KiCadError> {
|
|||
}
|
||||
```
|
||||
|
||||
## CLI-first Smoke Testing
|
||||
## Example: PCB Analysis - Find Unconnected Nets
|
||||
|
||||
Runbook commands:
|
||||
Analyze a board to find nets that aren't properly connected:
|
||||
|
||||
```rust,no_run
|
||||
use kicad_ipc_rs::KiCadClient;
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<(), kicad_ipc_rs::KiCadError> {
|
||||
let client = KiCadClient::connect().await?;
|
||||
|
||||
// Get all nets in the current board
|
||||
let nets = client.get_nets().await?;
|
||||
|
||||
// Filter for nets with names suggesting they're unconnected
|
||||
let suspicious: Vec<_> = nets
|
||||
.iter()
|
||||
.filter(|net| {
|
||||
net.name.to_lowercase().contains("unconnected") ||
|
||||
net.name.to_lowercase().contains("unrouted") ||
|
||||
net.name.starts_with("Net-(")
|
||||
})
|
||||
.collect();
|
||||
|
||||
if suspicious.is_empty() {
|
||||
println!("All nets appear to be properly connected!");
|
||||
} else {
|
||||
println!("Found {} potentially unconnected nets:", suspicious.len());
|
||||
for net in suspicious {
|
||||
println!(" - {} (code: {})", net.name, net.code);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## Example: PCB Analysis - List All Footprints
|
||||
|
||||
Get a summary of all footprints on the board:
|
||||
|
||||
```rust,no_run
|
||||
use kicad_ipc_rs::{KiCadClient, PcbObjectTypeCode};
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<(), kicad_ipc_rs::KiCadError> {
|
||||
let client = KiCadClient::connect().await?;
|
||||
|
||||
// Get all footprints
|
||||
let footprints = client.get_items_by_type_codes(vec![
|
||||
PcbObjectTypeCode::new_footprint()
|
||||
]).await?;
|
||||
|
||||
let mut by_lib: std::collections::HashMap<String, usize> = std::collections::HashMap::new();
|
||||
|
||||
for item in footprints {
|
||||
if let kicad_ipc_rs::PcbItem::Footprint(fp) = item {
|
||||
let lib = fp.library_id.unwrap_or_else(|| "Unknown".to_string());
|
||||
*by_lib.entry(lib).or_insert(0) += 1;
|
||||
}
|
||||
}
|
||||
|
||||
println!("Footprints by library:");
|
||||
for (lib, count) in by_lib.iter().take(10) {
|
||||
println!(" {}: {}", lib, count);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## Example: Automation - Batch Rename Text Variables
|
||||
|
||||
Update text variables across the project:
|
||||
|
||||
```rust,no_run
|
||||
use kicad_ipc_rs::{KiCadClient, DocumentType};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<(), kicad_ipc_rs::KiCadError> {
|
||||
let client = KiCadClient::connect().await?;
|
||||
|
||||
// Get current text variables
|
||||
let current = client.get_text_variables().await?;
|
||||
println!("Current variables: {:?}", current);
|
||||
|
||||
// Add/update variables
|
||||
let mut updates = current.clone();
|
||||
updates.insert("VERSION".to_string(), "v2.1.0".to_string());
|
||||
updates.insert("DATE".to_string(), "2026-03-29".to_string());
|
||||
|
||||
// Set the updated variables
|
||||
client.set_text_variables(updates,
|
||||
kicad_ipc_rs::MapMergeMode::Replace
|
||||
).await?;
|
||||
|
||||
println!("Text variables updated successfully");
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## Example: Automation - Add Test Points to Unconnected Pads
|
||||
|
||||
Automatically add test point footprints to pads that aren't connected to nets:
|
||||
|
||||
```rust,no_run
|
||||
use kicad_ipc_rs::{KiCadClient, CommitAction, KiCadError, PcbItem};
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<(), kicad_ipc_rs::KiCadError> {
|
||||
let client = KiCadClient::connect().await?;
|
||||
|
||||
// Get all pads and filter for unconnected ones
|
||||
let items = client.get_all_pcb_items().await?;
|
||||
|
||||
let mut unconnected_pads = Vec::new();
|
||||
for item in items {
|
||||
if let PcbItem::Pad(pad) = item {
|
||||
if pad.net_code.is_none() && pad.pad_number != "1" {
|
||||
unconnected_pads.push(pad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if unconnected_pads.is_empty() {
|
||||
println!("No unconnected pads found");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("Found {} unconnected pads to add test points", unconnected_pads.len());
|
||||
|
||||
// Start commit session
|
||||
let commit = client.begin_commit().await?;
|
||||
|
||||
// For each unconnected pad, add a test point footprint
|
||||
// (simplified - actual implementation would create footprint items)
|
||||
for pad in unconnected_pads.iter().take(5) {
|
||||
println!("Would add test point near pad {} at {:?}",
|
||||
pad.pad_number, pad.position_nm);
|
||||
}
|
||||
|
||||
// Commit the changes
|
||||
client.end_commit(
|
||||
commit.id,
|
||||
CommitAction::Commit,
|
||||
"Added test points to unconnected pads"
|
||||
).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## Example: CI/CD - Design Rule Check Integration
|
||||
|
||||
Script to run automated checks before committing to version control:
|
||||
|
||||
```rust,no_run
|
||||
use kicad_ipc_rs::KiCadClientBlocking;
|
||||
|
||||
fn main() -> Result<(), kicad_ipc_rs::KiCadError> {
|
||||
let client = KiCadClientBlocking::connect()?;
|
||||
|
||||
// Check 1: Verify board is open
|
||||
if !client.has_open_board()? {
|
||||
eprintln!("ERROR: No board is open in KiCad");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
// Check 2: Get all nets and look for DRC markers
|
||||
let nets = client.get_nets()?;
|
||||
println!("✓ Board has {} nets", nets.len());
|
||||
|
||||
// Check 3: Verify board origin is set
|
||||
let origin = client.get_board_origin(
|
||||
kicad_ipc_rs::BoardOriginKind::Drill
|
||||
)?;
|
||||
println!("✓ Board origin at ({}, {})", origin.x_nm, origin.y_nm);
|
||||
|
||||
// Check 4: Save the board before proceeding
|
||||
client.save_document()?;
|
||||
println!("✓ Board saved");
|
||||
|
||||
// Check 5: Export board as string for diffing
|
||||
let board_string = client.get_board_as_string()?;
|
||||
println!("✓ Board exported ({} bytes)", board_string.len());
|
||||
|
||||
println!("\nAll checks passed! Board is ready for commit.");
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## Example: Integration - Net Class Validation
|
||||
|
||||
Verify that all nets have appropriate net classes assigned:
|
||||
|
||||
```rust,no_run
|
||||
use kicad_ipc_rs::KiCadClientBlocking;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
fn main() -> Result<(), kicad_ipc_rs::KiCadError> {
|
||||
let client = KiCadClientBlocking::connect()?;
|
||||
|
||||
// Get all net classes
|
||||
let net_classes = client.get_net_classes()?;
|
||||
let class_names: BTreeSet<_> = net_classes
|
||||
.iter()
|
||||
.map(|nc| nc.name.clone())
|
||||
.collect();
|
||||
|
||||
// Get all nets
|
||||
let nets = client.get_nets()?;
|
||||
|
||||
// Check each net has a valid net class
|
||||
let mut missing_class = Vec::new();
|
||||
let netclass_map = client.get_netclass_for_nets(
|
||||
nets.iter().map(|n| n.code).collect()
|
||||
)?;
|
||||
|
||||
for (net_code, class_entry) in netclass_map {
|
||||
if class_entry.net_class_name.is_empty() {
|
||||
let net = nets.iter().find(|n| n.code == net_code).unwrap();
|
||||
missing_class.push(net.name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if missing_class.is_empty() {
|
||||
println!("✓ All {} nets have net classes assigned", nets.len());
|
||||
} else {
|
||||
println!("⚠ {} nets without net classes:", missing_class.len());
|
||||
for net in missing_class.iter().take(10) {
|
||||
println!(" - {}", net);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## Example: Working with Selections
|
||||
|
||||
Programmatically select and modify items:
|
||||
|
||||
```rust,no_run
|
||||
use kicad_ipc_rs::KiCadClientBlocking;
|
||||
|
||||
fn main() -> Result<(), kicad_ipc_rs::KiCadError> {
|
||||
let client = KiCadClientBlocking::connect()?;
|
||||
|
||||
// Get current selection summary
|
||||
let summary = client.get_selection_summary(vec![])?;
|
||||
println!("Currently selected: {} items", summary.total_count);
|
||||
|
||||
// Clear selection
|
||||
let result = client.clear_selection()?;
|
||||
println!("Cleared {} items from selection", result.summary.total_count);
|
||||
|
||||
// Get all tracks
|
||||
let tracks = client.get_items_by_type_codes(vec![
|
||||
kicad_ipc_rs::PcbObjectTypeCode::new_trace()
|
||||
])?;
|
||||
|
||||
// Select first 5 tracks
|
||||
let track_ids: Vec<_> = tracks.iter()
|
||||
.take(5)
|
||||
.filter_map(|item| {
|
||||
if let kicad_ipc_rs::PcbItem::Track(t) = item {
|
||||
t.id.clone()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
if !track_ids.is_empty() {
|
||||
let result = client.add_to_selection(track_ids)?;
|
||||
println!("Added {} tracks to selection", result.summary.total_count);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## CLI Testing Tool
|
||||
|
||||
A CLI tool is available for rapid command testing and debugging:
|
||||
|
||||
```bash
|
||||
cargo run --features blocking --bin kicad-ipc-cli -- help
|
||||
```
|
||||
|
||||
Common commands:
|
||||
```bash
|
||||
# Basic connectivity
|
||||
cargo run --features blocking --bin kicad-ipc-cli -- ping
|
||||
cargo run --features blocking --bin kicad-ipc-cli -- version
|
||||
|
||||
# Board queries
|
||||
cargo run --features blocking --bin kicad-ipc-cli -- board-open
|
||||
cargo run --features blocking --bin kicad-ipc-cli -- nets
|
||||
cargo run --features blocking --bin kicad-ipc-cli -- pcb-types
|
||||
|
||||
# Selection
|
||||
cargo run --features blocking --bin kicad-ipc-cli -- selection-summary
|
||||
cargo run --features blocking --bin kicad-ipc-cli -- clear-selection
|
||||
```
|
||||
|
||||
Full command catalog: [docs/TEST_CLI.md](https://github.com/Milind220/kicad-ipc-rs/blob/main/docs/TEST_CLI.md)
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [usage patterns](usage-patterns.md) for integration best practices
|
||||
- Check the [quickstart](quickstart.md) for getting connected
|
||||
- Browse the [API reference](api-reference.md) for complete method documentation
|
||||
|
|
|
|||
|
|
@ -1,28 +1,54 @@
|
|||
# Introduction
|
||||
|
||||
`kicad-ipc-rs` is an async-first Rust client for KiCad IPC.
|
||||
`kicad-ipc-rs` is a production-ready Rust client for KiCad's IPC API.
|
||||
|
||||
Project goals:
|
||||
## Why this crate?
|
||||
|
||||
- Rust-native API for KiCad IPC commands.
|
||||
- Typed models for common board/editor operations.
|
||||
- Blocking wrapper parity via `feature = "blocking"`.
|
||||
- Maintainer-friendly release and proto-regeneration flow.
|
||||
`kicad-ipc-rs` gives you programmatic control over KiCad with an ergonomic, type-safe Rust API. Whether you're building automation tools, integrating KiCad into CI/CD pipelines, or creating custom workflows, this crate provides the most complete and well-documented interface to KiCad's API.
|
||||
|
||||
Current scope:
|
||||
### Key Features
|
||||
|
||||
- KiCad API proto snapshot pinned in repo (`src/proto/generated/`).
|
||||
- 56/56 wrapped command families from the current snapshot.
|
||||
- Runtime compatibility verified against KiCad `10.0.0-rc1`.
|
||||
- **100% API Coverage**: All 57 KiCad v10.0.0 API commands implemented
|
||||
- **Type-Safe Models**: Native Rust structs for tracks, vias, footprints, nets, and more
|
||||
- **Dual API**: Async-first design with full synchronous support via `blocking` feature
|
||||
- **Zero Protobuf Hassle**: Pre-generated types — no KiCad source checkout needed
|
||||
- **Battle-Tested**: Used in real automation and integration workflows
|
||||
|
||||
Core entrypoints:
|
||||
### API Comparison
|
||||
|
||||
- Async: `kicad_ipc_rs::KiCadClient`
|
||||
- Blocking: `kicad_ipc_rs::KiCadClientBlocking` (`blocking` feature)
|
||||
- Error type: `kicad_ipc_rs::KiCadError`
|
||||
| Capability | `kicad-ipc-rs` | Python bindings | Official Rust |
|
||||
|------------|---------------|-----------------|---------------|
|
||||
| Rust-native API | ✅ Production-ready | ❌ Python only | ⚠️ Preview |
|
||||
| Async + Sync | ✅ Both supported | ⚠️ Event-loop | ⚠️ Preview |
|
||||
| Complete coverage | ✅ 57/57 commands | Unknown | Unknown |
|
||||
| Active maintenance | ✅ Yes | ✅ Official | ⚠️ Preview |
|
||||
|
||||
Related docs:
|
||||
## Project Goals
|
||||
|
||||
- Crate README: [README.md](https://github.com/Milind220/kicad-ipc-rs/blob/main/README.md)
|
||||
- CLI runbook: [docs/TEST_CLI.md](https://github.com/Milind220/kicad-ipc-rs/blob/main/docs/TEST_CLI.md)
|
||||
- API docs: [docs.rs/kicad-ipc-rs](https://docs.rs/kicad-ipc-rs)
|
||||
- Rust-native API for all KiCad IPC commands
|
||||
- Typed, ergonomic models for board and editor operations
|
||||
- Full parity between async and blocking APIs
|
||||
- Clear documentation and real-world examples
|
||||
- Stable, maintainable release workflow
|
||||
|
||||
## Current Scope
|
||||
|
||||
- KiCad API proto snapshot pinned in repo (`src/proto/generated/`)
|
||||
- 57/57 wrapped command families from KiCad v10.0.0
|
||||
- Runtime compatibility verified against KiCad 10.0.0
|
||||
|
||||
## Core Entrypoints
|
||||
|
||||
- **Async**: `kicad_ipc_rs::KiCadClient`
|
||||
- **Blocking**: `kicad_ipc_rs::KiCadClientBlocking` (enable `blocking` feature)
|
||||
- **Errors**: `kicad_ipc_rs::KiCadError`
|
||||
|
||||
## Getting Started
|
||||
|
||||
Jump to [Quickstart](quickstart.md) to connect to KiCad and run your first commands.
|
||||
|
||||
## Related Docs
|
||||
|
||||
- [Crate README](https://github.com/Milind220/kicad-ipc-rs/blob/main/README.md)
|
||||
- [API Reference on docs.rs](https://docs.rs/kicad-ipc-rs)
|
||||
- [Examples](examples.md) for real-world patterns
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
```toml
|
||||
[dependencies]
|
||||
kicad-ipc-rs = "0.3.1"
|
||||
kicad-ipc-rs = "0.4.1"
|
||||
tokio = { version = "1", features = ["macros", "rt"] }
|
||||
```
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ async fn main() -> Result<(), kicad_ipc_rs::KiCadError> {
|
|||
|
||||
```toml
|
||||
[dependencies]
|
||||
kicad-ipc-rs = { version = "0.3.1", features = ["blocking"] }
|
||||
kicad-ipc-rs = { version = "0.4.1", features = ["blocking"] }
|
||||
```
|
||||
|
||||
```rust,no_run
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ cargo test --features blocking
|
|||
- [`src/model/board.rs`](https://github.com/Milind220/kicad-ipc-rs/blob/main/src/model/board.rs)
|
||||
- [`test-scripts/kicad-ipc-cli.rs`](https://github.com/Milind220/kicad-ipc-rs/blob/main/test-scripts/kicad-ipc-cli.rs)
|
||||
- Runtime command coverage matrix:
|
||||
- [README coverage section](https://github.com/Milind220/kicad-ipc-rs#kicad-v10-rc11-api-completion-matrix)
|
||||
- [README coverage section](https://github.com/Milind220/kicad-ipc-rs#kicad-v1000-api-completion-matrix)
|
||||
- Runtime CLI verification flow:
|
||||
- [docs/TEST_CLI.md](https://github.com/Milind220/kicad-ipc-rs/blob/main/docs/TEST_CLI.md)
|
||||
|
||||
|
|
|
|||
2
kicad
2
kicad
|
|
@ -1 +1 @@
|
|||
Subproject commit c7c84125beb16c3a76f03fdb8daf60c9c3518daa
|
||||
Subproject commit 0feeca2a807f428ad2b3fa7c1e39625cb769f02c
|
||||
|
|
@ -428,6 +428,7 @@ impl KiCadClientBlocking {
|
|||
fn set_active_layer(&self, layer_id: i32) -> Result<(), KiCadError>;
|
||||
fn get_visible_layers(&self) -> Result<Vec<BoardLayerInfo>, KiCadError>;
|
||||
fn set_visible_layers(&self, layer_ids: Vec<i32>) -> Result<(), KiCadError>;
|
||||
fn get_board_layer_name(&self, layer_id: i32) -> Result<String, KiCadError>;
|
||||
fn get_board_origin(&self, kind: BoardOriginKind) -> Result<Vector2Nm, KiCadError>;
|
||||
fn set_board_origin(&self, kind: BoardOriginKind, origin: Vector2Nm) -> Result<(), KiCadError>;
|
||||
fn get_selection_summary(&self, type_codes: Vec<i32>) -> Result<SelectionSummary, KiCadError>;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ const CMD_GET_ACTIVE_LAYER: &str = "kiapi.board.commands.GetActiveLayer";
|
|||
const CMD_SET_ACTIVE_LAYER: &str = "kiapi.board.commands.SetActiveLayer";
|
||||
const CMD_GET_VISIBLE_LAYERS: &str = "kiapi.board.commands.GetVisibleLayers";
|
||||
const CMD_SET_VISIBLE_LAYERS: &str = "kiapi.board.commands.SetVisibleLayers";
|
||||
const CMD_GET_BOARD_LAYER_NAME: &str = "kiapi.board.commands.GetBoardLayerName";
|
||||
const CMD_GET_BOARD_ORIGIN: &str = "kiapi.board.commands.GetBoardOrigin";
|
||||
const CMD_SET_BOARD_ORIGIN: &str = "kiapi.board.commands.SetBoardOrigin";
|
||||
const CMD_GET_BOARD_STACKUP: &str = "kiapi.board.commands.GetBoardStackup";
|
||||
|
|
@ -95,6 +96,7 @@ const RES_GET_NETS: &str = "kiapi.board.commands.NetsResponse";
|
|||
const RES_GET_BOARD_ENABLED_LAYERS: &str = "kiapi.board.commands.BoardEnabledLayersResponse";
|
||||
const RES_BOARD_LAYER_RESPONSE: &str = "kiapi.board.commands.BoardLayerResponse";
|
||||
const RES_BOARD_LAYERS: &str = "kiapi.board.commands.BoardLayers";
|
||||
const RES_BOARD_LAYER_NAME_RESPONSE: &str = "kiapi.board.commands.BoardLayerNameResponse";
|
||||
const RES_BOARD_STACKUP_RESPONSE: &str = "kiapi.board.commands.BoardStackupResponse";
|
||||
const RES_GRAPHICS_DEFAULTS_RESPONSE: &str = "kiapi.board.commands.GraphicsDefaultsResponse";
|
||||
const RES_BOARD_EDITOR_APPEARANCE_SETTINGS: &str =
|
||||
|
|
@ -990,6 +992,22 @@ impl KiCadClient {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_board_layer_name(&self, layer_id: i32) -> Result<String, KiCadError> {
|
||||
let board = self.current_board_document_proto().await?;
|
||||
let command = board_commands::GetBoardLayerName {
|
||||
board: Some(board),
|
||||
layer: layer_id,
|
||||
};
|
||||
|
||||
let response = self
|
||||
.send_command(envelope::pack_any(&command, CMD_GET_BOARD_LAYER_NAME))
|
||||
.await?;
|
||||
|
||||
let payload: board_commands::BoardLayerNameResponse =
|
||||
envelope::unpack_any(&response, RES_BOARD_LAYER_NAME_RESPONSE)?;
|
||||
Ok(payload.name)
|
||||
}
|
||||
|
||||
pub async fn get_board_origin(&self, kind: BoardOriginKind) -> Result<Vector2Nm, KiCadError> {
|
||||
let board = self.current_board_document_proto().await?;
|
||||
let command = board_commands::GetBoardOrigin {
|
||||
|
|
@ -4720,6 +4738,58 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_board_layer_name_response_decodes_expected_type_url() {
|
||||
let payload = prost_types::Any {
|
||||
type_url: super::envelope::type_url("kiapi.board.commands.BoardLayerNameResponse"),
|
||||
value: crate::proto::kiapi::board::commands::BoardLayerNameResponse {
|
||||
name: "In1.Cu".to_string(),
|
||||
}
|
||||
.encode_to_vec(),
|
||||
};
|
||||
|
||||
let decoded: crate::proto::kiapi::board::commands::BoardLayerNameResponse =
|
||||
super::decode_any(&payload, super::RES_BOARD_LAYER_NAME_RESPONSE)
|
||||
.expect("layer-name response should decode");
|
||||
|
||||
assert_eq!(decoded.name, "In1.Cu");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_board_layer_name_response_rejects_wrong_type_url() {
|
||||
let payload = prost_types::Any {
|
||||
type_url: super::envelope::type_url("kiapi.board.commands.BoardLayerResponse"),
|
||||
value: crate::proto::kiapi::board::commands::BoardLayerNameResponse {
|
||||
name: "F.Cu".to_string(),
|
||||
}
|
||||
.encode_to_vec(),
|
||||
};
|
||||
|
||||
let err =
|
||||
super::decode_any::<crate::proto::kiapi::board::commands::BoardLayerNameResponse>(
|
||||
&payload,
|
||||
super::RES_BOARD_LAYER_NAME_RESPONSE,
|
||||
)
|
||||
.expect_err("mismatched type_url should fail");
|
||||
|
||||
assert!(matches!(err, KiCadError::UnexpectedPayloadType { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_board_layer_name_command_type_url_matches_proto_name() {
|
||||
let command = crate::proto::kiapi::board::commands::GetBoardLayerName {
|
||||
board: None,
|
||||
layer: crate::proto::kiapi::board::types::BoardLayer::BlFCu as i32,
|
||||
};
|
||||
|
||||
let any = super::envelope::pack_any(&command, super::CMD_GET_BOARD_LAYER_NAME);
|
||||
|
||||
assert_eq!(
|
||||
any.type_url,
|
||||
super::envelope::type_url("kiapi.board.commands.GetBoardLayerName")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn summarize_selection_counts_payload_types() {
|
||||
let items = vec![
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
// Generated by tools/proto-gen.
|
||||
pub const KICAD_API_VERSION: &str = "10.0.0-rc1.1-0-gc7c84125";
|
||||
pub const KICAD_API_VERSION: &str = "10.0.0-0-g0feeca2a";
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@
|
|||
//! | Rust-native client API | ✅ Yes | ❌ Python package | ⚠️ Development preview |
|
||||
//! | Async-first API design | ✅ `KiCadClient` | ⚠️ App-managed event-loop model | ⚠️ Development preview |
|
||||
//! | Blocking support for sync apps | ✅ `feature = "blocking"` | ✅ Native Python sync usage | ⚠️ Development preview |
|
||||
//! | Wrapped KiCad command coverage (current proto snapshot) | ✅ 56/56 command wrappers | Unknown | Unknown |
|
||||
//! | Wrapped KiCad command coverage (current proto snapshot) | ✅ 57/57 command wrappers | Unknown | Unknown |
|
||||
//! | Maintainer focus | ✅ This crate is actively maintained for Rust users | ✅ Official KiCad Python package | ⚠️ Preview status |
|
||||
//!
|
||||
//! Evidence and references:
|
||||
//! - `kicad-python` package: <https://gitlab.com/kicad/code/kicad-python>
|
||||
//! - `kicad-rs` package (states "development preview with no docs yet"): <https://gitlab.com/kicad/code/kicad-rs>
|
||||
//! - Coverage matrix and runtime notes: <https://github.com/Milind220/kicad-ipc-rs#kicad-v10-rc11-api-completion-matrix>
|
||||
//! - Coverage matrix and runtime notes: <https://github.com/Milind220/kicad-ipc-rs#kicad-v1000-api-completion-matrix>
|
||||
//!
|
||||
//! ## Quickstart (async)
|
||||
//!
|
||||
|
|
@ -60,10 +60,12 @@ pub mod client;
|
|||
///
|
||||
/// This module is public for advanced integrations and debugging, but most users
|
||||
/// should prefer [`crate::client::KiCadClient`] methods.
|
||||
#[allow(missing_docs)]
|
||||
pub mod commands;
|
||||
/// Envelope helpers for command/response packing and unpacking.
|
||||
///
|
||||
/// This is primarily an advanced/internal surface.
|
||||
#[allow(missing_docs)]
|
||||
pub mod envelope;
|
||||
/// Error types returned by this crate.
|
||||
pub mod error;
|
||||
|
|
@ -73,6 +75,7 @@ pub mod model;
|
|||
/// IPC transport implementation details.
|
||||
///
|
||||
/// Most applications should not need to use this module directly.
|
||||
#[allow(missing_docs)]
|
||||
pub mod transport;
|
||||
|
||||
#[cfg(feature = "blocking")]
|
||||
|
|
|
|||
|
|
@ -77,6 +77,20 @@ pub struct SetBoardOrigin {
|
|||
pub origin: ::core::option::Option<super::super::common::types::Vector2>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct GetBoardLayerName {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub board: ::core::option::Option<super::super::common::types::DocumentSpecifier>,
|
||||
#[prost(enumeration = "super::types::BoardLayer", tag = "2")]
|
||||
pub layer: i32,
|
||||
}
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct BoardLayerNameResponse {
|
||||
/// The name of the layer shown in the KiCad GUI, which may be a default value like "F.Cu" or may
|
||||
/// have been customized by the user.
|
||||
#[prost(string, tag = "1")]
|
||||
pub name: ::prost::alloc::string::String,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct GetNets {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub board: ::core::option::Option<super::super::common::types::DocumentSpecifier>,
|
||||
|
|
|
|||
|
|
@ -2586,6 +2586,11 @@ fn proto_coverage_board_read_rows() -> Vec<(&'static str, &'static str, &'static
|
|||
"implemented",
|
||||
"get_active_layer",
|
||||
),
|
||||
(
|
||||
"kiapi.board.commands.GetBoardLayerName",
|
||||
"implemented",
|
||||
"get_board_layer_name",
|
||||
),
|
||||
(
|
||||
"kiapi.board.commands.GetBoardEditorAppearanceSettings",
|
||||
"implemented",
|
||||
|
|
|
|||
Loading…
Reference in New Issue