diff --git a/README.md b/README.md index 3b52bbd..bdd64fa 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Maintainer workflow: see `CONTRIBUTIONS.md`. ## Status -Alpha. `v0.1.1` released. +Alpha. `v0.3.0` released. - Async API (default): implemented and usable. - Sync/blocking wrapper API (`feature = "blocking"`): implemented with full async parity. @@ -23,7 +23,7 @@ Alpha. `v0.1.1` released. ```toml [dependencies] -kicad-ipc-rs = "0.1.1" +kicad-ipc-rs = "0.3.0" tokio = { version = "1", features = ["macros", "rt"] } ``` @@ -48,7 +48,7 @@ Enable the `blocking` feature and use `KiCadClientBlocking` for synchronous call ```toml [dependencies] -kicad-ipc-rs = { version = "0.1.1", features = ["blocking"] } +kicad-ipc-rs = { version = "0.3.0", features = ["blocking"] } ``` ```rust @@ -226,3 +226,9 @@ Legend: - Expand runtime + integration testing coverage. - Set up CI to run checks/tests on commits and PRs. - Continue API hardening/docs/examples for stable `1.0` path. + +## Future Work: Public Surface + Docs + +- This crate is still in alpha, and some lower-level modules currently remain public for advanced/debugging workflows. +- `#![warn(missing_docs)]` is enabled; high-impact user APIs are documented first, and remaining warnings are being burned down incrementally. +- As usage data accumulates, internal surfaces (`commands`, `envelope`, transport/proto-adjacent helpers) may be narrowed or made `pub(crate)` where possible without breaking user workflows. diff --git a/src/client.rs b/src/client.rs index 38a403b..6b7b01a 100644 --- a/src/client.rs +++ b/src/client.rs @@ -212,6 +212,10 @@ const PCB_OBJECT_TYPES: [PcbObjectTypeCode; 18] = [ ]; #[derive(Clone, Debug)] +/// Async IPC client for communicating with a running KiCad instance. +/// +/// Create with [`KiCadClient::connect`] for defaults or [`KiCadClient::builder`] +/// to override socket path, timeout, token, or client name. pub struct KiCadClient { inner: Arc, } @@ -234,11 +238,19 @@ struct ClientConfig { } #[derive(Clone, Debug)] +/// Builder for [`KiCadClient`]. +/// +/// Defaults: +/// - timeout: `3s` +/// - socket path: `KICAD_API_SOCKET` env var, then platform default +/// - token: `KICAD_API_TOKEN` env var, then empty +/// - client name: autogenerated pub struct ClientBuilder { config: ClientConfig, } impl ClientBuilder { + /// Creates a builder with sensible defaults for local KiCad IPC usage. pub fn new() -> Self { Self { config: ClientConfig { @@ -250,26 +262,39 @@ impl ClientBuilder { } } + /// Sets per-request timeout used by the IPC transport. pub fn timeout(mut self, timeout: Duration) -> Self { self.config.timeout = timeout; self } + /// Sets explicit KiCad IPC socket URI/path. + /// + /// If unset, the builder resolves from environment/defaults. pub fn socket_path(mut self, socket_path: impl Into) -> Self { self.config.socket_uri = Some(socket_path.into()); self } + /// Sets the IPC authentication token. + /// + /// If unset, the builder uses `KICAD_API_TOKEN` when present. pub fn token(mut self, token: impl Into) -> Self { self.config.token = Some(token.into()); self } + /// Sets the client name reported to KiCad. pub fn client_name(mut self, client_name: impl Into) -> Self { self.config.client_name = Some(client_name.into()); self } + /// Connects to KiCad IPC with the configured options. + /// + /// # Errors + /// Returns [`KiCadError`] when socket discovery, connection, or transport + /// initialization fails. pub async fn connect(self) -> Result { let socket_uri = resolve_socket_uri(self.config.socket_uri.as_deref()); if is_missing_ipc_socket(&socket_uri) { @@ -306,28 +331,34 @@ impl Default for ClientBuilder { } impl KiCadClient { + /// Returns a configurable builder for creating a [`KiCadClient`]. pub fn builder() -> ClientBuilder { ClientBuilder::new() } + /// Connects with default builder settings. pub async fn connect() -> Result { ClientBuilder::new().connect().await } + /// Returns configured per-request timeout. pub fn timeout(&self) -> Duration { self.inner.timeout } + /// Returns resolved KiCad IPC socket URI/path. pub fn socket_uri(&self) -> &str { &self.inner.socket_uri } + /// Sends a health-check request to KiCad. pub async fn ping(&self) -> Result<(), KiCadError> { let command = envelope::pack_any(&common_commands::Ping {}, CMD_PING); self.send_command(command).await?; Ok(()) } + /// Requests KiCad to refresh a specific editor frame. pub async fn refresh_editor(&self, frame: EditorFrameType) -> Result<(), KiCadError> { let command = envelope::pack_any( &common_commands::RefreshEditor { @@ -352,6 +383,7 @@ impl KiCadClient { response_payload_as_any(response, RES_RUN_ACTION_RESPONSE) } + /// Runs a KiCad action by action name and returns mapped status. pub async fn run_action( &self, action: impl Into, @@ -362,6 +394,7 @@ impl KiCadClient { Ok(map_run_action_status(response.status)) } + /// Queries KiCad version info for the connected instance. pub async fn get_version(&self) -> Result { let command = envelope::pack_any(&common_commands::GetVersion {}, CMD_GET_VERSION); let response = self.send_command(command).await?; @@ -394,6 +427,7 @@ impl KiCadClient { response_payload_as_any(response, RES_PATH_RESPONSE) } + /// Resolves a KiCad binary path by binary name. pub async fn get_kicad_binary_path( &self, binary_name: impl Into, @@ -416,6 +450,7 @@ impl KiCadClient { response_payload_as_any(response, RES_STRING_RESPONSE) } + /// Resolves plugin settings path for a plugin identifier. pub async fn get_plugin_settings_path( &self, identifier: impl Into, @@ -425,6 +460,7 @@ impl KiCadClient { Ok(response.response) } + /// Lists open KiCad documents of the requested type. pub async fn get_open_documents( &self, document_type: DocumentType, @@ -455,6 +491,7 @@ impl KiCadClient { response_payload_as_any(response, RES_NET_CLASSES_RESPONSE) } + /// Reads project net classes from the current project context. pub async fn get_net_classes(&self) -> Result, KiCadError> { let payload = self.get_net_classes_raw().await?; let response: common_commands::NetClassesResponse = @@ -487,6 +524,7 @@ impl KiCadClient { response_payload_as_any(response, RES_PROTOBUF_EMPTY) } + /// Replaces or merges project net classes, then returns current classes. pub async fn set_net_classes( &self, net_classes: Vec, @@ -506,6 +544,7 @@ impl KiCadClient { response_payload_as_any(response, RES_TEXT_VARIABLES) } + /// Reads project text variables. pub async fn get_text_variables(&self) -> Result, KiCadError> { let payload = self.get_text_variables_raw().await?; let response: common_project::TextVariables = decode_any(&payload, RES_TEXT_VARIABLES)?; @@ -530,6 +569,7 @@ impl KiCadClient { response_payload_as_any(response, RES_PROTOBUF_EMPTY) } + /// Replaces or merges project text variables, then returns current values. pub async fn set_text_variables( &self, variables: BTreeMap, @@ -553,6 +593,7 @@ impl KiCadClient { response_payload_as_any(response, RES_EXPAND_TEXT_VARIABLES_RESPONSE) } + /// Expands `${VAR}`-style text variables using current project context. pub async fn expand_text_variables( &self, text: Vec, @@ -576,6 +617,7 @@ impl KiCadClient { response_payload_as_any(response, RES_BOX2) } + /// Computes rendered text extents in nanometer units. pub async fn get_text_extents(&self, text: TextSpec) -> Result { let payload = self.get_text_extents_raw(text).await?; let response: common_types::Box2 = decode_any(&payload, RES_BOX2)?; @@ -609,6 +651,7 @@ impl KiCadClient { response_payload_as_any(response, RES_GET_TEXT_AS_SHAPES_RESPONSE) } + /// Converts text/textbox specs into drawable shape geometry. pub async fn get_text_as_shapes( &self, text: Vec, @@ -624,11 +667,15 @@ impl KiCadClient { .collect() } + /// Returns the current PCB project's path. + /// + /// Fails if no PCB is open or if multiple project paths are present. pub async fn get_current_project_path(&self) -> Result { let docs = self.get_open_documents(DocumentType::Pcb).await?; select_single_project_path(&docs) } + /// Returns `true` when at least one PCB document is open in KiCad. pub async fn has_open_board(&self) -> Result { let docs = self.get_open_documents(DocumentType::Pcb).await?; Ok(!docs.is_empty()) @@ -642,6 +689,7 @@ impl KiCadClient { response_payload_as_any(response, RES_BEGIN_COMMIT_RESPONSE) } + /// Starts a KiCad commit session used for grouped board edits. pub async fn begin_commit(&self) -> Result { let payload = self.begin_commit_raw().await?; let response: common_commands::BeginCommitResponse = @@ -672,6 +720,7 @@ impl KiCadClient { response_payload_as_any(response, RES_END_COMMIT_RESPONSE) } + /// Finalizes a commit session, either committing or dropping staged changes. pub async fn end_commit( &self, session: CommitSession, @@ -699,6 +748,9 @@ impl KiCadClient { response_payload_as_any(response, RES_CREATE_ITEMS_RESPONSE) } + /// Creates items in the active PCB document. + /// + /// Returns created items as raw protobuf `Any` payloads. pub async fn create_items( &self, items: Vec, @@ -736,6 +788,9 @@ impl KiCadClient { response_payload_as_any(response, RES_UPDATE_ITEMS_RESPONSE) } + /// Updates existing items in the active PCB document. + /// + /// Returns updated items as raw protobuf `Any` payloads. pub async fn update_items( &self, items: Vec, @@ -775,6 +830,9 @@ impl KiCadClient { response_payload_as_any(response, RES_DELETE_ITEMS_RESPONSE) } + /// Deletes items by id from the active PCB document. + /// + /// Returns ids of items deleted by KiCad. pub async fn delete_items(&self, item_ids: Vec) -> Result, KiCadError> { let payload = self.delete_items_raw(item_ids).await?; let response: common_commands::DeleteItemsResponse = @@ -836,6 +894,7 @@ impl KiCadClient { .collect() } + /// Returns nets from the active PCB document. pub async fn get_nets(&self) -> Result, KiCadError> { let board = self.current_board_document_proto().await?; let command = board_commands::GetNets { @@ -981,6 +1040,7 @@ impl KiCadClient { Ok(()) } + /// Returns a compact summary of the current PCB selection. pub async fn get_selection_summary(&self) -> Result { let document = self.current_board_document_proto().await?; let command = common_commands::GetSelection { @@ -1023,6 +1083,7 @@ impl KiCadClient { summarize_item_details(items) } + /// Returns the current selection as decoded typed PCB items. pub async fn get_selection(&self) -> Result, KiCadError> { let items = self.get_selection_raw().await?; decode_pcb_items(items) @@ -1154,10 +1215,12 @@ impl KiCadClient { .collect()) } + /// Returns known KiCad PCB object type codes handled by this crate. pub fn pcb_object_type_codes() -> &'static [PcbObjectTypeCode] { &PCB_OBJECT_TYPES } + /// Resolves a human-readable object type name from a KiCad object type code. pub fn pcb_object_type_name(type_code: i32) -> Option<&'static str> { PCB_OBJECT_TYPES .iter() @@ -1165,6 +1228,7 @@ impl KiCadClient { .map(|entry| entry.name) } + /// Formats a raw protobuf PCB item payload for debugging/logging. pub fn debug_any_item(item: &prost_types::Any) -> Result { any_to_pretty_debug(item) } @@ -1184,6 +1248,7 @@ impl KiCadClient { summarize_item_details(items) } + /// Fetches and decodes items by KiCad object type codes. pub async fn get_items_by_type_codes( &self, type_codes: Vec, @@ -1216,6 +1281,7 @@ impl KiCadClient { Ok(rows) } + /// Fetches all known PCB item kinds and decodes each bucket. pub async fn get_all_pcb_items( &self, ) -> Result)>, KiCadError> { @@ -1537,6 +1603,7 @@ impl KiCadClient { response_payload_as_any(response, RES_BOARD_STACKUP_RESPONSE) } + /// Reads board stackup from the active PCB document. pub async fn get_board_stackup(&self) -> Result { let payload = self.get_board_stackup_raw().await?; let response: board_commands::BoardStackupResponse = @@ -1560,6 +1627,7 @@ impl KiCadClient { response_payload_as_any(response, RES_BOARD_STACKUP_RESPONSE) } + /// Writes a board stackup and returns KiCad's resulting stackup state. pub async fn update_board_stackup( &self, stackup: BoardStackup, @@ -1660,6 +1728,7 @@ impl KiCadClient { Ok(()) } + /// Reads title block metadata from the active PCB document. pub async fn get_title_block_info(&self) -> Result { let command = common_commands::GetTitleBlockInfo { document: Some(self.current_board_document_proto().await?), @@ -1760,6 +1829,7 @@ impl KiCadClient { Ok(()) } + /// Serializes the active PCB document to KiCad's string format. pub async fn get_board_as_string(&self) -> Result { let command = common_commands::SaveDocumentToString { document: Some(self.current_board_document_proto().await?), @@ -1773,6 +1843,7 @@ impl KiCadClient { Ok(payload.contents) } + /// Serializes current selection to KiCad's string format. pub async fn get_selection_as_string(&self) -> Result { let command = common_commands::SaveSelectionToString {}; @@ -1819,6 +1890,7 @@ impl KiCadClient { summarize_item_details(items) } + /// Fetches and decodes items by KiCad item id. pub async fn get_items_by_id(&self, item_ids: Vec) -> Result, KiCadError> { let items = self.get_items_by_id_raw(item_ids).await?; decode_pcb_items(items) diff --git a/src/error.rs b/src/error.rs index e35530f..954472c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,67 +3,88 @@ use std::time::Duration; use thiserror::Error; #[derive(Debug, Error)] +/// Error type returned by `kicad-ipc-rs` operations. pub enum KiCadError { + /// Invalid local configuration or user input before IPC dispatch. #[error("invalid configuration: {reason}")] Config { reason: String }, + /// KiCad IPC socket could not be found at connect time. #[error("KiCad IPC socket not available at `{socket_uri}`. Open KiCad and open a project/board first.")] SocketUnavailable { socket_uri: String }, + /// IPC connection failed. #[error("connection failed for `{socket_uri}`: {reason}")] Connection { socket_uri: String, reason: String }, + /// Transport send path failed. #[error("transport send failed: {reason}")] TransportSend { reason: String }, + /// Transport receive path failed. #[error("transport receive failed: {reason}")] TransportReceive { reason: String }, + /// Background transport task has stopped. #[error("transport task is unavailable")] TransportClosed, + /// Request exceeded configured timeout. #[error("request timed out after {timeout:?}")] Timeout { timeout: Duration }, + /// KiCad returned a non-success API status. #[error("API status error `{code}`: {message}")] ApiStatus { code: String, message: String }, + /// KiCad returned a non-success per-item status. #[error("item request status error `{code}`")] ItemStatus { code: String }, + /// Response payload content was malformed or inconsistent. #[error("invalid API response: {reason}")] InvalidResponse { reason: String }, + /// Response payload was missing when required. #[error("API response missing payload for `{expected_type_url}`")] MissingPayload { expected_type_url: String }, + /// Response payload type did not match expected protobuf type URL. #[error("unexpected payload type; expected `{expected_type_url}`, got `{actual_type_url}`")] UnexpectedPayloadType { expected_type_url: String, actual_type_url: String, }, + /// Protobuf encoding failed. #[error("protobuf encode failed: {0}")] ProtobufEncode(String), + /// Protobuf decoding failed. #[error("protobuf decode failed: {0}")] ProtobufDecode(String), + /// Blocking runtime worker join failed. #[error("runtime task join failed: {0}")] RuntimeJoin(String), + /// Blocking runtime worker is unavailable. #[error("blocking runtime is unavailable")] BlockingRuntimeClosed, + /// Internal mutex poisoning detected. #[error("mutex poisoned")] InternalPoisoned, + /// Operation requires an open PCB document. #[error("no open PCB document found; open a board in KiCad first")] BoardNotOpen, + /// Multiple project paths were detected where a single path was required. #[error("multiple project paths found across open PCB docs: {paths:?}")] AmbiguousProjectPath { paths: Vec }, + /// Multiple open PCB docs prevent choosing an implicit board context. #[error("multiple PCB documents are open; unable to choose one board context: {boards:?}")] AmbiguousBoardSelection { boards: Vec }, } diff --git a/src/lib.rs b/src/lib.rs index e5faea6..357995d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,20 +1,82 @@ -//! Async-first Rust bindings for the KiCad IPC API. +//! # KiCad IPC RS //! -//! Layering: +//! **Async-first, pure-Rust IPC bindings for KiCad's official API.** +//! Production-focused Rust API surface, typed models, and a blocking wrapper for sync callers. +//! +//! ## Why this crate? +//! +//! | Capability | `kicad-ipc-rs` | Official Python bindings (`kicad-python`) | Official Rust bindings (`kicad-rs`) | +//! | --- | --- | --- | --- | +//! | Rust-native client API | ✅ Yes | ❌ Python package | ⚠️ Development preview | +//! | Async-first API design | ✅ `KiCadClient` | ⚠️ App-managed event-loop model | ⚠️ Development preview | +//! | Blocking support for sync apps | ✅ `feature = "blocking"` | ✅ Native Python sync usage | ⚠️ Development preview | +//! | Wrapped KiCad command coverage (current proto snapshot) | ✅ 56/56 command wrappers | Unknown | Unknown | +//! | Maintainer focus | ✅ This crate is actively maintained for Rust users | ✅ Official KiCad Python package | ⚠️ Preview status | +//! +//! Evidence and references: +//! - `kicad-python` package: +//! - `kicad-rs` package (states "development preview with no docs yet"): +//! - Coverage matrix and runtime notes: +//! +//! ## Quickstart (async) +//! +//! ```no_run +//! use kicad_ipc_rs::KiCadClient; +//! +//! #[tokio::main(flavor = "current_thread")] +//! async fn main() -> Result<(), kicad_ipc_rs::KiCadError> { +//! let client = KiCadClient::connect().await?; +//! client.ping().await?; +//! let version = client.get_version().await?; +//! println!("KiCad: {}", version.full_version); +//! Ok(()) +//! } +//! ``` +//! +//! ## Quickstart (blocking) +//! +//! ```no_run +//! # #[cfg(feature = "blocking")] +//! # fn run() -> Result<(), kicad_ipc_rs::KiCadError> { +//! use kicad_ipc_rs::KiCadClientBlocking; +//! let client = KiCadClientBlocking::connect()?; +//! let version = client.get_version()?; +//! println!("KiCad: {}", version.full_version); +//! # Ok(()) +//! # } +//! ``` +//! +//! Architecture layers: //! - transport //! - envelope //! - command builders //! - high-level client +#![warn(missing_docs)] + +/// High-level async client and request/response convenience methods. pub mod client; +/// Low-level command payload builders. +/// +/// This module is public for advanced integrations and debugging, but most users +/// should prefer [`crate::client::KiCadClient`] methods. pub mod commands; +/// Envelope helpers for command/response packing and unpacking. +/// +/// This is primarily an advanced/internal surface. pub mod envelope; +/// Error types returned by this crate. pub mod error; mod kicad_api_version; +/// Stable data models used by typed client APIs. pub mod model; +/// IPC transport implementation details. +/// +/// Most applications should not need to use this module directly. pub mod transport; #[cfg(feature = "blocking")] +/// Blocking wrapper over the async client. pub mod blocking; pub(crate) mod proto; diff --git a/src/model/board.rs b/src/model/board.rs index a78dec6..20ee55d 100644 --- a/src/model/board.rs +++ b/src/model/board.rs @@ -1,26 +1,38 @@ use std::str::FromStr; #[derive(Clone, Debug, Eq, PartialEq)] +/// KiCad net descriptor. pub struct BoardNet { + /// Numeric net code. pub code: i32, + /// Net name. pub name: String, } #[derive(Clone, Debug, Eq, PartialEq)] +/// Board layer descriptor. pub struct BoardLayerInfo { + /// KiCad layer id. pub id: i32, + /// Human-readable layer name. pub name: String, } #[derive(Clone, Debug, Eq, PartialEq)] +/// Enabled layer set for a board. pub struct BoardEnabledLayers { + /// Number of copper layers configured in the board stack. pub copper_layer_count: u32, + /// Enabled board layers. pub layers: Vec, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] +/// Board origin kind. pub enum BoardOriginKind { + /// Grid origin. Grid, + /// Drill/place origin. Drill, } @@ -48,43 +60,66 @@ impl std::fmt::Display for BoardOriginKind { } #[derive(Clone, Copy, Debug, Eq, PartialEq)] +/// 2D coordinate in nanometer units. pub struct Vector2Nm { + /// X coordinate in nm. pub x_nm: i64, + /// Y coordinate in nm. pub y_nm: i64, } #[derive(Clone, Debug, Eq, PartialEq)] +/// Pad-to-net lookup row derived from footprint items. pub struct PadNetEntry { + /// Footprint reference (e.g. `U1`) when available. pub footprint_reference: Option, + /// Footprint id when available. pub footprint_id: Option, + /// Pad item id when available. pub pad_id: Option, + /// Pad number/text as shown in KiCad. pub pad_number: String, + /// Net code when connected. pub net_code: Option, + /// Net name when connected. pub net_name: Option, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] +/// Arc geometry in nanometer units. pub struct ArcStartMidEndNm { + /// Arc start point. pub start: Vector2Nm, + /// Arc midpoint. pub mid: Vector2Nm, + /// Arc end point. pub end: Vector2Nm, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] +/// Polyline node geometry. pub enum PolyLineNodeGeometryNm { + /// Straight segment point. Point(Vector2Nm), + /// Arc segment node. Arc(ArcStartMidEndNm), } #[derive(Clone, Debug, Eq, PartialEq)] +/// Polyline geometry. pub struct PolyLineNm { + /// Ordered geometry nodes. pub nodes: Vec, + /// Whether last node closes back to first. pub closed: bool, } #[derive(Clone, Debug, Eq, PartialEq)] +/// Polygon with optional interior holes. pub struct PolygonWithHolesNm { + /// Outer outline polygon. pub outline: Option, + /// Interior holes. pub holes: Vec, } diff --git a/src/model/common.rs b/src/model/common.rs index 745b0e3..efc38d9 100644 --- a/src/model/common.rs +++ b/src/model/common.rs @@ -5,21 +5,34 @@ use crate::model::board::{ColorRgba, PolygonWithHolesNm, Vector2Nm}; use crate::proto::kiapi::common::types as common_types; #[derive(Clone, Debug, Eq, PartialEq)] +/// KiCad semantic version returned by `GetVersion`. pub struct VersionInfo { + /// Major version component. pub major: u32, + /// Minor version component. pub minor: u32, + /// Patch version component. pub patch: u32, + /// Full KiCad version string (includes prerelease/build details). pub full_version: String, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] +/// KiCad top-level frame/editor targets used by API commands. pub enum EditorFrameType { + /// KiCad project manager frame. ProjectManager, + /// Schematic editor frame. SchematicEditor, + /// PCB editor frame. PcbEditor, + /// Spice simulator frame. SpiceSimulator, + /// Symbol editor frame. SymbolEditor, + /// Footprint editor frame. FootprintEditor, + /// Drawing-sheet editor frame. DrawingSheetEditor, } @@ -72,12 +85,19 @@ impl FromStr for EditorFrameType { } #[derive(Clone, Copy, Debug, Eq, PartialEq)] +/// KiCad document type selector used by document-scoped APIs. pub enum DocumentType { + /// Schematic document. Schematic, + /// Symbol document. Symbol, + /// PCB document. Pcb, + /// Footprint document. Footprint, + /// Drawing-sheet document. DrawingSheet, + /// Project-level document. Project, } @@ -141,59 +161,89 @@ impl FromStr for DocumentType { } #[derive(Clone, Debug, Eq, PartialEq)] +/// Minimal project information attached to open-document responses. pub struct ProjectInfo { + /// Project display name, if provided by KiCad. pub name: Option, + /// Project filesystem path, if available. pub path: Option, } #[derive(Clone, Debug, Eq, PartialEq)] +/// Descriptor for an open KiCad document. pub struct DocumentSpecifier { + /// KiCad document type. pub document_type: DocumentType, + /// Board filename when relevant. pub board_filename: Option, + /// Owning project metadata. pub project: ProjectInfo, } #[derive(Clone, Debug, Eq, PartialEq)] +/// Count of selected items for a specific protobuf type URL. pub struct SelectionTypeCount { + /// Protobuf type URL for the selected item type. pub type_url: String, + /// Number of selected items of this type. pub count: usize, } #[derive(Clone, Debug, Eq, PartialEq)] +/// Summary of current selection composition. pub struct SelectionSummary { + /// Total selected item count. pub total_items: usize, + /// Per-type counts by protobuf type URL. pub type_url_counts: Vec, } #[derive(Clone, Debug, Eq, PartialEq)] +/// Human/debug-friendly selection entry detail. pub struct SelectionItemDetail { + /// Protobuf type URL. pub type_url: String, + /// Decoded/debug string detail. pub detail: String, + /// Raw payload length in bytes. pub raw_len: usize, } #[derive(Clone, Debug, Eq, PartialEq)] +/// Opaque commit session identifier returned by `begin_commit`. pub struct CommitSession { + /// KiCad commit session id. pub id: String, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] +/// Final action to apply when ending a commit session. pub enum CommitAction { + /// Persist commit changes. Commit, + /// Discard commit changes. Drop, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] +/// Status result returned by `run_action`. pub enum RunActionStatus { + /// Action succeeded. Ok, + /// Action name or payload was invalid. Invalid, + /// Target editor frame was not open. FrameNotOpen, + /// Unrecognized status code from KiCad. Unknown(i32), } #[derive(Clone, Copy, Debug, Eq, PartialEq)] +/// Merge strategy for map-like update APIs. pub enum MapMergeMode { + /// Merge provided entries into existing map. Merge, + /// Replace existing map with provided entries. Replace, } @@ -244,11 +294,17 @@ impl FromStr for CommitAction { } #[derive(Clone, Debug, Eq, PartialEq)] +/// Title block fields from the active document. pub struct TitleBlockInfo { + /// Title block title. pub title: String, + /// Title block date. pub date: String, + /// Revision string. pub revision: String, + /// Company field. pub company: String, + /// Non-empty comment fields. pub comments: Vec, }