Skip to main content

spvirit_server/
convert.rs

1//! Type conversions from [`DecodedValue`] to Rust scalars and `ScalarValue`.
2
3use spvirit_codec::spvd_decode::DecodedValue;
4use spvirit_types::{ScalarArrayValue, ScalarValue};
5
6pub fn decoded_to_bool(val: &DecodedValue) -> Option<bool> {
7    match val {
8        DecodedValue::Boolean(v) => Some(*v),
9        DecodedValue::Int8(v) => Some(*v != 0),
10        DecodedValue::Int16(v) => Some(*v != 0),
11        DecodedValue::Int32(v) => Some(*v != 0),
12        DecodedValue::Int64(v) => Some(*v != 0),
13        DecodedValue::UInt8(v) => Some(*v != 0),
14        DecodedValue::UInt16(v) => Some(*v != 0),
15        DecodedValue::UInt32(v) => Some(*v != 0),
16        DecodedValue::UInt64(v) => Some(*v != 0),
17        DecodedValue::Float32(v) => Some(*v != 0.0),
18        DecodedValue::Float64(v) => Some(*v != 0.0),
19        _ => None,
20    }
21}
22
23pub fn decoded_to_i8(val: &DecodedValue) -> Option<i8> {
24    decoded_to_i64(val).and_then(|v| i8::try_from(v).ok())
25}
26
27pub fn decoded_to_i16(val: &DecodedValue) -> Option<i16> {
28    decoded_to_i64(val).and_then(|v| i16::try_from(v).ok())
29}
30
31pub fn decoded_to_i32(val: &DecodedValue) -> Option<i32> {
32    match val {
33        DecodedValue::Int8(v) => Some(*v as i32),
34        DecodedValue::Int16(v) => Some(*v as i32),
35        DecodedValue::Int32(v) => Some(*v),
36        DecodedValue::Int64(v) => Some(*v as i32),
37        DecodedValue::UInt8(v) => Some(*v as i32),
38        DecodedValue::UInt16(v) => Some(*v as i32),
39        DecodedValue::UInt32(v) => Some(*v as i32),
40        DecodedValue::UInt64(v) => Some(*v as i32),
41        DecodedValue::Boolean(v) => Some(if *v { 1 } else { 0 }),
42        DecodedValue::Float32(v) => Some(*v as i32),
43        DecodedValue::Float64(v) => Some(*v as i32),
44        _ => None,
45    }
46}
47
48pub fn decoded_to_i64(val: &DecodedValue) -> Option<i64> {
49    match val {
50        DecodedValue::Int8(v) => Some(*v as i64),
51        DecodedValue::Int16(v) => Some(*v as i64),
52        DecodedValue::Int32(v) => Some(*v as i64),
53        DecodedValue::Int64(v) => Some(*v),
54        DecodedValue::UInt8(v) => Some(*v as i64),
55        DecodedValue::UInt16(v) => Some(*v as i64),
56        DecodedValue::UInt32(v) => Some(*v as i64),
57        DecodedValue::UInt64(v) => i64::try_from(*v).ok(),
58        DecodedValue::Boolean(v) => Some(if *v { 1 } else { 0 }),
59        DecodedValue::Float32(v) => Some(*v as i64),
60        DecodedValue::Float64(v) => Some(*v as i64),
61        _ => None,
62    }
63}
64
65pub fn decoded_to_u8(val: &DecodedValue) -> Option<u8> {
66    decoded_to_u64(val).and_then(|v| u8::try_from(v).ok())
67}
68
69pub fn decoded_to_u16(val: &DecodedValue) -> Option<u16> {
70    decoded_to_u64(val).and_then(|v| u16::try_from(v).ok())
71}
72
73pub fn decoded_to_u32(val: &DecodedValue) -> Option<u32> {
74    decoded_to_u64(val).and_then(|v| u32::try_from(v).ok())
75}
76
77pub fn decoded_to_u64(val: &DecodedValue) -> Option<u64> {
78    match val {
79        DecodedValue::Int8(v) => (*v >= 0).then_some(*v as u64),
80        DecodedValue::Int16(v) => (*v >= 0).then_some(*v as u64),
81        DecodedValue::Int32(v) => (*v >= 0).then_some(*v as u64),
82        DecodedValue::Int64(v) => (*v >= 0).then_some(*v as u64),
83        DecodedValue::UInt8(v) => Some(*v as u64),
84        DecodedValue::UInt16(v) => Some(*v as u64),
85        DecodedValue::UInt32(v) => Some(*v as u64),
86        DecodedValue::UInt64(v) => Some(*v),
87        DecodedValue::Boolean(v) => Some(if *v { 1 } else { 0 }),
88        DecodedValue::Float32(v) => (*v >= 0.0).then_some(*v as u64),
89        DecodedValue::Float64(v) => (*v >= 0.0).then_some(*v as u64),
90        _ => None,
91    }
92}
93
94pub fn decoded_to_f32(val: &DecodedValue) -> Option<f32> {
95    decoded_to_f64(val).map(|v| v as f32)
96}
97
98pub fn decoded_to_f64(val: &DecodedValue) -> Option<f64> {
99    match val {
100        DecodedValue::Float64(v) => Some(*v),
101        DecodedValue::Float32(v) => Some(*v as f64),
102        DecodedValue::Int8(v) => Some(*v as f64),
103        DecodedValue::Int16(v) => Some(*v as f64),
104        DecodedValue::Int32(v) => Some(*v as f64),
105        DecodedValue::Int64(v) => Some(*v as f64),
106        DecodedValue::UInt8(v) => Some(*v as f64),
107        DecodedValue::UInt16(v) => Some(*v as f64),
108        DecodedValue::UInt32(v) => Some(*v as f64),
109        DecodedValue::UInt64(v) => Some(*v as f64),
110        DecodedValue::Boolean(v) => Some(if *v { 1.0 } else { 0.0 }),
111        _ => None,
112    }
113}
114
115pub fn decoded_to_string(val: &DecodedValue) -> Option<String> {
116    match val {
117        DecodedValue::String(s) => Some(s.clone()),
118        _ => None,
119    }
120}
121
122pub fn decoded_to_scalar_value(val: &DecodedValue) -> ScalarValue {
123    if let Some(b) = decoded_to_bool(val) {
124        return ScalarValue::Bool(b);
125    }
126    if let Some(i) = decoded_to_i32(val) {
127        return ScalarValue::I32(i);
128    }
129    if let Some(f) = decoded_to_f64(val) {
130        return ScalarValue::F64(f);
131    }
132    if let Some(s) = decoded_to_string(val) {
133        return ScalarValue::Str(s);
134    }
135    ScalarValue::I32(0)
136}
137
138pub fn decoded_to_scalar_array(
139    val: &DecodedValue,
140    template: &ScalarArrayValue,
141) -> Option<ScalarArrayValue> {
142    let DecodedValue::Array(items) = val else {
143        return None;
144    };
145    match template {
146        ScalarArrayValue::Bool(_) => Some(ScalarArrayValue::Bool(
147            items.iter().filter_map(decoded_to_bool).collect(),
148        )),
149        ScalarArrayValue::I8(_) => Some(ScalarArrayValue::I8(
150            items.iter().filter_map(decoded_to_i8).collect(),
151        )),
152        ScalarArrayValue::I16(_) => Some(ScalarArrayValue::I16(
153            items.iter().filter_map(decoded_to_i16).collect(),
154        )),
155        ScalarArrayValue::I32(_) => Some(ScalarArrayValue::I32(
156            items.iter().filter_map(decoded_to_i32).collect(),
157        )),
158        ScalarArrayValue::I64(_) => Some(ScalarArrayValue::I64(
159            items.iter().filter_map(decoded_to_i64).collect(),
160        )),
161        ScalarArrayValue::U8(_) => Some(ScalarArrayValue::U8(
162            items.iter().filter_map(decoded_to_u8).collect(),
163        )),
164        ScalarArrayValue::U16(_) => Some(ScalarArrayValue::U16(
165            items.iter().filter_map(decoded_to_u16).collect(),
166        )),
167        ScalarArrayValue::U32(_) => Some(ScalarArrayValue::U32(
168            items.iter().filter_map(decoded_to_u32).collect(),
169        )),
170        ScalarArrayValue::U64(_) => Some(ScalarArrayValue::U64(
171            items.iter().filter_map(decoded_to_u64).collect(),
172        )),
173        ScalarArrayValue::F32(_) => Some(ScalarArrayValue::F32(
174            items.iter().filter_map(decoded_to_f32).collect(),
175        )),
176        ScalarArrayValue::F64(_) => Some(ScalarArrayValue::F64(
177            items.iter().filter_map(decoded_to_f64).collect(),
178        )),
179        ScalarArrayValue::Str(_) => Some(ScalarArrayValue::Str(
180            items.iter().filter_map(decoded_to_string).collect(),
181        )),
182    }
183}
184
185/// Decode an `NtAlarm` from a decoded PVA alarm structure.
186pub fn decode_nt_alarm(val: &DecodedValue) -> Option<spvirit_types::NtAlarm> {
187    let DecodedValue::Structure(fields) = val else {
188        return None;
189    };
190    let severity = fields
191        .iter()
192        .find(|(n, _)| n == "severity")
193        .and_then(|(_, v)| decoded_to_i32(v))
194        .unwrap_or(0);
195    let status = fields
196        .iter()
197        .find(|(n, _)| n == "status")
198        .and_then(|(_, v)| decoded_to_i32(v))
199        .unwrap_or(0);
200    let message = fields
201        .iter()
202        .find(|(n, _)| n == "message")
203        .and_then(|(_, v)| decoded_to_string(v))
204        .unwrap_or_default();
205    Some(spvirit_types::NtAlarm {
206        severity,
207        status,
208        message,
209    })
210}
211
212/// Decode an `NtTimeStamp` from a decoded PVA timestamp structure.
213pub fn decode_nt_timestamp(val: &DecodedValue) -> Option<spvirit_types::NtTimeStamp> {
214    let DecodedValue::Structure(fields) = val else {
215        return None;
216    };
217    let seconds = fields
218        .iter()
219        .find(|(n, _)| n == "secondsPastEpoch")
220        .and_then(|(_, v)| decoded_to_i64(v))
221        .unwrap_or(0);
222    let nanos = fields
223        .iter()
224        .find(|(n, _)| n == "nanoseconds")
225        .and_then(|(_, v)| decoded_to_i32(v))
226        .unwrap_or(0);
227    let user_tag = fields
228        .iter()
229        .find(|(n, _)| n == "userTag")
230        .and_then(|(_, v)| decoded_to_i32(v))
231        .unwrap_or(0);
232    Some(spvirit_types::NtTimeStamp {
233        seconds_past_epoch: seconds,
234        nanoseconds: nanos,
235        user_tag,
236    })
237}
238
239/// Decode an `NtDisplay` from a decoded PVA display structure.
240pub fn decode_nt_display(val: &DecodedValue) -> Option<spvirit_types::NtDisplay> {
241    let DecodedValue::Structure(fields) = val else {
242        return None;
243    };
244    let limit_low = fields
245        .iter()
246        .find(|(n, _)| n == "limitLow")
247        .and_then(|(_, v)| decoded_to_f64(v))
248        .unwrap_or(0.0);
249    let limit_high = fields
250        .iter()
251        .find(|(n, _)| n == "limitHigh")
252        .and_then(|(_, v)| decoded_to_f64(v))
253        .unwrap_or(0.0);
254    let description = fields
255        .iter()
256        .find(|(n, _)| n == "description")
257        .and_then(|(_, v)| decoded_to_string(v))
258        .unwrap_or_default();
259    let units = fields
260        .iter()
261        .find(|(n, _)| n == "units")
262        .and_then(|(_, v)| decoded_to_string(v))
263        .unwrap_or_default();
264    let precision = fields
265        .iter()
266        .find(|(n, _)| n == "precision")
267        .and_then(|(_, v)| decoded_to_i32(v))
268        .unwrap_or(0);
269    Some(spvirit_types::NtDisplay {
270        limit_low,
271        limit_high,
272        description,
273        units,
274        precision,
275    })
276}