feat(project): add SetNetClasses API and CLI command
This commit is contained in:
parent
1c57eef959
commit
fff70f61c1
|
|
@ -43,6 +43,7 @@ Deferred manual/runtime verification (implemented after 2026-02-20 while user un
|
||||||
- `UpdateItems`
|
- `UpdateItems`
|
||||||
- `DeleteItems`
|
- `DeleteItems`
|
||||||
- `ParseAndCreateItemsFromString`
|
- `ParseAndCreateItemsFromString`
|
||||||
|
- `SetNetClasses`
|
||||||
|
|
||||||
## KiCad v10 RC1.1 API Completion Matrix
|
## KiCad v10 RC1.1 API Completion Matrix
|
||||||
|
|
||||||
|
|
@ -64,10 +65,10 @@ Legend:
|
||||||
| --- | ---: | ---: | ---: |
|
| --- | ---: | ---: | ---: |
|
||||||
| Common (base) | 6 | 6 | 100% |
|
| Common (base) | 6 | 6 | 100% |
|
||||||
| Common editor/document | 23 | 23 | 100% |
|
| Common editor/document | 23 | 23 | 100% |
|
||||||
| Project manager | 5 | 3 | 60% |
|
| Project manager | 5 | 4 | 80% |
|
||||||
| Board editor (PCB) | 22 | 20 | 91% |
|
| Board editor (PCB) | 22 | 20 | 91% |
|
||||||
| Schematic editor (dedicated proto commands) | 0 | 0 | n/a |
|
| Schematic editor (dedicated proto commands) | 0 | 0 | n/a |
|
||||||
| **Total** | **56** | **52** | **93%** |
|
| **Total** | **56** | **53** | **95%** |
|
||||||
|
|
||||||
### Common (base)
|
### Common (base)
|
||||||
|
|
||||||
|
|
@ -113,7 +114,7 @@ Legend:
|
||||||
| KiCad Command | Status | Rust API |
|
| KiCad Command | Status | Rust API |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `GetNetClasses` | Implemented | `KiCadClient::get_net_classes_raw`, `KiCadClient::get_net_classes` |
|
| `GetNetClasses` | Implemented | `KiCadClient::get_net_classes_raw`, `KiCadClient::get_net_classes` |
|
||||||
| `SetNetClasses` | Not yet | - |
|
| `SetNetClasses` | Implemented | `KiCadClient::set_net_classes_raw`, `KiCadClient::set_net_classes` |
|
||||||
| `ExpandTextVariables` | Implemented | `KiCadClient::expand_text_variables_raw`, `KiCadClient::expand_text_variables` |
|
| `ExpandTextVariables` | Implemented | `KiCadClient::expand_text_variables_raw`, `KiCadClient::expand_text_variables` |
|
||||||
| `GetTextVariables` | Implemented | `KiCadClient::get_text_variables_raw`, `KiCadClient::get_text_variables` |
|
| `GetTextVariables` | Implemented | `KiCadClient::get_text_variables_raw`, `KiCadClient::get_text_variables` |
|
||||||
| `SetTextVariables` | Not yet | - |
|
| `SetTextVariables` | Not yet | - |
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,12 @@ List project net classes:
|
||||||
cargo run --bin kicad-ipc-cli -- net-classes
|
cargo run --bin kicad-ipc-cli -- net-classes
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Write current net classes back with selected merge mode:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run --bin kicad-ipc-cli -- set-net-classes --merge-mode merge
|
||||||
|
```
|
||||||
|
|
||||||
List text variables for current board document:
|
List text variables for current board document:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
122
src/client.rs
122
src/client.rs
|
|
@ -19,10 +19,10 @@ use crate::model::board::{
|
||||||
};
|
};
|
||||||
use crate::model::common::{
|
use crate::model::common::{
|
||||||
CommitAction, CommitSession, DocumentSpecifier, DocumentType, EditorFrameType, ItemBoundingBox,
|
CommitAction, CommitSession, DocumentSpecifier, DocumentType, EditorFrameType, ItemBoundingBox,
|
||||||
ItemHitTestResult, PcbObjectTypeCode, ProjectInfo, RunActionStatus, SelectionItemDetail,
|
ItemHitTestResult, MapMergeMode, PcbObjectTypeCode, ProjectInfo, RunActionStatus,
|
||||||
SelectionSummary, SelectionTypeCount, TextAsShapesEntry, TextAttributesSpec, TextBoxSpec,
|
SelectionItemDetail, SelectionSummary, SelectionTypeCount, TextAsShapesEntry,
|
||||||
TextExtents, TextHorizontalAlignment, TextObjectSpec, TextShape, TextShapeGeometry, TextSpec,
|
TextAttributesSpec, TextBoxSpec, TextExtents, TextHorizontalAlignment, TextObjectSpec,
|
||||||
TextVerticalAlignment, TitleBlockInfo, VersionInfo,
|
TextShape, TextShapeGeometry, TextSpec, TextVerticalAlignment, TitleBlockInfo, VersionInfo,
|
||||||
};
|
};
|
||||||
use crate::proto::kiapi::board as board_proto;
|
use crate::proto::kiapi::board as board_proto;
|
||||||
use crate::proto::kiapi::board::commands as board_commands;
|
use crate::proto::kiapi::board::commands as board_commands;
|
||||||
|
|
@ -40,6 +40,7 @@ const CMD_GET_VERSION: &str = "kiapi.common.commands.GetVersion";
|
||||||
const CMD_GET_KICAD_BINARY_PATH: &str = "kiapi.common.commands.GetKiCadBinaryPath";
|
const CMD_GET_KICAD_BINARY_PATH: &str = "kiapi.common.commands.GetKiCadBinaryPath";
|
||||||
const CMD_GET_PLUGIN_SETTINGS_PATH: &str = "kiapi.common.commands.GetPluginSettingsPath";
|
const CMD_GET_PLUGIN_SETTINGS_PATH: &str = "kiapi.common.commands.GetPluginSettingsPath";
|
||||||
const CMD_GET_NET_CLASSES: &str = "kiapi.common.commands.GetNetClasses";
|
const CMD_GET_NET_CLASSES: &str = "kiapi.common.commands.GetNetClasses";
|
||||||
|
const CMD_SET_NET_CLASSES: &str = "kiapi.common.commands.SetNetClasses";
|
||||||
const CMD_GET_TEXT_VARIABLES: &str = "kiapi.common.commands.GetTextVariables";
|
const CMD_GET_TEXT_VARIABLES: &str = "kiapi.common.commands.GetTextVariables";
|
||||||
const CMD_EXPAND_TEXT_VARIABLES: &str = "kiapi.common.commands.ExpandTextVariables";
|
const CMD_EXPAND_TEXT_VARIABLES: &str = "kiapi.common.commands.ExpandTextVariables";
|
||||||
const CMD_GET_TEXT_EXTENTS: &str = "kiapi.common.commands.GetTextExtents";
|
const CMD_GET_TEXT_EXTENTS: &str = "kiapi.common.commands.GetTextExtents";
|
||||||
|
|
@ -465,6 +466,33 @@ impl KiCadClient {
|
||||||
Ok(classes)
|
Ok(classes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn set_net_classes_raw(
|
||||||
|
&self,
|
||||||
|
net_classes: Vec<NetClassInfo>,
|
||||||
|
merge_mode: MapMergeMode,
|
||||||
|
) -> Result<prost_types::Any, KiCadError> {
|
||||||
|
let command = common_commands::SetNetClasses {
|
||||||
|
net_classes: net_classes
|
||||||
|
.into_iter()
|
||||||
|
.map(net_class_info_to_proto)
|
||||||
|
.collect(),
|
||||||
|
merge_mode: map_merge_mode_to_proto(merge_mode),
|
||||||
|
};
|
||||||
|
let response = self
|
||||||
|
.send_command(envelope::pack_any(&command, CMD_SET_NET_CLASSES))
|
||||||
|
.await?;
|
||||||
|
response_payload_as_any(response, RES_PROTOBUF_EMPTY)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_net_classes(
|
||||||
|
&self,
|
||||||
|
net_classes: Vec<NetClassInfo>,
|
||||||
|
merge_mode: MapMergeMode,
|
||||||
|
) -> Result<Vec<NetClassInfo>, KiCadError> {
|
||||||
|
let _ = self.set_net_classes_raw(net_classes, merge_mode).await?;
|
||||||
|
self.get_net_classes().await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_text_variables_raw(&self) -> Result<prost_types::Any, KiCadError> {
|
pub async fn get_text_variables_raw(&self) -> Result<prost_types::Any, KiCadError> {
|
||||||
let command = common_commands::GetTextVariables {
|
let command = common_commands::GetTextVariables {
|
||||||
document: Some(self.current_board_document_proto().await?),
|
document: Some(self.current_board_document_proto().await?),
|
||||||
|
|
@ -2161,6 +2189,13 @@ fn commit_action_to_proto(action: CommitAction) -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn map_merge_mode_to_proto(value: MapMergeMode) -> i32 {
|
||||||
|
match value {
|
||||||
|
MapMergeMode::Merge => common_types::MapMergeMode::MmmMerge as i32,
|
||||||
|
MapMergeMode::Replace => common_types::MapMergeMode::MmmReplace as i32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn summarize_selection(items: Vec<prost_types::Any>) -> SelectionSummary {
|
fn summarize_selection(items: Vec<prost_types::Any>) -> SelectionSummary {
|
||||||
let mut counts = BTreeMap::<String, usize>::new();
|
let mut counts = BTreeMap::<String, usize>::new();
|
||||||
|
|
||||||
|
|
@ -2645,6 +2680,62 @@ fn board_editor_appearance_settings_to_proto(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn net_class_type_to_proto(value: NetClassType) -> i32 {
|
||||||
|
match value {
|
||||||
|
NetClassType::Explicit => common_project::NetClassType::NctExplicit as i32,
|
||||||
|
NetClassType::Implicit => common_project::NetClassType::NctImplicit as i32,
|
||||||
|
NetClassType::Unknown(raw) => raw,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn net_class_info_to_proto(value: NetClassInfo) -> common_project::NetClass {
|
||||||
|
let board = value
|
||||||
|
.board
|
||||||
|
.map(|board| common_project::NetClassBoardSettings {
|
||||||
|
clearance: board
|
||||||
|
.clearance_nm
|
||||||
|
.map(|value_nm| common_types::Distance { value_nm }),
|
||||||
|
track_width: board
|
||||||
|
.track_width_nm
|
||||||
|
.map(|value_nm| common_types::Distance { value_nm }),
|
||||||
|
diff_pair_track_width: board
|
||||||
|
.diff_pair_track_width_nm
|
||||||
|
.map(|value_nm| common_types::Distance { value_nm }),
|
||||||
|
diff_pair_gap: board
|
||||||
|
.diff_pair_gap_nm
|
||||||
|
.map(|value_nm| common_types::Distance { value_nm }),
|
||||||
|
diff_pair_via_gap: board
|
||||||
|
.diff_pair_via_gap_nm
|
||||||
|
.map(|value_nm| common_types::Distance { value_nm }),
|
||||||
|
via_stack: if board.has_via_stack {
|
||||||
|
Some(board_types::PadStack::default())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
microvia_stack: if board.has_microvia_stack {
|
||||||
|
Some(board_types::PadStack::default())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
color: board.color.map(|color| common_types::Color {
|
||||||
|
r: color.r,
|
||||||
|
g: color.g,
|
||||||
|
b: color.b,
|
||||||
|
a: color.a,
|
||||||
|
}),
|
||||||
|
tuning_profile: board.tuning_profile,
|
||||||
|
});
|
||||||
|
|
||||||
|
common_project::NetClass {
|
||||||
|
name: value.name,
|
||||||
|
priority: value.priority,
|
||||||
|
board,
|
||||||
|
schematic: None,
|
||||||
|
r#type: net_class_type_to_proto(value.class_type),
|
||||||
|
constituents: value.constituents,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn map_net_class_type(value: i32) -> NetClassType {
|
fn map_net_class_type(value: i32) -> NetClassType {
|
||||||
match common_project::NetClassType::try_from(value) {
|
match common_project::NetClassType::try_from(value) {
|
||||||
Ok(common_project::NetClassType::NctExplicit) => NetClassType::Explicit,
|
Ok(common_project::NetClassType::NctExplicit) => NetClassType::Explicit,
|
||||||
|
|
@ -3379,11 +3470,12 @@ mod tests {
|
||||||
any_to_pretty_debug, board_editor_appearance_settings_to_proto, commit_action_to_proto,
|
any_to_pretty_debug, board_editor_appearance_settings_to_proto, commit_action_to_proto,
|
||||||
drc_severity_to_proto, ensure_item_deletion_status_ok, ensure_item_request_ok,
|
drc_severity_to_proto, ensure_item_deletion_status_ok, ensure_item_request_ok,
|
||||||
ensure_item_status_ok, layer_to_model, map_commit_session, map_hit_test_result,
|
ensure_item_status_ok, layer_to_model, map_commit_session, map_hit_test_result,
|
||||||
map_item_bounding_boxes, map_polygon_with_holes, map_run_action_status,
|
map_item_bounding_boxes, map_merge_mode_to_proto, map_polygon_with_holes,
|
||||||
model_document_to_proto, normalize_socket_uri, pad_netlist_from_footprint_items,
|
map_run_action_status, model_document_to_proto, normalize_socket_uri,
|
||||||
response_payload_as_any, select_single_board_document, select_single_project_path,
|
pad_netlist_from_footprint_items, response_payload_as_any, select_single_board_document,
|
||||||
selection_item_detail, summarize_item_details, summarize_selection,
|
select_single_project_path, selection_item_detail, summarize_item_details,
|
||||||
text_horizontal_alignment_to_proto, text_spec_to_proto, PCB_OBJECT_TYPES,
|
summarize_selection, text_horizontal_alignment_to_proto, text_spec_to_proto,
|
||||||
|
PCB_OBJECT_TYPES,
|
||||||
};
|
};
|
||||||
use crate::error::KiCadError;
|
use crate::error::KiCadError;
|
||||||
use crate::model::common::{
|
use crate::model::common::{
|
||||||
|
|
@ -3551,6 +3643,18 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn map_merge_mode_to_proto_maps_known_variants() {
|
||||||
|
assert_eq!(
|
||||||
|
map_merge_mode_to_proto(crate::model::common::MapMergeMode::Merge),
|
||||||
|
crate::proto::kiapi::common::types::MapMergeMode::MmmMerge as i32
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
map_merge_mode_to_proto(crate::model::common::MapMergeMode::Replace),
|
||||||
|
crate::proto::kiapi::common::types::MapMergeMode::MmmReplace as i32
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn drc_severity_to_proto_maps_known_variants() {
|
fn drc_severity_to_proto_maps_known_variants() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,8 @@ pub use crate::model::board::{
|
||||||
};
|
};
|
||||||
pub use crate::model::common::{
|
pub use crate::model::common::{
|
||||||
CommitAction, CommitSession, DocumentSpecifier, DocumentType, EditorFrameType, ItemBoundingBox,
|
CommitAction, CommitSession, DocumentSpecifier, DocumentType, EditorFrameType, ItemBoundingBox,
|
||||||
ItemHitTestResult, PcbObjectTypeCode, RunActionStatus, SelectionItemDetail, SelectionSummary,
|
ItemHitTestResult, MapMergeMode, PcbObjectTypeCode, RunActionStatus, SelectionItemDetail,
|
||||||
SelectionTypeCount, TextAsShapesEntry, TextAttributesSpec, TextBoxSpec, TextExtents,
|
SelectionSummary, SelectionTypeCount, TextAsShapesEntry, TextAttributesSpec, TextBoxSpec,
|
||||||
TextHorizontalAlignment, TextObjectSpec, TextShape, TextShapeGeometry, TextSpec,
|
TextExtents, TextHorizontalAlignment, TextObjectSpec, TextShape, TextShapeGeometry, TextSpec,
|
||||||
TextVerticalAlignment, TitleBlockInfo, VersionInfo,
|
TextVerticalAlignment, TitleBlockInfo, VersionInfo,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,35 @@ pub enum RunActionStatus {
|
||||||
Unknown(i32),
|
Unknown(i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum MapMergeMode {
|
||||||
|
Merge,
|
||||||
|
Replace,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for MapMergeMode {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Merge => write!(f, "merge"),
|
||||||
|
Self::Replace => write!(f, "replace"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for MapMergeMode {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||||
|
match value {
|
||||||
|
"merge" => Ok(Self::Merge),
|
||||||
|
"replace" => Ok(Self::Replace),
|
||||||
|
_ => Err(format!(
|
||||||
|
"unknown merge mode `{value}`; expected `merge` or `replace`"
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for CommitAction {
|
impl std::fmt::Display for CommitAction {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -403,7 +432,7 @@ impl std::fmt::Display for ItemHitTestResult {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{CommitAction, EditorFrameType};
|
use super::{CommitAction, EditorFrameType, MapMergeMode};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -433,4 +462,15 @@ mod tests {
|
||||||
fn editor_frame_type_rejects_unknown_values() {
|
fn editor_frame_type_rejects_unknown_values() {
|
||||||
assert!(EditorFrameType::from_str("layout").is_err());
|
assert!(EditorFrameType::from_str("layout").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn map_merge_mode_parses_known_values() {
|
||||||
|
assert_eq!(MapMergeMode::from_str("merge"), Ok(MapMergeMode::Merge));
|
||||||
|
assert_eq!(MapMergeMode::from_str("replace"), Ok(MapMergeMode::Replace));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn map_merge_mode_rejects_unknown_values() {
|
||||||
|
assert!(MapMergeMode::from_str("upsert").is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue