From 0e8217fd8fb8a0c84c1a220cab645142dbc9bc51 Mon Sep 17 00:00:00 2001 From: Milind Sharma Date: Fri, 20 Feb 2026 18:48:38 +0800 Subject: [PATCH] feat(project): add SetTextVariables API and CLI command --- README.md | 7 ++-- docs/TEST_CLI.md | 6 +++ src/client.rs | 28 +++++++++++++ test-scripts/kicad-ipc-cli.rs | 77 ++++++++++++++++++++++++++++++++++- 4 files changed, 114 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e684c5b..3426aa9 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Deferred manual/runtime verification (implemented after 2026-02-20 while user un - `DeleteItems` - `ParseAndCreateItemsFromString` - `SetNetClasses` +- `SetTextVariables` ## KiCad v10 RC1.1 API Completion Matrix @@ -65,10 +66,10 @@ Legend: | --- | ---: | ---: | ---: | | Common (base) | 6 | 6 | 100% | | Common editor/document | 23 | 23 | 100% | -| Project manager | 5 | 4 | 80% | +| Project manager | 5 | 5 | 100% | | Board editor (PCB) | 22 | 20 | 91% | | Schematic editor (dedicated proto commands) | 0 | 0 | n/a | -| **Total** | **56** | **53** | **95%** | +| **Total** | **56** | **54** | **96%** | ### Common (base) @@ -117,7 +118,7 @@ Legend: | `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` | Not yet | - | +| `SetTextVariables` | Implemented | `KiCadClient::set_text_variables_raw`, `KiCadClient::set_text_variables` | ### Board editor (PCB) diff --git a/docs/TEST_CLI.md b/docs/TEST_CLI.md index 5370e61..f4a013c 100644 --- a/docs/TEST_CLI.md +++ b/docs/TEST_CLI.md @@ -77,6 +77,12 @@ List text variables for current board document: cargo run --bin kicad-ipc-cli -- text-variables ``` +Set text variables: + +```bash +cargo run --bin kicad-ipc-cli -- set-text-variables --merge-mode merge --var REV=A +``` + Expand text variables in one or more input strings: ```bash diff --git a/src/client.rs b/src/client.rs index 5f63cd4..9cc6609 100644 --- a/src/client.rs +++ b/src/client.rs @@ -42,6 +42,7 @@ const CMD_GET_PLUGIN_SETTINGS_PATH: &str = "kiapi.common.commands.GetPluginSetti 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_SET_TEXT_VARIABLES: &str = "kiapi.common.commands.SetTextVariables"; const CMD_EXPAND_TEXT_VARIABLES: &str = "kiapi.common.commands.ExpandTextVariables"; const CMD_GET_TEXT_EXTENTS: &str = "kiapi.common.commands.GetTextExtents"; const CMD_GET_TEXT_AS_SHAPES: &str = "kiapi.common.commands.GetTextAsShapes"; @@ -509,6 +510,33 @@ impl KiCadClient { Ok(response.variables.into_iter().collect()) } + pub async fn set_text_variables_raw( + &self, + variables: BTreeMap, + merge_mode: MapMergeMode, + ) -> Result { + let command = common_commands::SetTextVariables { + document: Some(self.current_board_document_proto().await?), + variables: Some(common_project::TextVariables { + variables: variables.into_iter().collect(), + }), + merge_mode: map_merge_mode_to_proto(merge_mode), + }; + let response = self + .send_command(envelope::pack_any(&command, CMD_SET_TEXT_VARIABLES)) + .await?; + response_payload_as_any(response, RES_PROTOBUF_EMPTY) + } + + pub async fn set_text_variables( + &self, + variables: BTreeMap, + merge_mode: MapMergeMode, + ) -> Result, KiCadError> { + let _ = self.set_text_variables_raw(variables, merge_mode).await?; + self.get_text_variables().await + } + pub async fn expand_text_variables_raw( &self, text: Vec, diff --git a/test-scripts/kicad-ipc-cli.rs b/test-scripts/kicad-ipc-cli.rs index bfabeec..f9fd45d 100644 --- a/test-scripts/kicad-ipc-cli.rs +++ b/test-scripts/kicad-ipc-cli.rs @@ -46,6 +46,10 @@ enum Command { merge_mode: MapMergeMode, }, TextVariables, + SetTextVariables { + merge_mode: MapMergeMode, + variables: BTreeMap, + }, ExpandTextVariables { text: Vec, }, @@ -315,6 +319,20 @@ async fn run() -> Result<(), KiCadError> { println!("name={} value={}", name, value); } } + Command::SetTextVariables { + merge_mode, + variables, + } => { + let updated = client.set_text_variables(variables, merge_mode).await?; + println!( + "text_variable_count={} merge_mode={}", + updated.len(), + merge_mode + ); + for (name, value) in updated { + println!("name={} value={}", name, value); + } + } Command::ExpandTextVariables { text } => { let expanded = client.expand_text_variables(text.clone()).await?; println!("expanded_count={}", expanded.len()); @@ -991,6 +1009,40 @@ fn parse_args_from(mut args: Vec) -> Result<(CliConfig, Command), KiCadE Command::SetNetClasses { merge_mode } } "text-variables" => Command::TextVariables, + "set-text-variables" => { + let mut merge_mode = MapMergeMode::Merge; + let mut variables = BTreeMap::new(); + let mut i = 1; + while i < args.len() { + match args[i].as_str() { + "--merge-mode" => { + let value = args.get(i + 1).ok_or_else(|| KiCadError::Config { + reason: "missing value for set-text-variables --merge-mode".to_string(), + })?; + merge_mode = MapMergeMode::from_str(value) + .map_err(|reason| KiCadError::Config { reason })?; + i += 2; + } + "--var" => { + let value = args.get(i + 1).ok_or_else(|| KiCadError::Config { + reason: "missing value for set-text-variables --var".to_string(), + })?; + let (name, text) = + value.split_once('=').ok_or_else(|| KiCadError::Config { + reason: "set-text-variables --var requires `=`" + .to_string(), + })?; + variables.insert(name.to_string(), text.to_string()); + i += 2; + } + _ => i += 1, + } + } + Command::SetTextVariables { + merge_mode, + variables, + } + } "expand-text-variables" => { let mut text = Vec::new(); let mut i = 1; @@ -1994,7 +2046,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 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 set-net-classes [--merge-mode ]\n Write current netclass set back with selected merge mode\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 save-doc Save current board document\n save-copy --path [--overwrite] [--include-project]\n Save current board document to a new location\n revert-doc Revert current board document from disk\n run-action --action Run a raw KiCad tool action\n create-items --item = ... [--container-id ]\n Create raw Any payload items in current board document\n update-items --item = ...\n Update raw Any payload items in current board document\n delete-items --id ...\n Delete item IDs from current board document\n parse-create-items --contents \n Parse s-expression and create resulting items\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 set-net-classes [--merge-mode ]\n Write current netclass set back with selected merge mode\n text-variables List text variables for current board document\n set-text-variables [--merge-mode ] [--var ...]\n Set 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 save-doc Save current board document\n save-copy --path [--overwrite] [--include-project]\n Save current board document to a new location\n revert-doc Revert current board document from disk\n run-action --action Run a raw KiCad tool action\n create-items --item = ... [--container-id ]\n Create raw Any payload items in current board document\n update-items --item = ...\n Update raw Any payload items in current board document\n delete-items --id ...\n Delete item IDs from current board document\n parse-create-items --contents \n Parse s-expression and create resulting items\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" ); } @@ -2745,6 +2797,29 @@ mod tests { } } + #[test] + fn parse_args_parses_set_text_variables() { + let (_, command) = parse_args_from(vec![ + "set-text-variables".to_string(), + "--merge-mode".to_string(), + "replace".to_string(), + "--var".to_string(), + "REV=A".to_string(), + ]) + .expect("set-text-variables args should parse"); + + match command { + Command::SetTextVariables { + merge_mode, + variables, + } => { + assert_eq!(merge_mode, kicad_ipc::MapMergeMode::Replace); + assert_eq!(variables.get("REV").map(|value| value.as_str()), Some("A")); + } + other => panic!("unexpected command variant: {other:?}"), + } + } + #[test] fn parse_args_parses_save_doc() { let (_, command) =