oa_gateway_core/
route.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//! Addressing key used by [`crate::Engine`].
//!
//! The engine compares these for equality only. It does not treat `topic`
//! as a path and does not interpret `type_hint`.

/// Topic plus an optional type hint.
///
/// `topic` is the only required coordinate. `type_hint` is whatever
/// discriminator the publishing adapter has: an OWP message name, a UCI
/// type, a PDU type. A subscription with `type_hint: None` matches every
/// type on that topic. A publish with no hint reaches wildcards only.
///
/// Neither field is validated. Matching is exact string equality, not a
/// hierarchy, even though [`Display`](Self#impl-Display-for-RouteKey)
/// looks like a path.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RouteKey {
    /// Engine topic. Often a UCI message name when bridging ActiveMQ.
    pub topic: String,
    /// Protocol discriminator. `None` is a wildcard subscription, or an
    /// untyped publish.
    pub type_hint: Option<String>,
}

impl RouteKey {
    /// A wildcard: every type on `topic`.
    #[must_use]
    pub fn topic(topic: impl Into<String>) -> Self {
        Self {
            topic: topic.into(),
            type_hint: None,
        }
    }

    /// One type on `topic`. A publish with this hint also reaches
    /// [`Self::topic`] subscribers on the same topic.
    #[must_use]
    pub fn typed(topic: impl Into<String>, type_hint: impl Into<String>) -> Self {
        Self {
            topic: topic.into(),
            type_hint: Some(type_hint.into()),
        }
    }

    /// Whether this key has no type hint.
    ///
    /// True for both a wildcard subscription and an untyped publish.
    /// Those two roles match different sets; this predicate does not
    /// distinguish them.
    #[must_use]
    pub fn is_wildcard(&self) -> bool {
        self.type_hint.is_none()
    }
}

impl std::fmt::Display for RouteKey {
    /// Formats as `topic/hint`, or `topic/` when there is no hint.
    ///
    /// Trailing slashes on `topic` are stripped only in the wildcard
    /// form, so `demo/` and `demo` display the same when untyped.
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self.type_hint {
            Some(hint) => write!(f, "{}/{}", self.topic, hint),
            None => write!(f, "{}/", self.topic.trim_end_matches('/')),
        }
    }
}