1use astarte_interfaces::mapping::endpoint::EndpointError;
22use astarte_interfaces::mapping::path::MappingPathError;
23
24use crate::aggregate::AstarteObject;
25use crate::error::{AggregationError, InterfaceTypeError};
26use crate::types::TypeError;
27use crate::{AstarteData, Timestamp};
28
29#[derive(Debug, Clone, PartialEq)]
34pub struct DeviceEvent {
35 pub interface: String,
37 pub path: String,
39 pub data: Value,
41}
42
43#[derive(thiserror::Error, Debug)]
45#[non_exhaustive]
46pub enum FromEventError {
47 #[error("couldn't parse request from interface {0}")]
49 Interface(String),
50 #[error("the interface {interface} has wrong path {base_path}")]
52 Path {
53 interface: &'static str,
55 base_path: String,
57 },
58 #[error("invalid interface aggregation for the event")]
60 Aggregation(#[from] AggregationError),
61 #[error("invalid interface type for the event")]
63 InterfaceType(#[from] InterfaceTypeError),
64 #[error("unset passed to {interface}{endpoint} without allow unset")]
66 Unset {
67 interface: &'static str,
69 endpoint: String,
71 },
72 #[error("object {interface} missing field {base_path}/{path}")]
74 MissingField {
75 interface: &'static str,
77 base_path: &'static str,
79 path: &'static str,
81 },
82 #[error("couldn't convert from AstarteData")]
84 Conversion(#[from] TypeError),
85 #[error("couldn't parse the endpoint")]
87 Endpoint(#[from] EndpointError),
88 #[error("couldn't parse the mapping path")]
90 MappingPath(#[from] MappingPathError),
91}
92
93pub trait FromEvent: Sized {
163 type Err;
165
166 fn from_event(event: DeviceEvent) -> Result<Self, Self::Err>;
168}
169
170#[derive(Debug, Clone, PartialEq)]
172pub enum Value {
173 Individual {
175 data: AstarteData,
177 timestamp: Timestamp,
182 },
183 Object {
185 data: AstarteObject,
187 timestamp: Timestamp,
192 },
193 Property(Option<AstarteData>),
195}
196
197impl Value {
198 #[must_use]
202 pub fn is_individual(&self) -> bool {
203 matches!(self, Self::Individual { .. })
204 }
205
206 #[must_use]
209 pub fn as_individual(&self) -> Option<(&AstarteData, &Timestamp)> {
210 if let Self::Individual { data, timestamp } = self {
211 Some((data, timestamp))
212 } else {
213 None
214 }
215 }
216
217 pub fn try_into_individual(self) -> Result<(AstarteData, Timestamp), Self> {
220 if let Self::Individual { data, timestamp } = self {
221 Ok((data, timestamp))
222 } else {
223 Err(self)
224 }
225 }
226
227 #[must_use]
231 pub fn is_object(&self) -> bool {
232 matches!(self, Self::Object { .. })
233 }
234
235 #[must_use]
237 pub fn as_object(&self) -> Option<(&AstarteObject, &Timestamp)> {
238 if let Self::Object { data, timestamp } = self {
239 Some((data, timestamp))
240 } else {
241 None
242 }
243 }
244
245 pub fn try_into_object(self) -> Result<(AstarteObject, Timestamp), Self> {
247 if let Self::Object { data, timestamp } = self {
248 Ok((data, timestamp))
249 } else {
250 Err(self)
251 }
252 }
253
254 #[must_use]
258 pub fn is_property(&self) -> bool {
259 matches!(self, Self::Property(_))
260 }
261
262 #[must_use]
264 pub fn as_property(&self) -> Option<&Option<AstarteData>> {
265 if let Self::Property(v) = self {
266 Some(v)
267 } else {
268 None
269 }
270 }
271
272 pub fn try_into_property(self) -> Result<Option<AstarteData>, Self> {
274 if let Self::Property(v) = self {
275 Ok(v)
276 } else {
277 Err(self)
278 }
279 }
280}
281
282#[cfg(test)]
283mod tests {
284 use chrono::Utc;
285 use pretty_assertions::assert_eq;
286
287 use super::*;
288
289 #[test]
290 fn should_increase_coverage() {
291 let timestamp = Utc::now();
292 let individual = AstarteData::Integer(42);
293 let val = Value::Individual {
294 data: individual.clone(),
295 timestamp,
296 };
297 assert!(val.is_individual());
298 assert_eq!(val.as_individual(), Some((&individual, ×tamp)));
299 assert_eq!(val.as_object(), None);
300 assert_eq!(val.as_property(), None);
301 assert_eq!(
302 val.clone().try_into_individual(),
303 Ok((individual, timestamp))
304 );
305 assert_eq!(val.clone().try_into_object(), Err(val.clone()));
306 assert_eq!(val.clone().try_into_property(), Err(val));
307
308 let val = Value::Object {
309 data: AstarteObject::new(),
310 timestamp,
311 };
312 assert!(val.is_object());
313 assert_eq!(val.as_individual(), None);
314 assert_eq!(val.as_property(), None);
315 assert_eq!(val.as_object(), Some((&AstarteObject::new(), ×tamp)));
316 assert_eq!(
317 val.clone().try_into_object(),
318 Ok((AstarteObject::new(), timestamp))
319 );
320 assert_eq!(val.clone().try_into_individual(), Err(val.clone()));
321 assert_eq!(val.clone().try_into_property(), Err(val));
322
323 let prop = Some(AstarteData::Integer(42));
324 let val = Value::Property(prop.clone());
325 assert!(val.is_property());
326 assert_eq!(val.as_property(), Some(&prop));
327 assert_eq!(val.as_object(), None);
328 assert_eq!(val.as_individual(), None);
329 assert_eq!(val.clone().try_into_individual(), Err(val.clone()));
330 assert_eq!(val.clone().try_into_object(), Err(val.clone()));
331 assert_eq!(val.clone().try_into_property(), Ok(prop));
332 }
333
334 #[test]
335 #[cfg(feature = "derive")]
336 fn should_derive_form_event_obj() {
337 use crate::aggregate::AstarteObject;
338 use crate::{DeviceEvent, FromEvent, Value};
339
340 use crate::{self as astarte_device_sdk};
342
343 #[derive(Debug, FromEvent, PartialEq, Eq)]
344 #[from_event(
345 interface = "com.example.Sensor",
346 path = "/sensor",
347 aggregation = "object"
348 )]
349 struct Sensor {
350 name: String,
351 value: i32,
352 }
353
354 let mut data = AstarteObject::new();
355 data.insert("name".to_string(), "Foo".to_string().into());
356 data.insert("value".to_string(), 42i32.into());
357
358 let event = DeviceEvent {
359 interface: "com.example.Sensor".to_string(),
360 path: "/sensor".to_string(),
361 data: Value::Object {
362 data,
363 timestamp: Utc::now(),
364 },
365 };
366
367 let sensor = Sensor::from_event(event).expect("couldn't parse the event");
368
369 let expected = Sensor {
370 name: "Foo".to_string(),
371 value: 42,
372 };
373
374 assert_eq!(sensor, expected);
375 }
376
377 #[test]
378 #[cfg(feature = "derive")]
379 fn should_derive_form_event_individual() {
380 use crate::{AstarteData, DeviceEvent, FromEvent, Value};
381
382 use crate::{self as astarte_device_sdk};
384
385 #[derive(Debug, FromEvent, PartialEq)]
386 #[from_event(interface = "com.example.Sensor", aggregation = "individual")]
387 enum Sensor {
388 #[mapping(endpoint = "/%{param}/luminosity")]
389 Luminosity(i32),
390 #[mapping(endpoint = "/sensor/temperature")]
391 Temperature(f64),
392 }
393
394 let event = DeviceEvent {
395 interface: "com.example.Sensor".to_string(),
396 path: "/sensor/luminosity".to_string(),
397 data: Value::Individual {
398 data: 42i32.into(),
399 timestamp: Utc::now(),
400 },
401 };
402
403 let luminosity = Sensor::from_event(event).expect("couldn't parse the event");
404
405 let expected = Sensor::Luminosity(42);
406
407 assert_eq!(luminosity, expected);
408
409 let event = DeviceEvent {
410 interface: "com.example.Sensor".to_string(),
411 path: "/sensor/temperature".to_string(),
412 data: Value::Individual {
413 data: AstarteData::try_from(3.0).unwrap(),
414 timestamp: Utc::now(),
415 },
416 };
417
418 let temperature = Sensor::from_event(event).expect("couldn't parse the event");
419
420 let expected = Sensor::Temperature(3.);
421
422 assert_eq!(temperature, expected);
423 }
424
425 #[test]
426 #[cfg(feature = "derive")]
427 fn should_derive_form_event_property() {
428 use crate::{AstarteData, DeviceEvent, FromEvent, Value};
429
430 use crate::{self as astarte_device_sdk};
432
433 #[derive(Debug, FromEvent, PartialEq)]
434 #[from_event(interface = "com.example.Sensor", interface_type = "properties")]
435 enum Sensor {
436 #[mapping(endpoint = "/%{param}/luminosity")]
437 Luminosity(i32),
438 #[mapping(endpoint = "/sensor/temperature")]
439 Temperature(f64),
440 #[mapping(endpoint = "/sensor/unsettable", allow_unset = true)]
441 Unsettable(Option<bool>),
442 }
443
444 let event = DeviceEvent {
445 interface: "com.example.Sensor".to_string(),
446 path: "/sensor/luminosity".to_string(),
447 data: Value::Property(Some(42i32.into())),
448 };
449
450 let luminosity = Sensor::from_event(event).expect("couldn't parse the event");
451
452 let expected = Sensor::Luminosity(42);
453
454 assert_eq!(luminosity, expected);
455
456 let event = DeviceEvent {
457 interface: "com.example.Sensor".to_string(),
458 path: "/sensor/temperature".to_string(),
459 data: Value::Property(Some(AstarteData::try_from(3.0).unwrap())),
460 };
461
462 let temperature = Sensor::from_event(event).expect("couldn't parse the event");
463
464 let expected = Sensor::Temperature(3.0);
465
466 assert_eq!(temperature, expected);
467
468 let event = DeviceEvent {
469 interface: "com.example.Sensor".to_string(),
470 path: "/sensor/unsettable".to_string(),
471 data: Value::Property(Some(AstarteData::Boolean(true))),
472 };
473
474 let temperature = Sensor::from_event(event).expect("couldn't parse the event");
475
476 let expected = Sensor::Unsettable(Some(true));
477
478 assert_eq!(temperature, expected);
479
480 let event = DeviceEvent {
481 interface: "com.example.Sensor".to_string(),
482 path: "/sensor/unsettable".to_string(),
483 data: Value::Property(None),
484 };
485
486 let temperature = Sensor::from_event(event).expect("couldn't parse the event");
487
488 let expected = Sensor::Unsettable(None);
489
490 assert_eq!(temperature, expected);
491 }
492}