feat(client): add BeginCommit API and CLI command
This commit is contained in:
parent
e644eb0ac5
commit
8e8033f07c
|
|
@ -39,11 +39,11 @@ Legend:
|
|||
| Section | Proto Commands | Implemented | Coverage |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Common (base) | 6 | 4 | 67% |
|
||||
| Common editor/document | 23 | 9 | 39% |
|
||||
| Common editor/document | 23 | 10 | 43% |
|
||||
| Project manager | 5 | 3 | 60% |
|
||||
| Board editor (PCB) | 22 | 13 | 59% |
|
||||
| Schematic editor (dedicated proto commands) | 0 | 0 | n/a |
|
||||
| **Total** | **56** | **29** | **52%** |
|
||||
| **Total** | **56** | **30** | **54%** |
|
||||
|
||||
### Common (base)
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ Legend:
|
|||
| `SaveCopyOfDocument` | Not yet | - |
|
||||
| `RevertDocument` | Not yet | - |
|
||||
| `RunAction` | Not yet | - |
|
||||
| `BeginCommit` | Not yet | - |
|
||||
| `BeginCommit` | Implemented | `KiCadClient::begin_commit_raw`, `KiCadClient::begin_commit` |
|
||||
| `EndCommit` | Not yet | - |
|
||||
| `CreateItems` | Not yet | - |
|
||||
| `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` |
|
||||
|
|
|
|||
|
|
@ -107,6 +107,12 @@ Show drill origin:
|
|||
cargo run --bin kicad-ipc-cli -- board-origin --type drill
|
||||
```
|
||||
|
||||
Start a staged commit and print commit ID:
|
||||
|
||||
```bash
|
||||
cargo run --bin kicad-ipc-cli -- begin-commit
|
||||
```
|
||||
|
||||
Show summary of current PCB selection by item type:
|
||||
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -18,10 +18,11 @@ use crate::model::board::{
|
|||
Vector2Nm,
|
||||
};
|
||||
use crate::model::common::{
|
||||
DocumentSpecifier, DocumentType, ItemBoundingBox, ItemHitTestResult, PcbObjectTypeCode,
|
||||
ProjectInfo, SelectionItemDetail, SelectionSummary, SelectionTypeCount, TextAsShapesEntry,
|
||||
TextAttributesSpec, TextBoxSpec, TextExtents, TextHorizontalAlignment, TextObjectSpec,
|
||||
TextShape, TextShapeGeometry, TextSpec, TextVerticalAlignment, TitleBlockInfo, VersionInfo,
|
||||
CommitSession, DocumentSpecifier, DocumentType, ItemBoundingBox, ItemHitTestResult,
|
||||
PcbObjectTypeCode, ProjectInfo, SelectionItemDetail, SelectionSummary, SelectionTypeCount,
|
||||
TextAsShapesEntry, TextAttributesSpec, TextBoxSpec, TextExtents, TextHorizontalAlignment,
|
||||
TextObjectSpec, TextShape, TextShapeGeometry, TextSpec, TextVerticalAlignment, TitleBlockInfo,
|
||||
VersionInfo,
|
||||
};
|
||||
use crate::proto::kiapi::board as board_proto;
|
||||
use crate::proto::kiapi::board::commands as board_commands;
|
||||
|
|
@ -58,6 +59,7 @@ const CMD_GET_PAD_SHAPE_AS_POLYGON: &str = "kiapi.board.commands.GetPadShapeAsPo
|
|||
const CMD_CHECK_PADSTACK_PRESENCE_ON_LAYERS: &str =
|
||||
"kiapi.board.commands.CheckPadstackPresenceOnLayers";
|
||||
const CMD_GET_SELECTION: &str = "kiapi.common.commands.GetSelection";
|
||||
const CMD_BEGIN_COMMIT: &str = "kiapi.common.commands.BeginCommit";
|
||||
const CMD_GET_ITEMS: &str = "kiapi.common.commands.GetItems";
|
||||
const CMD_GET_ITEMS_BY_ID: &str = "kiapi.common.commands.GetItemsById";
|
||||
const CMD_GET_BOUNDING_BOX: &str = "kiapi.common.commands.GetBoundingBox";
|
||||
|
|
@ -87,6 +89,7 @@ const RES_PAD_SHAPE_AS_POLYGON_RESPONSE: &str = "kiapi.board.commands.PadShapeAs
|
|||
const RES_PADSTACK_PRESENCE_RESPONSE: &str = "kiapi.board.commands.PadstackPresenceResponse";
|
||||
const RES_VECTOR2: &str = "kiapi.common.types.Vector2";
|
||||
const RES_SELECTION_RESPONSE: &str = "kiapi.common.commands.SelectionResponse";
|
||||
const RES_BEGIN_COMMIT_RESPONSE: &str = "kiapi.common.commands.BeginCommitResponse";
|
||||
const RES_GET_ITEMS_RESPONSE: &str = "kiapi.common.commands.GetItemsResponse";
|
||||
const RES_GET_BOUNDING_BOX_RESPONSE: &str = "kiapi.common.commands.GetBoundingBoxResponse";
|
||||
const RES_HIT_TEST_RESPONSE: &str = "kiapi.common.commands.HitTestResponse";
|
||||
|
|
@ -462,6 +465,21 @@ impl KiCadClient {
|
|||
Ok(!docs.is_empty())
|
||||
}
|
||||
|
||||
pub async fn begin_commit_raw(&self) -> Result<prost_types::Any, KiCadError> {
|
||||
let command = common_commands::BeginCommit {};
|
||||
let response = self
|
||||
.send_command(envelope::pack_any(&command, CMD_BEGIN_COMMIT))
|
||||
.await?;
|
||||
response_payload_as_any(response, RES_BEGIN_COMMIT_RESPONSE)
|
||||
}
|
||||
|
||||
pub async fn begin_commit(&self) -> Result<CommitSession, KiCadError> {
|
||||
let payload = self.begin_commit_raw().await?;
|
||||
let response: common_commands::BeginCommitResponse =
|
||||
decode_any(&payload, RES_BEGIN_COMMIT_RESPONSE)?;
|
||||
map_commit_session(response)
|
||||
}
|
||||
|
||||
pub async fn get_nets(&self) -> Result<Vec<BoardNet>, KiCadError> {
|
||||
let board = self.current_board_document_proto().await?;
|
||||
let command = board_commands::GetNets {
|
||||
|
|
@ -1551,6 +1569,22 @@ fn summarize_item_details(
|
|||
Ok(details)
|
||||
}
|
||||
|
||||
fn map_commit_session(
|
||||
response: common_commands::BeginCommitResponse,
|
||||
) -> Result<CommitSession, KiCadError> {
|
||||
let id = response.id.ok_or_else(|| KiCadError::InvalidResponse {
|
||||
reason: "BeginCommit response missing commit id".to_string(),
|
||||
})?;
|
||||
|
||||
if id.value.is_empty() {
|
||||
return Err(KiCadError::InvalidResponse {
|
||||
reason: "BeginCommit response returned empty commit id".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(CommitSession { id: id.value })
|
||||
}
|
||||
|
||||
fn ensure_item_request_ok(status: i32) -> Result<(), KiCadError> {
|
||||
let request_status = common_types::ItemRequestStatus::try_from(status)
|
||||
.unwrap_or(common_types::ItemRequestStatus::IrsUnknown);
|
||||
|
|
@ -2617,12 +2651,12 @@ fn default_client_name() -> String {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
any_to_pretty_debug, ensure_item_request_ok, layer_to_model, map_hit_test_result,
|
||||
map_item_bounding_boxes, map_polygon_with_holes, model_document_to_proto,
|
||||
normalize_socket_uri, pad_netlist_from_footprint_items, select_single_board_document,
|
||||
select_single_project_path, selection_item_detail, summarize_item_details,
|
||||
summarize_selection, text_horizontal_alignment_to_proto, text_spec_to_proto,
|
||||
PCB_OBJECT_TYPES,
|
||||
any_to_pretty_debug, ensure_item_request_ok, layer_to_model, map_commit_session,
|
||||
map_hit_test_result, map_item_bounding_boxes, map_polygon_with_holes,
|
||||
model_document_to_proto, normalize_socket_uri, pad_netlist_from_footprint_items,
|
||||
response_payload_as_any, select_single_board_document, select_single_project_path,
|
||||
selection_item_detail, summarize_item_details, summarize_selection,
|
||||
text_horizontal_alignment_to_proto, text_spec_to_proto, PCB_OBJECT_TYPES,
|
||||
};
|
||||
use crate::error::KiCadError;
|
||||
use crate::model::common::{
|
||||
|
|
@ -2759,6 +2793,41 @@ mod tests {
|
|||
assert_eq!(project.path, "/tmp/demo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_commit_session_maps_commit_id() {
|
||||
let response = crate::proto::kiapi::common::commands::BeginCommitResponse {
|
||||
id: Some(crate::proto::kiapi::common::types::Kiid {
|
||||
value: "commit-123".to_string(),
|
||||
}),
|
||||
};
|
||||
|
||||
let session = map_commit_session(response).expect("commit id should map");
|
||||
assert_eq!(session.id, "commit-123");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_commit_session_requires_commit_id() {
|
||||
let response = crate::proto::kiapi::common::commands::BeginCommitResponse { id: None };
|
||||
let err = map_commit_session(response).expect_err("missing id must fail");
|
||||
assert!(matches!(err, KiCadError::InvalidResponse { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn response_payload_as_any_validates_type_url() {
|
||||
let response = crate::proto::kiapi::common::ApiResponse {
|
||||
header: None,
|
||||
status: None,
|
||||
message: Some(prost_types::Any {
|
||||
type_url: super::envelope::type_url("kiapi.common.commands.GetVersionResponse"),
|
||||
value: Vec::new(),
|
||||
}),
|
||||
};
|
||||
|
||||
let err = response_payload_as_any(response, "kiapi.common.commands.BeginCommitResponse")
|
||||
.expect_err("wrong type_url must fail");
|
||||
assert!(matches!(err, KiCadError::UnexpectedPayloadType { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn summarize_selection_counts_payload_types() {
|
||||
let items = vec![
|
||||
|
|
|
|||
|
|
@ -33,8 +33,9 @@ pub use crate::model::board::{
|
|||
Vector2Nm,
|
||||
};
|
||||
pub use crate::model::common::{
|
||||
DocumentSpecifier, DocumentType, ItemBoundingBox, ItemHitTestResult, PcbObjectTypeCode,
|
||||
SelectionItemDetail, SelectionSummary, SelectionTypeCount, TextAsShapesEntry,
|
||||
TextAttributesSpec, TextBoxSpec, TextExtents, TextHorizontalAlignment, TextObjectSpec,
|
||||
TextShape, TextShapeGeometry, TextSpec, TextVerticalAlignment, TitleBlockInfo, VersionInfo,
|
||||
CommitSession, DocumentSpecifier, DocumentType, ItemBoundingBox, ItemHitTestResult,
|
||||
PcbObjectTypeCode, SelectionItemDetail, SelectionSummary, SelectionTypeCount,
|
||||
TextAsShapesEntry, TextAttributesSpec, TextBoxSpec, TextExtents, TextHorizontalAlignment,
|
||||
TextObjectSpec, TextShape, TextShapeGeometry, TextSpec, TextVerticalAlignment, TitleBlockInfo,
|
||||
VersionInfo,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -113,6 +113,11 @@ pub struct SelectionItemDetail {
|
|||
pub raw_len: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct CommitSession {
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct TitleBlockInfo {
|
||||
pub title: String,
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ enum Command {
|
|||
BoardOrigin {
|
||||
kind: BoardOriginKind,
|
||||
},
|
||||
BeginCommit,
|
||||
SelectionSummary,
|
||||
SelectionDetails,
|
||||
SelectionRaw,
|
||||
|
|
@ -309,6 +310,10 @@ async fn run() -> Result<(), KiCadError> {
|
|||
kind, origin.x_nm, origin.y_nm
|
||||
);
|
||||
}
|
||||
Command::BeginCommit => {
|
||||
let session = client.begin_commit().await?;
|
||||
println!("commit_id={}", session.id);
|
||||
}
|
||||
Command::SelectionSummary => {
|
||||
let summary = client.get_selection_summary().await?;
|
||||
println!("selection_total={}", summary.total_items);
|
||||
|
|
@ -761,6 +766,7 @@ fn parse_args() -> Result<(CliConfig, Command), KiCadError> {
|
|||
}
|
||||
Command::BoardOrigin { kind }
|
||||
}
|
||||
"begin-commit" => Command::BeginCommit,
|
||||
"selection-summary" => Command::SelectionSummary,
|
||||
"selection-details" => Command::SelectionDetails,
|
||||
"selection-raw" => Command::SelectionRaw,
|
||||
|
|
@ -1075,7 +1081,7 @@ fn default_config() -> CliConfig {
|
|||
|
||||
fn print_help() {
|
||||
println!(
|
||||
"kicad-ipc-cli\n\nUSAGE:\n cargo run --bin kicad-ipc-cli -- [--socket URI] [--token TOKEN] [--timeout-ms N] <command> [command options]\n\nCOMMANDS:\n ping Check IPC connectivity\n version Fetch KiCad version\n open-docs [--type <type>] List open docs (default type: pcb)\n project-path Get current project path from open PCB docs\n board-open Exit non-zero if no PCB doc is open\n net-classes List project netclass definitions\n text-variables List text variables for current board document\n expand-text-variables Expand variables in provided text values\n Options: --text <value> (repeatable)\n text-extents Measure text bounding box\n Options: --text <value>\n text-as-shapes Convert text to rendered shapes\n Options: --text <value> (repeatable)\n nets List board nets (requires one open PCB)\n netlist-pads Emit pad-level netlist data (with footprint context)\n items-by-id --id <uuid> ... Show parsed details for specific item IDs\n item-bbox --id <uuid> ... Show bounding boxes for item IDs\n hit-test --id <uuid> --x-nm <x> --y-nm <y> [--tolerance-nm <n>]\n Hit-test one item at a point\n types-pcb List PCB KiCad object type IDs from proto enum\n items-raw --type-id <id> ... Dump raw Any payloads for requested item type IDs\n items-raw-all-pcb [--debug] Dump all PCB item payloads across all PCB object types\n pad-shape-polygon --pad-id <uuid> ... --layer-id <i32> [--debug]\n Dump pad polygons on a target layer\n padstack-presence --item-id <uuid> ... --layer-id <i32> ... [--debug]\n Check padstack shape presence matrix across layers\n title-block Show title block fields\n board-as-string Dump board as KiCad s-expression text\n selection-as-string Dump current selection as KiCad s-expression text\n stackup Show typed board stackup\n graphics-defaults Show typed graphics defaults\n appearance Show typed editor appearance settings\n netclass Show typed netclass map for current board nets\n proto-coverage-board-read Print board-read command coverage vs proto\n board-read-report [--out P] Write markdown board reconstruction report\n enabled-layers List enabled board layers\n active-layer Show active board layer\n visible-layers Show currently visible board layers\n board-origin [--type <t>] Show board origin (`grid` default, or `drill`)\n selection-summary Show current selection item type counts\n selection-details Show parsed details for selected items\n selection-raw Show raw Any payload bytes for selected items\n smoke ping + version + board-open summary\n help Show help\n\nTYPES:\n schematic | symbol | pcb | footprint | drawing-sheet | project\n"
|
||||
"kicad-ipc-cli\n\nUSAGE:\n cargo run --bin kicad-ipc-cli -- [--socket URI] [--token TOKEN] [--timeout-ms N] <command> [command options]\n\nCOMMANDS:\n ping Check IPC connectivity\n version Fetch KiCad version\n open-docs [--type <type>] List open docs (default type: pcb)\n project-path Get current project path from open PCB docs\n board-open Exit non-zero if no PCB doc is open\n net-classes List project netclass definitions\n text-variables List text variables for current board document\n expand-text-variables Expand variables in provided text values\n Options: --text <value> (repeatable)\n text-extents Measure text bounding box\n Options: --text <value>\n text-as-shapes Convert text to rendered shapes\n Options: --text <value> (repeatable)\n nets List board nets (requires one open PCB)\n netlist-pads Emit pad-level netlist data (with footprint context)\n items-by-id --id <uuid> ... Show parsed details for specific item IDs\n item-bbox --id <uuid> ... Show bounding boxes for item IDs\n hit-test --id <uuid> --x-nm <x> --y-nm <y> [--tolerance-nm <n>]\n Hit-test one item at a point\n types-pcb List PCB KiCad object type IDs from proto enum\n items-raw --type-id <id> ... Dump raw Any payloads for requested item type IDs\n items-raw-all-pcb [--debug] Dump all PCB item payloads across all PCB object types\n pad-shape-polygon --pad-id <uuid> ... --layer-id <i32> [--debug]\n Dump pad polygons on a target layer\n padstack-presence --item-id <uuid> ... --layer-id <i32> ... [--debug]\n Check padstack shape presence matrix across layers\n title-block Show title block fields\n board-as-string Dump board as KiCad s-expression text\n selection-as-string Dump current selection as KiCad s-expression text\n stackup Show typed board stackup\n graphics-defaults Show typed graphics defaults\n appearance Show typed editor appearance settings\n netclass Show typed netclass map for current board nets\n proto-coverage-board-read Print board-read command coverage vs proto\n board-read-report [--out P] Write markdown board reconstruction report\n enabled-layers List enabled board layers\n active-layer Show active board layer\n visible-layers Show currently visible board layers\n board-origin [--type <t>] Show board origin (`grid` default, or `drill`)\n begin-commit Start staged commit and print commit ID\n selection-summary Show current selection item type counts\n selection-details Show parsed details for selected items\n selection-raw Show raw Any payload bytes for selected items\n smoke ping + version + board-open summary\n help Show help\n\nTYPES:\n schematic | symbol | pcb | footprint | drawing-sheet | project\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue