1use crate::error::NanonisError;
2use serde::{Deserialize, Serialize};
3use std::time::Duration;
4
5pub(crate) fn duration_from_secs_f32(secs: f32) -> Result<Duration, NanonisError> {
10 if secs.is_nan() || secs.is_infinite() || secs < 0.0 {
11 return Err(NanonisError::Protocol(format!(
12 "Invalid duration value: {secs}"
13 )));
14 }
15 Ok(Duration::from_secs_f32(secs))
16}
17
18#[derive(Debug, Clone)]
21pub enum NanonisValue {
22 U8(u8),
23 U16(u16),
24 I16(i16),
25 U32(u32),
26 I32(i32),
27 F32(f32),
28 F64(f64),
29 String(String),
30 ArrayU8(Vec<u8>),
31 ArrayU32(Vec<u32>),
32 ArrayI32(Vec<i32>),
33 ArrayF32(Vec<f32>),
34 ArrayF64(Vec<f64>),
35 ArrayString(Vec<String>),
36 Array2DF32(Vec<Vec<f32>>),
37}
38
39impl From<f32> for NanonisValue {
41 fn from(value: f32) -> Self {
42 NanonisValue::F32(value)
43 }
44}
45
46impl From<f64> for NanonisValue {
47 fn from(value: f64) -> Self {
48 NanonisValue::F64(value)
49 }
50}
51
52impl From<u16> for NanonisValue {
53 fn from(value: u16) -> Self {
54 NanonisValue::U16(value)
55 }
56}
57
58impl From<u32> for NanonisValue {
59 fn from(value: u32) -> Self {
60 NanonisValue::U32(value)
61 }
62}
63
64impl From<i16> for NanonisValue {
65 fn from(value: i16) -> Self {
66 NanonisValue::I16(value)
67 }
68}
69
70impl From<i32> for NanonisValue {
71 fn from(value: i32) -> Self {
72 NanonisValue::I32(value)
73 }
74}
75
76impl From<String> for NanonisValue {
77 fn from(value: String) -> Self {
78 NanonisValue::String(value)
79 }
80}
81
82impl From<Vec<f32>> for NanonisValue {
83 fn from(value: Vec<f32>) -> Self {
84 NanonisValue::ArrayF32(value)
85 }
86}
87
88impl From<Vec<String>> for NanonisValue {
89 fn from(value: Vec<String>) -> Self {
90 NanonisValue::ArrayString(value)
91 }
92}
93
94impl From<Vec<i32>> for NanonisValue {
95 fn from(value: Vec<i32>) -> Self {
96 NanonisValue::ArrayI32(value)
97 }
98}
99
100impl TryFrom<NanonisValue> for f32 {
102 type Error = NanonisError;
103
104 fn try_from(value: NanonisValue) -> Result<Self, Self::Error> {
105 match value {
106 NanonisValue::F32(v) => Ok(v),
107 _ => Err(NanonisError::Protocol(format!(
108 "Expected f32, got {value:?}"
109 ))),
110 }
111 }
112}
113
114impl TryFrom<NanonisValue> for f64 {
115 type Error = NanonisError;
116
117 fn try_from(value: NanonisValue) -> Result<Self, Self::Error> {
118 match value {
119 NanonisValue::F64(v) => Ok(v),
120 _ => Err(NanonisError::Protocol(format!(
121 "Expected f64, got {value:?}"
122 ))),
123 }
124 }
125}
126
127impl TryFrom<NanonisValue> for u16 {
128 type Error = NanonisError;
129
130 fn try_from(value: NanonisValue) -> Result<Self, Self::Error> {
131 match value {
132 NanonisValue::U16(v) => Ok(v),
133 _ => Err(NanonisError::Protocol(format!(
134 "Expected u16, got {value:?}"
135 ))),
136 }
137 }
138}
139
140impl TryFrom<NanonisValue> for u32 {
141 type Error = NanonisError;
142
143 fn try_from(value: NanonisValue) -> Result<Self, Self::Error> {
144 match value {
145 NanonisValue::U32(v) => Ok(v),
146 _ => Err(NanonisError::Protocol(format!(
147 "Expected u32, got {value:?}"
148 ))),
149 }
150 }
151}
152
153impl TryFrom<NanonisValue> for i16 {
154 type Error = NanonisError;
155
156 fn try_from(value: NanonisValue) -> Result<Self, Self::Error> {
157 match value {
158 NanonisValue::I16(v) => Ok(v),
159 _ => Err(NanonisError::Protocol(format!(
160 "Expected i16, got {value:?}"
161 ))),
162 }
163 }
164}
165
166impl TryFrom<NanonisValue> for i32 {
167 type Error = NanonisError;
168
169 fn try_from(value: NanonisValue) -> Result<Self, Self::Error> {
170 match value {
171 NanonisValue::I32(v) => Ok(v),
172 _ => Err(NanonisError::Protocol(format!(
173 "Expected i32, got {value:?}"
174 ))),
175 }
176 }
177}
178
179impl TryFrom<NanonisValue> for Vec<f32> {
180 type Error = NanonisError;
181
182 fn try_from(value: NanonisValue) -> Result<Self, Self::Error> {
183 match value {
184 NanonisValue::ArrayF32(v) => Ok(v),
185 _ => Err(NanonisError::Protocol(format!(
186 "Expected Vec<f32>, got {value:?}"
187 ))),
188 }
189 }
190}
191
192impl TryFrom<NanonisValue> for Vec<String> {
193 type Error = NanonisError;
194
195 fn try_from(value: NanonisValue) -> Result<Self, Self::Error> {
196 match value {
197 NanonisValue::ArrayString(v) => Ok(v),
198 _ => Err(NanonisError::Protocol(format!(
199 "Expected Vec<String>, got {value:?}"
200 ))),
201 }
202 }
203}
204
205impl TryFrom<NanonisValue> for Vec<i32> {
206 type Error = NanonisError;
207
208 fn try_from(value: NanonisValue) -> Result<Self, Self::Error> {
209 match value {
210 NanonisValue::ArrayI32(v) => Ok(v),
211 _ => Err(NanonisError::Protocol(format!(
212 "Expected Vec<i32>, got {value:?}"
213 ))),
214 }
215 }
216}
217
218impl NanonisValue {
220 pub fn as_f32(&self) -> Result<f32, NanonisError> {
221 match self {
222 NanonisValue::F32(v) => Ok(*v),
223 _ => Err(NanonisError::Protocol(format!(
224 "Expected f32, got {self:?}"
225 ))),
226 }
227 }
228
229 pub fn as_f64(&self) -> Result<f64, NanonisError> {
230 match self {
231 NanonisValue::F64(v) => Ok(*v),
232 _ => Err(NanonisError::Protocol(format!(
233 "Expected f64, got {self:?}"
234 ))),
235 }
236 }
237
238 pub fn as_u16(&self) -> Result<u16, NanonisError> {
239 match self {
240 NanonisValue::U16(v) => Ok(*v),
241 _ => Err(NanonisError::Protocol(format!(
242 "Expected u16, got {self:?}"
243 ))),
244 }
245 }
246
247 pub fn as_u32(&self) -> Result<u32, NanonisError> {
248 match self {
249 NanonisValue::U32(v) => Ok(*v),
250 _ => Err(NanonisError::Protocol(format!(
251 "Expected u32, got {self:?}"
252 ))),
253 }
254 }
255
256 pub fn as_i16(&self) -> Result<i16, NanonisError> {
257 match self {
258 NanonisValue::I16(v) => Ok(*v),
259 _ => Err(NanonisError::Protocol(format!(
260 "Expected i16, got {self:?}"
261 ))),
262 }
263 }
264
265 pub fn as_i32(&self) -> Result<i32, NanonisError> {
266 match self {
267 NanonisValue::I32(v) => Ok(*v),
268 _ => Err(NanonisError::Protocol(format!(
269 "Expected i32, got {self:?}"
270 ))),
271 }
272 }
273
274 pub fn as_string_array(&self) -> Result<&[String], NanonisError> {
275 match self {
276 NanonisValue::ArrayString(arr) => Ok(arr),
277 _ => Err(NanonisError::Protocol(format!(
278 "Expected string array, got {self:?}"
279 ))),
280 }
281 }
282
283 pub fn as_f32_array(&self) -> Result<&[f32], NanonisError> {
284 match self {
285 NanonisValue::ArrayF32(arr) => Ok(arr),
286 _ => Err(NanonisError::Protocol(format!(
287 "Expected f32 array, got {self:?}"
288 ))),
289 }
290 }
291
292 pub fn as_f64_array(&self) -> Result<&[f64], NanonisError> {
293 match self {
294 NanonisValue::ArrayF64(arr) => Ok(arr),
295 _ => Err(NanonisError::Protocol(format!(
296 "Expected f64 array, got {self:?}"
297 ))),
298 }
299 }
300
301 pub fn as_i32_array(&self) -> Result<&[i32], NanonisError> {
302 match self {
303 NanonisValue::ArrayI32(arr) => Ok(arr),
304 _ => Err(NanonisError::Protocol(format!(
305 "Expected i32 array, got {self:?}"
306 ))),
307 }
308 }
309
310 pub fn as_u32_array(&self) -> Result<&[u32], NanonisError> {
311 match self {
312 NanonisValue::ArrayU32(arr) => Ok(arr),
313 _ => Err(NanonisError::Protocol(format!(
314 "Expected u32 array, got {self:?}"
315 ))),
316 }
317 }
318
319 pub fn as_string(&self) -> Result<&str, NanonisError> {
320 match self {
321 NanonisValue::String(s) => Ok(s),
322 _ => Err(NanonisError::Protocol(format!(
323 "Expected string, got {self:?}"
324 ))),
325 }
326 }
327
328 pub fn as_f32_2d_array(&self) -> Result<&Vec<Vec<f32>>, NanonisError> {
329 match self {
330 NanonisValue::Array2DF32(arr) => Ok(arr),
331 _ => Err(NanonisError::Protocol(format!(
332 "Expected 2D f32 array, got {self:?}"
333 ))),
334 }
335 }
336}
337
338#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
351pub struct Position {
352 pub x: f64,
353 pub y: f64,
354}
355
356impl Position {
357 pub fn new(x: f64, y: f64) -> Self {
358 Self { x, y }
359 }
360}
361
362#[cfg(test)]
363mod tests {
364 use super::*;
365
366 #[test]
369 fn duration_valid_values() {
370 assert_eq!(
371 duration_from_secs_f32(0.0).unwrap(),
372 Duration::from_secs_f32(0.0)
373 );
374 assert_eq!(
375 duration_from_secs_f32(1.5).unwrap(),
376 Duration::from_secs_f32(1.5)
377 );
378 assert_eq!(
379 duration_from_secs_f32(0.001).unwrap(),
380 Duration::from_secs_f32(0.001)
381 );
382 }
383
384 #[test]
385 fn duration_rejects_negative() {
386 assert!(duration_from_secs_f32(-1.0).is_err());
387 assert!(duration_from_secs_f32(-0.001).is_err());
388 }
389
390 #[test]
391 fn duration_rejects_nan() {
392 assert!(duration_from_secs_f32(f32::NAN).is_err());
393 }
394
395 #[test]
396 fn duration_rejects_infinity() {
397 assert!(duration_from_secs_f32(f32::INFINITY).is_err());
398 assert!(duration_from_secs_f32(f32::NEG_INFINITY).is_err());
399 }
400
401 #[test]
404 fn from_primitives() {
405 assert!(matches!(NanonisValue::from(1.0f32), NanonisValue::F32(v) if v == 1.0));
406 assert!(matches!(NanonisValue::from(2.0f64), NanonisValue::F64(v) if v == 2.0));
407 assert!(matches!(NanonisValue::from(42u16), NanonisValue::U16(42)));
408 assert!(matches!(NanonisValue::from(100u32), NanonisValue::U32(100)));
409 assert!(matches!(NanonisValue::from(-5i16), NanonisValue::I16(-5)));
410 assert!(matches!(NanonisValue::from(-10i32), NanonisValue::I32(-10)));
411 }
412
413 #[test]
414 fn from_collections() {
415 assert!(
416 matches!(NanonisValue::from("hello".to_string()), NanonisValue::String(s) if s == "hello")
417 );
418 assert!(matches!(
419 NanonisValue::from(vec![1.0f32, 2.0]),
420 NanonisValue::ArrayF32(_)
421 ));
422 assert!(matches!(
423 NanonisValue::from(vec![1i32, 2]),
424 NanonisValue::ArrayI32(_)
425 ));
426 }
427
428 #[test]
431 fn tryfrom_correct_types() {
432 assert_eq!(f32::try_from(NanonisValue::F32(3.14)).unwrap(), 3.14);
433 assert_eq!(f64::try_from(NanonisValue::F64(2.718)).unwrap(), 2.718);
434 assert_eq!(u16::try_from(NanonisValue::U16(100)).unwrap(), 100);
435 assert_eq!(u32::try_from(NanonisValue::U32(200)).unwrap(), 200);
436 assert_eq!(i16::try_from(NanonisValue::I16(-5)).unwrap(), -5);
437 assert_eq!(i32::try_from(NanonisValue::I32(-10)).unwrap(), -10);
438 }
439
440 #[test]
441 fn tryfrom_wrong_types() {
442 assert!(f32::try_from(NanonisValue::I32(1)).is_err());
443 assert!(u16::try_from(NanonisValue::F32(1.0)).is_err());
444 assert!(Vec::<f32>::try_from(NanonisValue::I32(1)).is_err());
445 assert!(Vec::<String>::try_from(NanonisValue::F32(1.0)).is_err());
446 }
447
448 #[test]
449 fn as_methods_correct() {
450 assert_eq!(NanonisValue::F32(1.0).as_f32().unwrap(), 1.0);
451 assert_eq!(NanonisValue::F64(2.0).as_f64().unwrap(), 2.0);
452 assert_eq!(NanonisValue::U16(10).as_u16().unwrap(), 10);
453 assert_eq!(NanonisValue::U32(20).as_u32().unwrap(), 20);
454 assert_eq!(NanonisValue::I16(-3).as_i16().unwrap(), -3);
455 assert_eq!(NanonisValue::I32(-7).as_i32().unwrap(), -7);
456 assert_eq!(NanonisValue::String("hi".into()).as_string().unwrap(), "hi");
457 }
458
459 #[test]
460 fn as_methods_wrong_type() {
461 assert!(NanonisValue::I32(1).as_f32().is_err());
462 assert!(NanonisValue::F32(1.0).as_u32().is_err());
463 assert!(NanonisValue::U16(1).as_string().is_err());
464 }
465
466 #[test]
467 fn as_array_methods() {
468 let f32_arr = NanonisValue::ArrayF32(vec![1.0, 2.0]);
469 assert_eq!(f32_arr.as_f32_array().unwrap(), &[1.0, 2.0]);
470 assert!(f32_arr.as_f64_array().is_err());
471
472 let str_arr = NanonisValue::ArrayString(vec!["a".into(), "b".into()]);
473 assert_eq!(str_arr.as_string_array().unwrap(), &["a", "b"]);
474 assert!(str_arr.as_f32_array().is_err());
475 }
476}