Skip to main content

altium_format/traits/
conversion.rs

1//! Value conversion traits for parameter serialization.
2
3use crate::error::{AltiumError, Result};
4use crate::types::ParameterValue;
5
6/// Convert from a parameter value to a Rust type.
7///
8/// This trait is used by the derive macro to convert individual parameter
9/// values to their corresponding Rust types.
10pub trait FromParamValue: Sized {
11    /// Parse this type from a parameter value.
12    fn from_param_value(value: &ParameterValue) -> Result<Self>;
13
14    /// Default value to use if the parameter is missing.
15    fn default_value() -> Option<Self> {
16        None
17    }
18}
19
20/// Convert from a Rust type to a parameter value string.
21pub trait ToParamValue {
22    /// Convert this value to a parameter string.
23    fn to_param_value(&self) -> String;
24
25    /// Whether this value should be skipped if it equals the default.
26    fn should_skip_default(&self) -> bool {
27        false
28    }
29}
30
31/// Convert from a list parameter (comma-separated values).
32pub trait FromParamList: Sized {
33    /// Parse this type from a comma-separated parameter value.
34    fn from_param_list(value: &ParameterValue) -> Result<Self>;
35}
36
37/// Convert to a list parameter (comma-separated values).
38pub trait ToParamList {
39    /// Convert this value to a comma-separated string.
40    fn to_param_list(&self) -> String;
41}
42
43// ============================================================================
44// Basic type implementations
45// ============================================================================
46
47impl FromParamValue for i32 {
48    fn from_param_value(value: &ParameterValue) -> Result<Self> {
49        value
50            .as_int()
51            .map_err(|e| AltiumError::InvalidParameter(e.to_string()))
52    }
53
54    fn default_value() -> Option<Self> {
55        Some(0)
56    }
57}
58
59impl ToParamValue for i32 {
60    fn to_param_value(&self) -> String {
61        self.to_string()
62    }
63
64    fn should_skip_default(&self) -> bool {
65        *self == 0
66    }
67}
68
69impl FromParamValue for i64 {
70    fn from_param_value(value: &ParameterValue) -> Result<Self> {
71        value
72            .as_str()
73            .parse()
74            .map_err(|e: std::num::ParseIntError| AltiumError::InvalidParameter(e.to_string()))
75    }
76
77    fn default_value() -> Option<Self> {
78        Some(0)
79    }
80}
81
82impl ToParamValue for i64 {
83    fn to_param_value(&self) -> String {
84        self.to_string()
85    }
86}
87
88impl FromParamValue for u32 {
89    fn from_param_value(value: &ParameterValue) -> Result<Self> {
90        value
91            .as_str()
92            .parse()
93            .map_err(|e: std::num::ParseIntError| AltiumError::InvalidParameter(e.to_string()))
94    }
95
96    fn default_value() -> Option<Self> {
97        Some(0)
98    }
99}
100
101impl ToParamValue for u32 {
102    fn to_param_value(&self) -> String {
103        self.to_string()
104    }
105}
106
107impl FromParamValue for f64 {
108    fn from_param_value(value: &ParameterValue) -> Result<Self> {
109        value
110            .as_double()
111            .map_err(|e| AltiumError::InvalidParameter(e.to_string()))
112    }
113
114    fn default_value() -> Option<Self> {
115        Some(0.0)
116    }
117}
118
119impl ToParamValue for f64 {
120    fn to_param_value(&self) -> String {
121        self.to_string()
122    }
123}
124
125impl FromParamValue for bool {
126    fn from_param_value(value: &ParameterValue) -> Result<Self> {
127        value
128            .as_bool()
129            .map_err(|e| AltiumError::InvalidParameter(e.to_string()))
130    }
131
132    fn default_value() -> Option<Self> {
133        Some(false)
134    }
135}
136
137impl ToParamValue for bool {
138    fn to_param_value(&self) -> String {
139        if *self {
140            "T".to_string()
141        } else {
142            "F".to_string()
143        }
144    }
145
146    fn should_skip_default(&self) -> bool {
147        !*self
148    }
149}
150
151impl FromParamValue for String {
152    fn from_param_value(value: &ParameterValue) -> Result<Self> {
153        Ok(value.as_str().to_string())
154    }
155
156    fn default_value() -> Option<Self> {
157        Some(String::new())
158    }
159}
160
161impl ToParamValue for String {
162    fn to_param_value(&self) -> String {
163        self.clone()
164    }
165
166    fn should_skip_default(&self) -> bool {
167        self.is_empty()
168    }
169}
170
171impl<T: FromParamValue> FromParamValue for Option<T> {
172    fn from_param_value(value: &ParameterValue) -> Result<Self> {
173        Ok(Some(T::from_param_value(value)?))
174    }
175
176    fn default_value() -> Option<Self> {
177        Some(None)
178    }
179}
180
181impl<T: ToParamValue> ToParamValue for Option<T> {
182    fn to_param_value(&self) -> String {
183        match self {
184            Some(v) => v.to_param_value(),
185            None => String::new(),
186        }
187    }
188
189    fn should_skip_default(&self) -> bool {
190        self.is_none()
191    }
192}
193
194// ============================================================================
195// List implementations
196// ============================================================================
197
198impl<T: FromParamValue> FromParamList for Vec<T> {
199    fn from_param_list(value: &ParameterValue) -> Result<Self> {
200        let s = value.as_str();
201        if s.is_empty() {
202            return Ok(Vec::new());
203        }
204
205        s.split(',')
206            .map(|part| {
207                let trimmed = part.trim();
208                // Create a temporary ParameterValue for each part (level 0)
209                let temp_value = ParameterValue::new(trimmed.to_string(), 0);
210                T::from_param_value(&temp_value)
211            })
212            .collect()
213    }
214}
215
216impl<T: ToParamValue> ToParamList for Vec<T> {
217    fn to_param_list(&self) -> String {
218        self.iter()
219            .map(|v| v.to_param_value())
220            .collect::<Vec<_>>()
221            .join(",")
222    }
223}