Skip to main content

kicad_ipc_rs/model/
common.rs

1use std::path::PathBuf;
2use std::str::FromStr;
3
4use crate::model::board::{ColorRgba, PolygonWithHolesNm, Vector2Nm};
5use crate::proto::kiapi::common::types as common_types;
6
7#[derive(Clone, Debug, Eq, PartialEq)]
8/// KiCad semantic version returned by `GetVersion`.
9pub struct VersionInfo {
10    /// Major version component.
11    pub major: u32,
12    /// Minor version component.
13    pub minor: u32,
14    /// Patch version component.
15    pub patch: u32,
16    /// Full KiCad version string (includes prerelease/build details).
17    pub full_version: String,
18}
19
20#[derive(Clone, Copy, Debug, Eq, PartialEq)]
21/// KiCad top-level frame/editor targets used by API commands.
22pub enum EditorFrameType {
23    /// KiCad project manager frame.
24    ProjectManager,
25    /// Schematic editor frame.
26    SchematicEditor,
27    /// PCB editor frame.
28    PcbEditor,
29    /// Spice simulator frame.
30    SpiceSimulator,
31    /// Symbol editor frame.
32    SymbolEditor,
33    /// Footprint editor frame.
34    FootprintEditor,
35    /// Drawing-sheet editor frame.
36    DrawingSheetEditor,
37}
38
39impl EditorFrameType {
40    pub(crate) fn to_proto(self) -> i32 {
41        match self {
42            Self::ProjectManager => common_types::FrameType::FtProjectManager as i32,
43            Self::SchematicEditor => common_types::FrameType::FtSchematicEditor as i32,
44            Self::PcbEditor => common_types::FrameType::FtPcbEditor as i32,
45            Self::SpiceSimulator => common_types::FrameType::FtSpiceSimulator as i32,
46            Self::SymbolEditor => common_types::FrameType::FtSymbolEditor as i32,
47            Self::FootprintEditor => common_types::FrameType::FtFootprintEditor as i32,
48            Self::DrawingSheetEditor => common_types::FrameType::FtDrawingSheetEditor as i32,
49        }
50    }
51}
52
53impl std::fmt::Display for EditorFrameType {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        let value = match self {
56            Self::ProjectManager => "project-manager",
57            Self::SchematicEditor => "schematic",
58            Self::PcbEditor => "pcb",
59            Self::SpiceSimulator => "spice",
60            Self::SymbolEditor => "symbol",
61            Self::FootprintEditor => "footprint",
62            Self::DrawingSheetEditor => "drawing-sheet",
63        };
64        write!(f, "{value}")
65    }
66}
67
68impl FromStr for EditorFrameType {
69    type Err = String;
70
71    fn from_str(value: &str) -> Result<Self, Self::Err> {
72        match value {
73            "project-manager" => Ok(Self::ProjectManager),
74            "schematic" => Ok(Self::SchematicEditor),
75            "pcb" => Ok(Self::PcbEditor),
76            "spice" => Ok(Self::SpiceSimulator),
77            "symbol" => Ok(Self::SymbolEditor),
78            "footprint" => Ok(Self::FootprintEditor),
79            "drawing-sheet" => Ok(Self::DrawingSheetEditor),
80            _ => Err(format!(
81                "unknown frame `{value}`; expected one of: project-manager, schematic, pcb, spice, symbol, footprint, drawing-sheet"
82            )),
83        }
84    }
85}
86
87#[derive(Clone, Copy, Debug, Eq, PartialEq)]
88/// KiCad document type selector used by document-scoped APIs.
89pub enum DocumentType {
90    /// Schematic document.
91    Schematic,
92    /// Symbol document.
93    Symbol,
94    /// PCB document.
95    Pcb,
96    /// Footprint document.
97    Footprint,
98    /// Drawing-sheet document.
99    DrawingSheet,
100    /// Project-level document.
101    Project,
102}
103
104impl DocumentType {
105    pub(crate) fn to_proto(self) -> i32 {
106        match self {
107            Self::Schematic => common_types::DocumentType::DoctypeSchematic as i32,
108            Self::Symbol => common_types::DocumentType::DoctypeSymbol as i32,
109            Self::Pcb => common_types::DocumentType::DoctypePcb as i32,
110            Self::Footprint => common_types::DocumentType::DoctypeFootprint as i32,
111            Self::DrawingSheet => common_types::DocumentType::DoctypeDrawingSheet as i32,
112            Self::Project => common_types::DocumentType::DoctypeProject as i32,
113        }
114    }
115
116    pub(crate) fn from_proto(value: i32) -> Option<Self> {
117        let ty = common_types::DocumentType::try_from(value).ok()?;
118        match ty {
119            common_types::DocumentType::DoctypeSchematic => Some(Self::Schematic),
120            common_types::DocumentType::DoctypeSymbol => Some(Self::Symbol),
121            common_types::DocumentType::DoctypePcb => Some(Self::Pcb),
122            common_types::DocumentType::DoctypeFootprint => Some(Self::Footprint),
123            common_types::DocumentType::DoctypeDrawingSheet => Some(Self::DrawingSheet),
124            common_types::DocumentType::DoctypeProject => Some(Self::Project),
125            common_types::DocumentType::DoctypeUnknown => None,
126        }
127    }
128}
129
130impl std::fmt::Display for DocumentType {
131    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132        let value = match self {
133            Self::Schematic => "schematic",
134            Self::Symbol => "symbol",
135            Self::Pcb => "pcb",
136            Self::Footprint => "footprint",
137            Self::DrawingSheet => "drawing-sheet",
138            Self::Project => "project",
139        };
140
141        write!(f, "{value}")
142    }
143}
144
145impl FromStr for DocumentType {
146    type Err = String;
147
148    fn from_str(value: &str) -> Result<Self, Self::Err> {
149        match value {
150            "schematic" => Ok(Self::Schematic),
151            "symbol" => Ok(Self::Symbol),
152            "pcb" => Ok(Self::Pcb),
153            "footprint" => Ok(Self::Footprint),
154            "drawing-sheet" => Ok(Self::DrawingSheet),
155            "project" => Ok(Self::Project),
156            _ => Err(format!(
157                "unknown document type `{value}`; expected one of: schematic, symbol, pcb, footprint, drawing-sheet, project"
158            )),
159        }
160    }
161}
162
163#[derive(Clone, Debug, Eq, PartialEq)]
164/// Minimal project information attached to open-document responses.
165pub struct ProjectInfo {
166    /// Project display name, if provided by KiCad.
167    pub name: Option<String>,
168    /// Project filesystem path, if available.
169    pub path: Option<PathBuf>,
170}
171
172#[derive(Clone, Debug, Eq, PartialEq)]
173/// Descriptor for an open KiCad document.
174pub struct DocumentSpecifier {
175    /// KiCad document type.
176    pub document_type: DocumentType,
177    /// Board filename when relevant.
178    pub board_filename: Option<String>,
179    /// Owning project metadata.
180    pub project: ProjectInfo,
181}
182
183#[derive(Clone, Debug, Eq, PartialEq)]
184/// Count of selected items for a specific protobuf type URL.
185pub struct SelectionTypeCount {
186    /// Protobuf type URL for the selected item type.
187    pub type_url: String,
188    /// Number of selected items of this type.
189    pub count: usize,
190}
191
192#[derive(Clone, Debug, Eq, PartialEq)]
193/// Summary of current selection composition.
194pub struct SelectionSummary {
195    /// Total selected item count.
196    pub total_items: usize,
197    /// Per-type counts by protobuf type URL.
198    pub type_url_counts: Vec<SelectionTypeCount>,
199}
200
201#[derive(Clone, Debug, Eq, PartialEq)]
202/// Human/debug-friendly selection entry detail.
203pub struct SelectionItemDetail {
204    /// Protobuf type URL.
205    pub type_url: String,
206    /// Decoded/debug string detail.
207    pub detail: String,
208    /// Raw payload length in bytes.
209    pub raw_len: usize,
210}
211
212#[derive(Clone, Debug, Eq, PartialEq)]
213/// Opaque commit session identifier returned by `begin_commit`.
214pub struct CommitSession {
215    /// KiCad commit session id.
216    pub id: String,
217}
218
219#[derive(Clone, Copy, Debug, Eq, PartialEq)]
220/// Final action to apply when ending a commit session.
221pub enum CommitAction {
222    /// Persist commit changes.
223    Commit,
224    /// Discard commit changes.
225    Drop,
226}
227
228#[derive(Clone, Copy, Debug, Eq, PartialEq)]
229/// Status result returned by `run_action`.
230pub enum RunActionStatus {
231    /// Action succeeded.
232    Ok,
233    /// Action name or payload was invalid.
234    Invalid,
235    /// Target editor frame was not open.
236    FrameNotOpen,
237    /// Unrecognized status code from KiCad.
238    Unknown(i32),
239}
240
241#[derive(Clone, Copy, Debug, Eq, PartialEq)]
242/// Merge strategy for map-like update APIs.
243pub enum MapMergeMode {
244    /// Merge provided entries into existing map.
245    Merge,
246    /// Replace existing map with provided entries.
247    Replace,
248}
249
250impl std::fmt::Display for MapMergeMode {
251    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
252        match self {
253            Self::Merge => write!(f, "merge"),
254            Self::Replace => write!(f, "replace"),
255        }
256    }
257}
258
259impl FromStr for MapMergeMode {
260    type Err = String;
261
262    fn from_str(value: &str) -> Result<Self, Self::Err> {
263        match value {
264            "merge" => Ok(Self::Merge),
265            "replace" => Ok(Self::Replace),
266            _ => Err(format!(
267                "unknown merge mode `{value}`; expected `merge` or `replace`"
268            )),
269        }
270    }
271}
272
273impl std::fmt::Display for CommitAction {
274    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275        match self {
276            Self::Commit => write!(f, "commit"),
277            Self::Drop => write!(f, "drop"),
278        }
279    }
280}
281
282impl FromStr for CommitAction {
283    type Err = String;
284
285    fn from_str(value: &str) -> Result<Self, Self::Err> {
286        match value {
287            "commit" => Ok(Self::Commit),
288            "drop" => Ok(Self::Drop),
289            _ => Err(format!(
290                "unknown commit action `{value}`; expected `commit` or `drop`"
291            )),
292        }
293    }
294}
295
296#[derive(Clone, Debug, Eq, PartialEq)]
297/// Title block fields from the active document.
298pub struct TitleBlockInfo {
299    /// Title block title.
300    pub title: String,
301    /// Title block date.
302    pub date: String,
303    /// Revision string.
304    pub revision: String,
305    /// Company field.
306    pub company: String,
307    /// Non-empty comment fields.
308    pub comments: Vec<String>,
309}
310
311#[derive(Clone, Debug, Eq, PartialEq)]
312pub struct ItemBoundingBox {
313    pub item_id: String,
314    pub x_nm: i64,
315    pub y_nm: i64,
316    pub width_nm: i64,
317    pub height_nm: i64,
318}
319
320#[derive(Clone, Copy, Debug, Eq, PartialEq)]
321pub enum ItemHitTestResult {
322    Unknown,
323    NoHit,
324    Hit,
325}
326
327#[derive(Clone, Copy, Debug, Eq, PartialEq)]
328pub struct PcbObjectTypeCode {
329    pub code: i32,
330    pub name: &'static str,
331}
332
333#[derive(Clone, Copy, Debug, Eq, PartialEq)]
334pub enum TextHorizontalAlignment {
335    Unknown,
336    Left,
337    Center,
338    Right,
339    Indeterminate,
340}
341
342#[derive(Clone, Copy, Debug, Eq, PartialEq)]
343pub enum TextVerticalAlignment {
344    Unknown,
345    Top,
346    Center,
347    Bottom,
348    Indeterminate,
349}
350
351#[derive(Clone, Debug, PartialEq)]
352pub struct TextAttributesSpec {
353    pub font_name: Option<String>,
354    pub horizontal_alignment: TextHorizontalAlignment,
355    pub vertical_alignment: TextVerticalAlignment,
356    pub angle_degrees: Option<f64>,
357    pub line_spacing: Option<f64>,
358    pub stroke_width_nm: Option<i64>,
359    pub italic: bool,
360    pub bold: bool,
361    pub underlined: bool,
362    pub mirrored: bool,
363    pub multiline: bool,
364    pub keep_upright: bool,
365    pub size_nm: Option<Vector2Nm>,
366}
367
368impl Default for TextAttributesSpec {
369    fn default() -> Self {
370        Self {
371            font_name: None,
372            horizontal_alignment: TextHorizontalAlignment::Unknown,
373            vertical_alignment: TextVerticalAlignment::Unknown,
374            angle_degrees: None,
375            line_spacing: None,
376            stroke_width_nm: None,
377            italic: false,
378            bold: false,
379            underlined: false,
380            mirrored: false,
381            multiline: false,
382            keep_upright: false,
383            size_nm: None,
384        }
385    }
386}
387
388#[derive(Clone, Debug, PartialEq)]
389pub struct TextSpec {
390    pub text: String,
391    pub position_nm: Option<Vector2Nm>,
392    pub attributes: Option<TextAttributesSpec>,
393    pub hyperlink: Option<String>,
394}
395
396impl TextSpec {
397    pub fn plain(text: impl Into<String>) -> Self {
398        Self {
399            text: text.into(),
400            position_nm: None,
401            attributes: None,
402            hyperlink: None,
403        }
404    }
405}
406
407#[derive(Clone, Debug, Eq, PartialEq)]
408pub struct TextExtents {
409    pub x_nm: i64,
410    pub y_nm: i64,
411    pub width_nm: i64,
412    pub height_nm: i64,
413}
414
415#[derive(Clone, Debug, PartialEq)]
416pub struct TextBoxSpec {
417    pub text: String,
418    pub top_left_nm: Option<Vector2Nm>,
419    pub bottom_right_nm: Option<Vector2Nm>,
420    pub attributes: Option<TextAttributesSpec>,
421}
422
423#[derive(Clone, Debug, PartialEq)]
424pub enum TextObjectSpec {
425    Text(TextSpec),
426    TextBox(TextBoxSpec),
427}
428
429#[derive(Clone, Debug, PartialEq)]
430pub enum TextShapeGeometry {
431    Segment {
432        start_nm: Option<Vector2Nm>,
433        end_nm: Option<Vector2Nm>,
434    },
435    Rectangle {
436        top_left_nm: Option<Vector2Nm>,
437        bottom_right_nm: Option<Vector2Nm>,
438        corner_radius_nm: Option<i64>,
439    },
440    Arc {
441        start_nm: Option<Vector2Nm>,
442        mid_nm: Option<Vector2Nm>,
443        end_nm: Option<Vector2Nm>,
444    },
445    Circle {
446        center_nm: Option<Vector2Nm>,
447        radius_point_nm: Option<Vector2Nm>,
448    },
449    Polygon {
450        polygons: Vec<PolygonWithHolesNm>,
451    },
452    Bezier {
453        start_nm: Option<Vector2Nm>,
454        control1_nm: Option<Vector2Nm>,
455        control2_nm: Option<Vector2Nm>,
456        end_nm: Option<Vector2Nm>,
457    },
458    Unknown,
459}
460
461#[derive(Clone, Debug, PartialEq)]
462pub struct TextShape {
463    pub geometry: TextShapeGeometry,
464    pub stroke_width_nm: Option<i64>,
465    pub stroke_style: Option<i32>,
466    pub stroke_color: Option<ColorRgba>,
467    pub fill_type: Option<i32>,
468    pub fill_color: Option<ColorRgba>,
469}
470
471#[derive(Clone, Debug, PartialEq)]
472pub struct TextAsShapesEntry {
473    pub source: Option<TextObjectSpec>,
474    pub shapes: Vec<TextShape>,
475}
476
477impl std::fmt::Display for ItemHitTestResult {
478    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
479        let value = match self {
480            Self::Unknown => "unknown",
481            Self::NoHit => "no-hit",
482            Self::Hit => "hit",
483        };
484
485        write!(f, "{value}")
486    }
487}
488
489#[cfg(test)]
490mod tests {
491    use super::{CommitAction, EditorFrameType, MapMergeMode};
492    use std::str::FromStr;
493
494    #[test]
495    fn commit_action_parses_known_values() {
496        assert_eq!(CommitAction::from_str("commit"), Ok(CommitAction::Commit));
497        assert_eq!(CommitAction::from_str("drop"), Ok(CommitAction::Drop));
498    }
499
500    #[test]
501    fn commit_action_rejects_unknown_values() {
502        assert!(CommitAction::from_str("rollback").is_err());
503    }
504
505    #[test]
506    fn editor_frame_type_parses_known_values() {
507        assert_eq!(
508            EditorFrameType::from_str("pcb"),
509            Ok(EditorFrameType::PcbEditor)
510        );
511        assert_eq!(
512            EditorFrameType::from_str("project-manager"),
513            Ok(EditorFrameType::ProjectManager)
514        );
515    }
516
517    #[test]
518    fn editor_frame_type_rejects_unknown_values() {
519        assert!(EditorFrameType::from_str("layout").is_err());
520    }
521
522    #[test]
523    fn map_merge_mode_parses_known_values() {
524        assert_eq!(MapMergeMode::from_str("merge"), Ok(MapMergeMode::Merge));
525        assert_eq!(MapMergeMode::from_str("replace"), Ok(MapMergeMode::Replace));
526    }
527
528    #[test]
529    fn map_merge_mode_rejects_unknown_values() {
530        assert!(MapMergeMode::from_str("upsert").is_err());
531    }
532}