diff --git a/README.md b/README.md index 359f64d..b3893fd 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Commands wrapped in this crate but currently unhandled/unsupported by this KiCad Deferred manual/runtime verification (implemented after 2026-02-20 while user unavailable): - `GetKiCadBinaryPath` +- `GetPluginSettingsPath` ## KiCad v10 RC1.1 API Completion Matrix @@ -53,12 +54,12 @@ Legend: | Section | Proto Commands | Implemented | Coverage | | --- | ---: | ---: | ---: | -| Common (base) | 6 | 5 | 83% | +| Common (base) | 6 | 6 | 100% | | Common editor/document | 23 | 15 | 65% | | Project manager | 5 | 3 | 60% | | Board editor (PCB) | 22 | 20 | 91% | | Schematic editor (dedicated proto commands) | 0 | 0 | n/a | -| **Total** | **56** | **43** | **77%** | +| **Total** | **56** | **44** | **79%** | ### Common (base) @@ -69,7 +70,7 @@ Legend: | `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` | Not yet | - | +| `GetPluginSettingsPath` | Implemented | `KiCadClient::get_plugin_settings_path_raw`, `KiCadClient::get_plugin_settings_path` | ### Common editor/document diff --git a/docs/TEST_CLI.md b/docs/TEST_CLI.md index de209b1..01105b2 100644 --- a/docs/TEST_CLI.md +++ b/docs/TEST_CLI.md @@ -35,6 +35,12 @@ Resolve KiCad binary path (default `kicad-cli`): cargo run --bin kicad-ipc-cli -- kicad-binary-path --binary-name kicad-cli ``` +Resolve plugin settings path (default identifier `kicad-ipc-rust`): + +```bash +cargo run --bin kicad-ipc-cli -- plugin-settings-path --identifier kicad-ipc-rust +``` + List open PCB docs: ```bash diff --git a/src/client.rs b/src/client.rs index b0fbbf1..2bbafd1 100644 --- a/src/client.rs +++ b/src/client.rs @@ -38,6 +38,7 @@ const KICAD_API_TOKEN_ENV: &str = "KICAD_API_TOKEN"; const CMD_PING: &str = "kiapi.common.commands.Ping"; const CMD_GET_VERSION: &str = "kiapi.common.commands.GetVersion"; 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_NET_CLASSES: &str = "kiapi.common.commands.GetNetClasses"; const CMD_GET_TEXT_VARIABLES: &str = "kiapi.common.commands.GetTextVariables"; const CMD_EXPAND_TEXT_VARIABLES: &str = "kiapi.common.commands.ExpandTextVariables"; @@ -84,6 +85,7 @@ const CMD_SAVE_SELECTION_TO_STRING: &str = "kiapi.common.commands.SaveSelectionT const RES_GET_VERSION: &str = "kiapi.common.commands.GetVersionResponse"; const RES_PATH_RESPONSE: &str = "kiapi.common.commands.PathResponse"; +const RES_STRING_RESPONSE: &str = "kiapi.common.commands.StringResponse"; const RES_NET_CLASSES_RESPONSE: &str = "kiapi.common.commands.NetClassesResponse"; const RES_TEXT_VARIABLES: &str = "kiapi.common.project.TextVariables"; const RES_EXPAND_TEXT_VARIABLES_RESPONSE: &str = @@ -361,6 +363,28 @@ impl KiCadClient { Ok(response.path) } + pub async fn get_plugin_settings_path_raw( + &self, + identifier: impl Into, + ) -> Result { + let command = common_commands::GetPluginSettingsPath { + identifier: identifier.into(), + }; + let response = self + .send_command(envelope::pack_any(&command, CMD_GET_PLUGIN_SETTINGS_PATH)) + .await?; + response_payload_as_any(response, RES_STRING_RESPONSE) + } + + pub async fn get_plugin_settings_path( + &self, + identifier: impl Into, + ) -> Result { + let payload = self.get_plugin_settings_path_raw(identifier).await?; + let response: common_commands::StringResponse = decode_any(&payload, RES_STRING_RESPONSE)?; + Ok(response.response) + } + pub async fn get_open_documents( &self, document_type: DocumentType, diff --git a/test-scripts/kicad-ipc-cli.rs b/test-scripts/kicad-ipc-cli.rs index b443a75..4b27aed 100644 --- a/test-scripts/kicad-ipc-cli.rs +++ b/test-scripts/kicad-ipc-cli.rs @@ -33,6 +33,9 @@ enum Command { KiCadBinaryPath { binary_name: String, }, + PluginSettingsPath { + identifier: String, + }, OpenDocs { document_type: DocumentType, }, @@ -216,6 +219,10 @@ async fn run() -> Result<(), KiCadError> { let path = client.get_kicad_binary_path(binary_name).await?; println!("kicad_binary_path={path}"); } + Command::PluginSettingsPath { identifier } => { + let path = client.get_plugin_settings_path(identifier).await?; + println!("plugin_settings_path={path}"); + } Command::OpenDocs { document_type } => { let docs = client.get_open_documents(document_type).await?; if docs.is_empty() { @@ -844,6 +851,22 @@ fn parse_args_from(mut args: Vec) -> Result<(CliConfig, Command), KiCadE } Command::KiCadBinaryPath { binary_name } } + "plugin-settings-path" => { + let mut identifier = "kicad-ipc-rust".to_string(); + let mut i = 1; + while i < args.len() { + if args[i] == "--identifier" { + let value = args.get(i + 1).ok_or_else(|| KiCadError::Config { + reason: "missing value for plugin-settings-path --identifier".to_string(), + })?; + identifier = value.clone(); + i += 2; + continue; + } + i += 1; + } + Command::PluginSettingsPath { identifier } + } "project-path" => Command::ProjectPath, "board-open" => Command::BoardOpen, "net-classes" => Command::NetClasses, @@ -1694,7 +1717,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] [--client-name NAME] [--timeout-ms N] [command options]\n\nCOMMANDS:\n ping Check IPC connectivity\n version Fetch KiCad version\n kicad-binary-path [--binary-name ]\n Resolve absolute path for a KiCad binary (default: kicad-cli)\n open-docs [--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 (repeatable)\n text-extents Measure text bounding box\n Options: --text \n text-as-shapes Convert text to rendered shapes\n Options: --text (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 ... Show parsed details for specific item IDs\n item-bbox --id ... Show bounding boxes for item IDs\n hit-test --id --x-nm --y-nm [--tolerance-nm ]\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 ... 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 ... --layer-id [--debug]\n Dump pad polygons on a target layer\n padstack-presence --item-id ... --layer-id ... [--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 set-appearance --inactive-layer-display \n --net-color-display \n --board-flip \n --ratsnest-display \n Set editor appearance settings\n inject-drc-error --severity --message [--x-nm --y-nm ] [--item-id ...]\n Inject a DRC marker (severity: warning|error|exclusion|ignore|info|action|debug|undefined)\n refill-zones [--zone-id ...]\n Refill all zones or a provided subset\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 set-enabled-layers --copper-layer-count [--layer-id ...]\n Set enabled board layer set\n active-layer Show active board layer\n set-active-layer --layer-id \n Set active board layer\n visible-layers Show currently visible board layers\n set-visible-layers --layer-id ...\n Set visible board layers\n board-origin [--type ] Show board origin (`grid` default, or `drill`)\n set-board-origin --type --x-nm --y-nm \n Set board origin (`grid` or `drill`)\n refresh-editor [--frame ] Refresh a specific editor frame (default: pcb)\n begin-commit Start staged commit and print commit ID\n end-commit --id [--action ] [--message ]\n End staged commit with commit/drop action\n add-to-selection --id ...\n Add items to current selection\n remove-from-selection --id ...\n Remove items from current selection\n clear-selection Clear current item selection\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] [--client-name NAME] [--timeout-ms N] [command options]\n\nCOMMANDS:\n ping Check IPC connectivity\n version Fetch KiCad version\n kicad-binary-path [--binary-name ]\n Resolve absolute path for a KiCad binary (default: kicad-cli)\n plugin-settings-path [--identifier ]\n Resolve writeable plugin settings directory (default: kicad-ipc-rust)\n open-docs [--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 (repeatable)\n text-extents Measure text bounding box\n Options: --text \n text-as-shapes Convert text to rendered shapes\n Options: --text (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 ... Show parsed details for specific item IDs\n item-bbox --id ... Show bounding boxes for item IDs\n hit-test --id --x-nm --y-nm [--tolerance-nm ]\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 ... 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 ... --layer-id [--debug]\n Dump pad polygons on a target layer\n padstack-presence --item-id ... --layer-id ... [--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 set-appearance --inactive-layer-display \n --net-color-display \n --board-flip \n --ratsnest-display \n Set editor appearance settings\n inject-drc-error --severity --message [--x-nm --y-nm ] [--item-id ...]\n Inject a DRC marker (severity: warning|error|exclusion|ignore|info|action|debug|undefined)\n refill-zones [--zone-id ...]\n Refill all zones or a provided subset\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 set-enabled-layers --copper-layer-count [--layer-id ...]\n Set enabled board layer set\n active-layer Show active board layer\n set-active-layer --layer-id \n Set active board layer\n visible-layers Show currently visible board layers\n set-visible-layers --layer-id ...\n Set visible board layers\n board-origin [--type ] Show board origin (`grid` default, or `drill`)\n set-board-origin --type --x-nm --y-nm \n Set board origin (`grid` or `drill`)\n refresh-editor [--frame ] Refresh a specific editor frame (default: pcb)\n begin-commit Start staged commit and print commit ID\n end-commit --id [--action ] [--message ]\n End staged commit with commit/drop action\n add-to-selection --id ...\n Add items to current selection\n remove-from-selection --id ...\n Remove items from current selection\n clear-selection Clear current item selection\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" ); } @@ -2384,6 +2407,23 @@ mod tests { } } + #[test] + fn parse_args_parses_plugin_settings_path() { + let (_, command) = parse_args_from(vec![ + "plugin-settings-path".to_string(), + "--identifier".to_string(), + "com.example.test".to_string(), + ]) + .expect("plugin-settings-path args should parse"); + + match command { + Command::PluginSettingsPath { identifier } => { + assert_eq!(identifier, "com.example.test") + } + other => panic!("unexpected command variant: {other:?}"), + } + } + #[test] fn parse_args_parses_set_enabled_layers() { let (_, command) = parse_args_from(vec![