Skip to main content

iot_core/
path.rs

1//! Protocol-agnostic property addressing.
2//!
3//! A [`PropertyPath`] points at exactly one property of one feature of one
4//! Thing. The actual physical location (Modbus register, MQTT topic, CoAP
5//! URI, OPC UA NodeId) is held by a [`crate::binding::ProtocolBinding`]
6//! looked up via [`crate::binding::ThingMapping`].
7
8use core::fmt;
9
10use crate::thing::{FeatureId, PropertyId, ThingId};
11
12/// Identifies one property in a Thing — protocol-agnostic.
13///
14/// `Ord` is implemented so paths can key a `BTreeMap` (the default Thing
15/// mapping container in `no_std + alloc`).
16#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub struct PropertyPath {
19    /// Owning Thing.
20    #[cfg_attr(feature = "serde", serde(rename = "thingId"))]
21    pub thing: ThingId,
22    /// Feature inside the Thing.
23    #[cfg_attr(feature = "serde", serde(rename = "featureId"))]
24    pub feature: FeatureId,
25    /// Property inside the feature.
26    #[cfg_attr(feature = "serde", serde(rename = "propertyId"))]
27    pub property: PropertyId,
28}
29
30impl PropertyPath {
31    /// Construct from already-validated identifiers.
32    pub fn new(thing: ThingId, feature: FeatureId, property: PropertyId) -> Self {
33        Self {
34            thing,
35            feature,
36            property,
37        }
38    }
39}
40
41impl fmt::Display for PropertyPath {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        // Canonical text form: `thingId/feature/property`. Used by the
44        // gateway crate for log lines and as the default MQTT topic suffix.
45        write!(
46            f,
47            "{}/{}/{}",
48            self.thing.as_str(),
49            self.feature.as_str(),
50            self.property.as_str()
51        )
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn display_canonical() {
61        let p = PropertyPath::new(
62            ThingId::new("acme:pump-7"),
63            FeatureId::new("temperature"),
64            PropertyId::new("value"),
65        );
66        assert_eq!(p.to_string(), "acme:pump-7/temperature/value");
67    }
68}