feat(common): add CreateItems API and CLI command
This commit is contained in:
parent
a29a573c6d
commit
01ed710ae2
|
|
@ -39,6 +39,7 @@ Deferred manual/runtime verification (implemented after 2026-02-20 while user un
|
||||||
- `SaveCopyOfDocument`
|
- `SaveCopyOfDocument`
|
||||||
- `RevertDocument`
|
- `RevertDocument`
|
||||||
- `RunAction`
|
- `RunAction`
|
||||||
|
- `CreateItems`
|
||||||
|
|
||||||
## KiCad v10 RC1.1 API Completion Matrix
|
## KiCad v10 RC1.1 API Completion Matrix
|
||||||
|
|
||||||
|
|
@ -59,11 +60,11 @@ Legend:
|
||||||
| Section | Proto Commands | Implemented | Coverage |
|
| Section | Proto Commands | Implemented | Coverage |
|
||||||
| --- | ---: | ---: | ---: |
|
| --- | ---: | ---: | ---: |
|
||||||
| Common (base) | 6 | 6 | 100% |
|
| Common (base) | 6 | 6 | 100% |
|
||||||
| Common editor/document | 23 | 19 | 83% |
|
| Common editor/document | 23 | 20 | 87% |
|
||||||
| Project manager | 5 | 3 | 60% |
|
| Project manager | 5 | 3 | 60% |
|
||||||
| 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** | **48** | **86%** |
|
| **Total** | **56** | **49** | **88%** |
|
||||||
|
|
||||||
### Common (base)
|
### Common (base)
|
||||||
|
|
||||||
|
|
@ -88,7 +89,7 @@ Legend:
|
||||||
| `RunAction` | Implemented | `KiCadClient::run_action_raw`, `KiCadClient::run_action` |
|
| `RunAction` | Implemented | `KiCadClient::run_action_raw`, `KiCadClient::run_action` |
|
||||||
| `BeginCommit` | Implemented | `KiCadClient::begin_commit_raw`, `KiCadClient::begin_commit` |
|
| `BeginCommit` | Implemented | `KiCadClient::begin_commit_raw`, `KiCadClient::begin_commit` |
|
||||||
| `EndCommit` | Implemented | `KiCadClient::end_commit_raw`, `KiCadClient::end_commit` |
|
| `EndCommit` | Implemented | `KiCadClient::end_commit_raw`, `KiCadClient::end_commit` |
|
||||||
| `CreateItems` | Not yet | - |
|
| `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` |
|
| `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` |
|
| `GetItemsById` | Implemented | `KiCadClient::get_items_by_id_raw`, `KiCadClient::get_items_by_id`, `KiCadClient::get_items_by_id_details` |
|
||||||
| `UpdateItems` | Not yet | - |
|
| `UpdateItems` | Not yet | - |
|
||||||
|
|
|
||||||
|
|
@ -187,6 +187,12 @@ Run a raw KiCad tool action:
|
||||||
cargo run --bin kicad-ipc-cli -- run-action --action pcbnew.InteractiveSelection.ClearSelection
|
cargo run --bin kicad-ipc-cli -- run-action --action pcbnew.InteractiveSelection.ClearSelection
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Create raw Any item payload(s):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run --bin kicad-ipc-cli -- create-items --item type.googleapis.com/kiapi.board.types.Text=<hex_payload>
|
||||||
|
```
|
||||||
|
|
||||||
Show summary of current PCB selection by item type:
|
Show summary of current PCB selection by item type:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ const CMD_REMOVE_FROM_SELECTION: &str = "kiapi.common.commands.RemoveFromSelecti
|
||||||
const CMD_CLEAR_SELECTION: &str = "kiapi.common.commands.ClearSelection";
|
const CMD_CLEAR_SELECTION: &str = "kiapi.common.commands.ClearSelection";
|
||||||
const CMD_BEGIN_COMMIT: &str = "kiapi.common.commands.BeginCommit";
|
const CMD_BEGIN_COMMIT: &str = "kiapi.common.commands.BeginCommit";
|
||||||
const CMD_END_COMMIT: &str = "kiapi.common.commands.EndCommit";
|
const CMD_END_COMMIT: &str = "kiapi.common.commands.EndCommit";
|
||||||
|
const CMD_CREATE_ITEMS: &str = "kiapi.common.commands.CreateItems";
|
||||||
const CMD_GET_ITEMS: &str = "kiapi.common.commands.GetItems";
|
const CMD_GET_ITEMS: &str = "kiapi.common.commands.GetItems";
|
||||||
const CMD_GET_ITEMS_BY_ID: &str = "kiapi.common.commands.GetItemsById";
|
const CMD_GET_ITEMS_BY_ID: &str = "kiapi.common.commands.GetItemsById";
|
||||||
const CMD_GET_BOUNDING_BOX: &str = "kiapi.common.commands.GetBoundingBox";
|
const CMD_GET_BOUNDING_BOX: &str = "kiapi.common.commands.GetBoundingBox";
|
||||||
|
|
@ -114,6 +115,7 @@ const RES_VECTOR2: &str = "kiapi.common.types.Vector2";
|
||||||
const RES_SELECTION_RESPONSE: &str = "kiapi.common.commands.SelectionResponse";
|
const RES_SELECTION_RESPONSE: &str = "kiapi.common.commands.SelectionResponse";
|
||||||
const RES_BEGIN_COMMIT_RESPONSE: &str = "kiapi.common.commands.BeginCommitResponse";
|
const RES_BEGIN_COMMIT_RESPONSE: &str = "kiapi.common.commands.BeginCommitResponse";
|
||||||
const RES_END_COMMIT_RESPONSE: &str = "kiapi.common.commands.EndCommitResponse";
|
const RES_END_COMMIT_RESPONSE: &str = "kiapi.common.commands.EndCommitResponse";
|
||||||
|
const RES_CREATE_ITEMS_RESPONSE: &str = "kiapi.common.commands.CreateItemsResponse";
|
||||||
const RES_GET_ITEMS_RESPONSE: &str = "kiapi.common.commands.GetItemsResponse";
|
const RES_GET_ITEMS_RESPONSE: &str = "kiapi.common.commands.GetItemsResponse";
|
||||||
const RES_GET_BOUNDING_BOX_RESPONSE: &str = "kiapi.common.commands.GetBoundingBoxResponse";
|
const RES_GET_BOUNDING_BOX_RESPONSE: &str = "kiapi.common.commands.GetBoundingBoxResponse";
|
||||||
const RES_HIT_TEST_RESPONSE: &str = "kiapi.common.commands.HitTestResponse";
|
const RES_HIT_TEST_RESPONSE: &str = "kiapi.common.commands.HitTestResponse";
|
||||||
|
|
@ -616,6 +618,45 @@ impl KiCadClient {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn create_items_raw(
|
||||||
|
&self,
|
||||||
|
items: Vec<prost_types::Any>,
|
||||||
|
container_id: Option<String>,
|
||||||
|
) -> Result<prost_types::Any, KiCadError> {
|
||||||
|
let command = common_commands::CreateItems {
|
||||||
|
header: Some(self.current_board_item_header().await?),
|
||||||
|
items,
|
||||||
|
container: container_id.map(|value| common_types::Kiid { value }),
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = self
|
||||||
|
.send_command(envelope::pack_any(&command, CMD_CREATE_ITEMS))
|
||||||
|
.await?;
|
||||||
|
response_payload_as_any(response, RES_CREATE_ITEMS_RESPONSE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_items(
|
||||||
|
&self,
|
||||||
|
items: Vec<prost_types::Any>,
|
||||||
|
container_id: Option<String>,
|
||||||
|
) -> Result<Vec<prost_types::Any>, KiCadError> {
|
||||||
|
let payload = self.create_items_raw(items, container_id).await?;
|
||||||
|
let response: common_commands::CreateItemsResponse =
|
||||||
|
decode_any(&payload, RES_CREATE_ITEMS_RESPONSE)?;
|
||||||
|
ensure_item_request_ok(response.status)?;
|
||||||
|
|
||||||
|
response
|
||||||
|
.created_items
|
||||||
|
.into_iter()
|
||||||
|
.map(|row| {
|
||||||
|
ensure_item_status_ok(row.status)?;
|
||||||
|
row.item.ok_or_else(|| KiCadError::InvalidResponse {
|
||||||
|
reason: "CreateItemsResponse missing created item payload".to_string(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_nets(&self) -> Result<Vec<BoardNet>, KiCadError> {
|
pub async fn get_nets(&self) -> Result<Vec<BoardNet>, KiCadError> {
|
||||||
let board = self.current_board_document_proto().await?;
|
let board = self.current_board_document_proto().await?;
|
||||||
let command = board_commands::GetNets {
|
let command = board_commands::GetNets {
|
||||||
|
|
@ -2063,6 +2104,24 @@ fn ensure_item_request_ok(status: i32) -> Result<(), KiCadError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ensure_item_status_ok(status: Option<common_commands::ItemStatus>) -> Result<(), KiCadError> {
|
||||||
|
let status = status.unwrap_or_default();
|
||||||
|
let code = common_commands::ItemStatusCode::try_from(status.code)
|
||||||
|
.unwrap_or(common_commands::ItemStatusCode::IscUnknown);
|
||||||
|
|
||||||
|
if code != common_commands::ItemStatusCode::IscOk {
|
||||||
|
let detail = if status.error_message.is_empty() {
|
||||||
|
code.as_str_name().to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}: {}", code.as_str_name(), status.error_message)
|
||||||
|
};
|
||||||
|
|
||||||
|
return Err(KiCadError::ItemStatus { code: detail });
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn map_item_bounding_boxes(
|
fn map_item_bounding_boxes(
|
||||||
item_ids: Vec<common_types::Kiid>,
|
item_ids: Vec<common_types::Kiid>,
|
||||||
boxes: Vec<common_types::Box2>,
|
boxes: Vec<common_types::Box2>,
|
||||||
|
|
@ -3184,8 +3243,8 @@ fn default_client_name() -> String {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{
|
use super::{
|
||||||
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_request_ok, layer_to_model, map_commit_session,
|
drc_severity_to_proto, ensure_item_request_ok, ensure_item_status_ok, layer_to_model,
|
||||||
map_hit_test_result, map_item_bounding_boxes, map_polygon_with_holes,
|
map_commit_session, map_hit_test_result, map_item_bounding_boxes, map_polygon_with_holes,
|
||||||
map_run_action_status, model_document_to_proto, normalize_socket_uri,
|
map_run_action_status, model_document_to_proto, normalize_socket_uri,
|
||||||
pad_netlist_from_footprint_items, response_payload_as_any, select_single_board_document,
|
pad_netlist_from_footprint_items, response_payload_as_any, select_single_board_document,
|
||||||
select_single_project_path, selection_item_detail, summarize_item_details,
|
select_single_project_path, selection_item_detail, summarize_item_details,
|
||||||
|
|
@ -3595,6 +3654,27 @@ mod tests {
|
||||||
.is_err());
|
.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ensure_item_status_ok_accepts_ok_and_rejects_non_ok() {
|
||||||
|
assert!(
|
||||||
|
ensure_item_status_ok(Some(crate::proto::kiapi::common::commands::ItemStatus {
|
||||||
|
code: crate::proto::kiapi::common::commands::ItemStatusCode::IscOk as i32,
|
||||||
|
error_message: String::new(),
|
||||||
|
}))
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
|
||||||
|
let err = ensure_item_status_ok(Some(crate::proto::kiapi::common::commands::ItemStatus {
|
||||||
|
code: crate::proto::kiapi::common::commands::ItemStatusCode::IscInvalidType as i32,
|
||||||
|
error_message: "bad item type".to_string(),
|
||||||
|
}))
|
||||||
|
.expect_err("non-OK item status should fail");
|
||||||
|
match err {
|
||||||
|
KiCadError::ItemStatus { code } => assert!(code.contains("ISC_INVALID_TYPE")),
|
||||||
|
_ => panic!("expected item status error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn summarize_item_details_reports_unknown_payload_as_unparsed() {
|
fn summarize_item_details_reports_unknown_payload_as_unparsed() {
|
||||||
let items = vec![prost_types::Any {
|
let items = vec![prost_types::Any {
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue