Skip to main content

altium_format/records/sch/
port.rs

1//! SchPort - Schematic port object (Record 18).
2//!
3//! A port is a labelled connection point used to connect nets across sheets.
4
5use crate::error::Result;
6use crate::traits::{FromParamValue, FromParams, ToParamValue, ToParams};
7use crate::types::ParameterValue;
8use crate::types::{Coord, CoordRect, ParameterCollection, UnknownFields};
9use altium_format_derive::AltiumRecord;
10
11use super::{SchGraphicalBase, SchPrimitive};
12
13/// Port style determining the visual appearance.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
15#[repr(u8)]
16pub enum PortStyle {
17    /// No arrow indicators.
18    #[default]
19    None = 0,
20    /// Extends leftward.
21    Left = 1,
22    /// Extends rightward.
23    Right = 2,
24    /// Extends downward.
25    Top = 3,
26    /// Extends upward (value 7 in format doc).
27    Bottom = 4,
28    /// Internal value 3 - extends rightward.
29    ExtendRight = 5,
30    /// Internal value 7 - extends upwards.
31    ExtendUp = 6,
32}
33
34impl PortStyle {
35    pub fn from_int(value: i32) -> Self {
36        match value {
37            0 => PortStyle::None,
38            1 => PortStyle::Left,
39            2 => PortStyle::Right,
40            3 => PortStyle::ExtendRight,
41            4 => PortStyle::Top,
42            5 | 6 => PortStyle::Bottom,
43            7 => PortStyle::ExtendUp,
44            _ => PortStyle::None,
45        }
46    }
47
48    pub fn to_int(self) -> i32 {
49        match self {
50            PortStyle::None => 0,
51            PortStyle::Left => 1,
52            PortStyle::Right => 2,
53            PortStyle::ExtendRight => 3,
54            PortStyle::Top => 4,
55            PortStyle::Bottom => 5,
56            PortStyle::ExtendUp => 7,
57        }
58    }
59}
60
61impl FromParamValue for PortStyle {
62    fn from_param_value(value: &ParameterValue) -> Result<Self> {
63        Ok(Self::from_int(value.as_int_or(0)))
64    }
65}
66
67impl ToParamValue for PortStyle {
68    fn to_param_value(&self) -> String {
69        self.to_int().to_string()
70    }
71}
72
73/// Port I/O type determining the shape.
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
75#[repr(u8)]
76pub enum PortIoType {
77    /// Unspecified (flat left edge with angled right).
78    #[default]
79    Unspecified = 0,
80    /// Output port.
81    Output = 1,
82    /// Input port.
83    Input = 2,
84    /// Bidirectional port (angled points on both ends).
85    Bidirectional = 3,
86}
87
88impl PortIoType {
89    pub fn from_int(value: i32) -> Self {
90        match value {
91            0 => PortIoType::Unspecified,
92            1 => PortIoType::Output,
93            2 => PortIoType::Input,
94            3 => PortIoType::Bidirectional,
95            _ => PortIoType::Unspecified,
96        }
97    }
98
99    pub fn to_int(self) -> i32 {
100        self as i32
101    }
102}
103
104impl FromParamValue for PortIoType {
105    fn from_param_value(value: &ParameterValue) -> Result<Self> {
106        Ok(Self::from_int(value.as_int_or(0)))
107    }
108}
109
110impl ToParamValue for PortIoType {
111    fn to_param_value(&self) -> String {
112        self.to_int().to_string()
113    }
114}
115
116/// Port text alignment.
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
118#[repr(u8)]
119pub enum PortAlignment {
120    /// Unknown/default alignment.
121    #[default]
122    Unknown = 0,
123    /// Text right-aligned.
124    Right = 1,
125    /// Text left-aligned.
126    Left = 2,
127}
128
129impl PortAlignment {
130    pub fn from_int(value: i32) -> Self {
131        match value {
132            0 => PortAlignment::Unknown,
133            1 => PortAlignment::Right,
134            2 => PortAlignment::Left,
135            _ => PortAlignment::Unknown,
136        }
137    }
138
139    pub fn to_int(self) -> i32 {
140        self as i32
141    }
142}
143
144impl FromParamValue for PortAlignment {
145    fn from_param_value(value: &ParameterValue) -> Result<Self> {
146        Ok(Self::from_int(value.as_int_or(0)))
147    }
148}
149
150impl ToParamValue for PortAlignment {
151    fn to_param_value(&self) -> String {
152        self.to_int().to_string()
153    }
154}
155
156/// Schematic port primitive - labelled connection point.
157#[derive(Debug, Clone, Default, AltiumRecord)]
158#[altium(record_id = 18, format = "params")]
159pub struct SchPort {
160    /// Graphical base (location, color).
161    #[altium(flatten)]
162    pub graphical: SchGraphicalBase,
163
164    /// Port style determining visual appearance.
165    #[altium(param = "STYLE", default)]
166    pub style: PortStyle,
167
168    /// Port I/O type determining shape.
169    #[altium(param = "IOTYPE", default)]
170    pub io_type: PortIoType,
171
172    /// Text alignment.
173    #[altium(param = "ALIGNMENT", default)]
174    pub alignment: PortAlignment,
175
176    /// Port width.
177    #[altium(param = "WIDTH", default)]
178    pub width: i32,
179
180    /// Port height.
181    #[altium(param = "HEIGHT", default)]
182    pub height: i32,
183
184    /// Port name/label text (backslash indicates overline).
185    #[altium(param = "NAME", default)]
186    pub name: String,
187
188    /// Text color.
189    #[altium(param = "TEXTCOLOR", default)]
190    pub text_color: i32,
191
192    /// Font ID (zero uses default).
193    #[altium(param = "FONTID", default)]
194    pub font_id: i32,
195
196    /// Unique identifier.
197    #[altium(param = "UNIQUEID", default)]
198    pub unique_id: String,
199
200    /// Harness type name (omitted if normal signal).
201    #[altium(param = "HARNESSTYPE", default)]
202    pub harness_type: String,
203
204    /// Unknown parameters (preserved for non-destructive editing).
205    #[altium(unknown)]
206    pub unknown_params: UnknownFields,
207}
208
209impl SchPrimitive for SchPort {
210    const RECORD_ID: i32 = 18;
211
212    fn location(&self) -> Option<crate::types::CoordPoint> {
213        Some(crate::types::CoordPoint::from_raw(
214            self.graphical.location_x,
215            self.graphical.location_y,
216        ))
217    }
218
219    fn record_type_name(&self) -> &'static str {
220        "Port"
221    }
222
223    fn get_property(&self, name: &str) -> Option<String> {
224        match name {
225            "NAME" => Some(self.name.clone()),
226            _ => None,
227        }
228    }
229
230    fn import_from_params(params: &ParameterCollection) -> Result<Self> {
231        Self::from_params(params)
232    }
233
234    fn export_to_params(&self) -> ParameterCollection {
235        self.to_params()
236    }
237
238    fn owner_index(&self) -> i32 {
239        self.graphical.base.owner_index
240    }
241
242    fn calculate_bounds(&self) -> CoordRect {
243        CoordRect::from_points(
244            Coord::from_raw(self.graphical.location_x),
245            Coord::from_raw(self.graphical.location_y),
246            Coord::from_raw(self.graphical.location_x + self.width * 10000),
247            Coord::from_raw(self.graphical.location_y + self.height * 10000),
248        )
249    }
250}