tether_agent/channels/definitions/
mod.rs

1use super::tether_compliant_topic::TetherOrCustomTopic;
2
3pub mod receiver_def_builder;
4pub mod sender_def_builder;
5
6pub use receiver_def_builder::ChannelReceiverDefBuilder;
7use rumqttc::QoS;
8pub use sender_def_builder::ChannelSenderDefBuilder;
9use serde::{Deserialize, Serialize};
10
11/**
12A Channel Def(inition) Builder is used for creating a Channel Def(inition).
13*/
14pub trait ChannelDefBuilder {
15    fn new(name: &str) -> Self;
16    fn qos(self, qos: Option<QoS>) -> Self;
17    fn role(self, role: Option<&str>) -> Self;
18    fn id(self, id: Option<&str>) -> Self;
19    fn override_name(self, override_channel_name: Option<&str>) -> Self;
20    fn override_topic(self, override_topic: Option<&str>) -> Self;
21}
22
23/**
24A Channel Def(inition) is intended to encapsulate only the essential metadata
25and configuration needed to describe a Channel. In contrast with a Channel Sender/Receiver,
26it is **not** responsible for actually sending or receiving messages on that Channel.
27*/
28pub trait ChannelDef<'a> {
29    fn name(&'a self) -> &'a str;
30    /// Return the generated topic string actually used by the Channel
31    fn generated_topic(&'a self) -> &'a str;
32    /// Return the custom or Tether-compliant topic
33    fn topic(&'a self) -> &'a TetherOrCustomTopic;
34    fn qos(&'a self) -> QoS;
35}
36
37pub fn number_to_qos(number: u8) -> QoS {
38    match number {
39        0 => QoS::AtMostOnce,
40        1 => QoS::AtLeastOnce,
41        2 => QoS::ExactlyOnce,
42        _ => QoS::AtMostOnce,
43    }
44}
45
46pub fn qos_to_number(qos: QoS) -> u8 {
47    match qos {
48        QoS::AtMostOnce => 0,
49        QoS::AtLeastOnce => 1,
50        QoS::ExactlyOnce => 2,
51    }
52}
53
54#[derive(Serialize, Deserialize)]
55#[serde(remote = "QoS")]
56#[allow(renamed_and_removed_lints)]
57#[allow(enum_variant_names)]
58enum QosDef {
59    AtMostOnce,
60    AtLeastOnce,
61    ExactlyOnce,
62}
63
64#[derive(Clone, Serialize, Deserialize)]
65pub struct ChannelSenderDef {
66    pub name: String,
67    pub generated_topic: String,
68    pub topic: TetherOrCustomTopic,
69    #[serde(with = "QosDef")]
70    pub qos: QoS,
71    pub retain: bool,
72}
73
74// impl Serialize for ChannelSenderDef {
75//     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
76//     where
77//         S: serde::Serializer,
78//     {
79//         let mut state = serializer.serialize_struct("ChannelSenderDef", 5)?;
80//         state.serialize_field("name", &self.name)?;
81//         state.serialize_field("generated_topic", &self.generated_topic)?;
82//         state.serialize_field("topic", &self.topic)?;
83//         state.serialize_field("qos", &qos_to_number(self.qos))?;
84//         state.serialize_field("retain", &self.retain)?;
85//         state.end()
86//     }
87// }
88
89// impl<'de> Deserialize<'de> for ChannelSenderDef {
90//     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
91//     where
92//         D: serde::Deserializer<'de>,
93//     {
94//         let state = deserializer.deserialize_struct("ChannelSenderDef", fields, visitor)
95//     }
96// }
97
98// impl Serialize for QoS {
99//     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
100//     where
101//         S: serde::Serializer,
102//     {
103//         todo!()
104//     }
105// }
106
107// impl<'de> Deserialize<'de> for QoS {
108//     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
109//     where
110//         D: serde::Deserializer<'de>,
111//     {
112//         todo!()
113//     }
114// }
115
116impl ChannelSenderDef {
117    pub fn retain(&self) -> bool {
118        self.retain
119    }
120}
121
122#[derive(Clone, Deserialize, Serialize)]
123pub struct ChannelReceiverDef {
124    pub name: String,
125    pub generated_topic: String,
126    pub topic: TetherOrCustomTopic,
127    #[serde(with = "QosDef")]
128    pub qos: QoS,
129}
130
131impl<'a> ChannelDef<'a> for ChannelSenderDef {
132    fn name(&'a self) -> &'a str {
133        &self.name
134    }
135
136    fn generated_topic(&'a self) -> &'a str {
137        &self.generated_topic
138    }
139
140    fn topic(&'a self) -> &'a TetherOrCustomTopic {
141        &self.topic
142    }
143
144    fn qos(&'a self) -> QoS {
145        self.qos
146    }
147}
148
149impl<'a> ChannelDef<'a> for ChannelReceiverDef {
150    fn name(&'a self) -> &'a str {
151        &self.name
152    }
153
154    fn generated_topic(&'a self) -> &'a str {
155        &self.generated_topic
156    }
157
158    fn topic(&'a self) -> &'a TetherOrCustomTopic {
159        &self.topic
160    }
161
162    fn qos(&'a self) -> QoS {
163        self.qos
164    }
165}
166
167#[cfg(test)]
168mod tests {
169
170    fn verbose_logging() {
171        use env_logger::{Builder, Env};
172        let mut logger_builder = Builder::from_env(Env::default().default_filter_or("debug"));
173        logger_builder.init();
174    }
175
176    use crate::{
177        builder::TetherAgentBuilder, ChannelDef, ChannelDefBuilder, ChannelReceiverDefBuilder,
178        ChannelSenderDefBuilder,
179    };
180
181    #[test]
182    fn default_receiver_channel() {
183        // verbose_logging();
184        let tether_agent = TetherAgentBuilder::new("tester")
185            .auto_connect(false)
186            .build()
187            .expect("sorry, these tests require working localhost Broker");
188        let receiver = ChannelReceiverDefBuilder::new("one").build(&tether_agent);
189        assert_eq!(&receiver.name, "one");
190        assert_eq!(&receiver.generated_topic, "+/one/#");
191    }
192
193    #[test]
194    /// This is a fairly trivial example, but contrast with the test
195    /// `sender_channel_default_but_agent_id_custom`: a custom ID was set for the
196    /// Agent, so does get added into the Topic for a Channel Receiver created without any
197    /// explicit overrides.
198    fn default_channel_receiver_with_agent_custom_id() {
199        // verbose_logging();
200        let tether_agent = TetherAgentBuilder::new("tester")
201            .auto_connect(false)
202            .id(Some("verySpecialGroup"))
203            .build()
204            .expect("sorry, these tests require working localhost Broker");
205        let receiver = tether_agent.create_receiver::<u8>("one").unwrap();
206        assert_eq!(receiver.definition().name(), "one");
207        assert_eq!(
208            receiver.definition().generated_topic(),
209            "+/one/verySpecialGroup"
210        );
211    }
212
213    #[test]
214    fn default_channel_sender() {
215        let tether_agent = TetherAgentBuilder::new("tester")
216            .auto_connect(false)
217            .build()
218            .expect("sorry, these tests require working localhost Broker");
219        let channel = tether_agent.create_sender::<u8>("two");
220        assert_eq!(channel.definition().name(), "two");
221        assert_eq!(channel.definition().generated_topic(), "tester/two");
222    }
223
224    #[test]
225    /// This is identical to the case in which a Channel Sender is created with defaults (no overrides),
226    /// BUT the Agent had a custom ID set, which means that the final topic includes this custom
227    /// ID/Group value.
228    fn sender_channel_default_but_agent_id_custom() {
229        let tether_agent = TetherAgentBuilder::new("tester")
230            .auto_connect(false)
231            .id(Some("specialCustomGrouping"))
232            .build()
233            .expect("sorry, these tests require working localhost Broker");
234        let channel = tether_agent.create_sender::<u8>("somethingStandard");
235        assert_eq!(channel.definition().name(), "somethingStandard");
236        assert_eq!(
237            channel.definition().generated_topic(),
238            "tester/somethingStandard/specialCustomGrouping"
239        );
240    }
241
242    #[test]
243    fn receiver_id_andor_role() {
244        let tether_agent = TetherAgentBuilder::new("tester")
245            .auto_connect(false)
246            .build()
247            .expect("sorry, these tests require working localhost Broker");
248
249        let receive_role_only = ChannelReceiverDefBuilder::new("theChannel")
250            .role(Some("specificRole"))
251            .build(&tether_agent);
252        assert_eq!(receive_role_only.name(), "theChannel");
253        assert_eq!(
254            receive_role_only.generated_topic(),
255            "specificRole/theChannel/#"
256        );
257
258        let receiver_id_only = ChannelReceiverDefBuilder::new("theChannel")
259            .id(Some("specificID"))
260            .build(&tether_agent);
261        assert_eq!(receiver_id_only.name(), "theChannel");
262        assert_eq!(
263            receiver_id_only.generated_topic(),
264            "+/theChannel/specificID"
265        );
266
267        let receiver_both_custom = ChannelReceiverDefBuilder::new("theChannel")
268            .id(Some("specificID"))
269            .role(Some("specificRole"))
270            .build(&tether_agent);
271        assert_eq!(receiver_both_custom.name(), "theChannel");
272        assert_eq!(
273            receiver_both_custom.generated_topic(),
274            "specificRole/theChannel/specificID"
275        );
276    }
277
278    #[test]
279    /// If the end-user implicitly specifies the chanel name part (does not set it to Some(_)
280    /// or None) then the ID and/or Role parts will change but the Channel Name part will
281    /// remain the "original" / default
282    /// Contrast with receiver_specific_id_andor_role_no_chanel_name below.
283    fn receiver_specific_id_andor_role_with_channel_name() {
284        let tether_agent = TetherAgentBuilder::new("tester")
285            .auto_connect(false)
286            .build()
287            .expect("sorry, these tests require working localhost Broker");
288
289        let receiver_role_only = ChannelReceiverDefBuilder::new("theChannel")
290            .role(Some("specificRole"))
291            .build(&tether_agent);
292        assert_eq!(receiver_role_only.name(), "theChannel");
293        assert_eq!(
294            receiver_role_only.generated_topic(),
295            "specificRole/theChannel/#"
296        );
297
298        let receiver_id_only = ChannelReceiverDefBuilder::new("theChannel")
299            .id(Some("specificID"))
300            .build(&tether_agent);
301        assert_eq!(receiver_id_only.name(), "theChannel");
302        assert_eq!(
303            receiver_id_only.generated_topic(),
304            "+/theChannel/specificID"
305        );
306
307        let receiver_both = ChannelReceiverDefBuilder::new("theChannel")
308            .id(Some("specificID"))
309            .role(Some("specificRole"))
310            .build(&tether_agent);
311        assert_eq!(receiver_both.name(), "theChannel");
312        assert_eq!(
313            receiver_both.generated_topic(),
314            "specificRole/theChannel/specificID"
315        );
316    }
317
318    #[test]
319    /// Unlike receiver_specific_id_andor_role_with_channel_name, this tests the situation where
320    /// the end-user (possibly) specifies the ID and/or Role, but also explicitly
321    /// sets the Channel Name to Some("+"), ie. "use a wildcard at this
322    /// position instead".
323    fn receiver_specific_id_andor_role_no_channel_name() {
324        verbose_logging();
325        let tether_agent = TetherAgentBuilder::new("tester")
326            .auto_connect(false)
327            .build()
328            .expect("sorry, these tests require working localhost Broker");
329
330        let receiver_role_only = ChannelReceiverDefBuilder::new("theOriginalChannel")
331            .override_name(Some("+"))
332            .role(Some("specificRole"))
333            .build(&tether_agent);
334        assert_eq!(receiver_role_only.name(), "+");
335        assert_eq!(receiver_role_only.generated_topic(), "specificRole/+/#");
336
337        let receiver_id_only = ChannelReceiverDefBuilder::new("+")
338            // .name(Some("+"))
339            .any_channel() // equivalent to Some("+")
340            .id(Some("specificID"))
341            .build(&tether_agent);
342        assert_eq!(receiver_id_only.name(), "+");
343        assert_eq!(receiver_id_only.generated_topic(), "+/+/specificID");
344
345        let receiver_both = ChannelReceiverDefBuilder::new("+")
346            .id(Some("specificID"))
347            .role(Some("specificRole"))
348            .build(&tether_agent);
349        assert_eq!(receiver_both.name(), "+");
350        assert_eq!(receiver_both.generated_topic(), "specificRole/+/specificID");
351    }
352
353    #[test]
354    /// Some fairly niche cases here
355    fn any_name_but_specify_role() {
356        let tether_agent = TetherAgentBuilder::new("tester")
357            .auto_connect(false)
358            .build()
359            .expect("sorry, these tests require working localhost Broker");
360
361        let receiver_any_channel = ChannelReceiverDefBuilder::new("aTest")
362            .any_channel()
363            .build(&tether_agent);
364
365        assert_eq!(receiver_any_channel.name(), "+");
366        assert_eq!(receiver_any_channel.generated_topic(), "+/+/#");
367
368        let receiver_specify_role = ChannelReceiverDefBuilder::new("aTest")
369            .any_channel()
370            .role(Some("brain"))
371            .build(&tether_agent);
372
373        assert_eq!(receiver_specify_role.name(), "+");
374        assert_eq!(receiver_specify_role.generated_topic(), "brain/+/#");
375    }
376
377    #[test]
378    fn sender_custom() {
379        let tether_agent = TetherAgentBuilder::new("tester")
380            .auto_connect(false)
381            .build()
382            .expect("sorry, these tests require working localhost Broker");
383
384        let sender_custom_role = ChannelSenderDefBuilder::new("theChannelSender")
385            .role(Some("customRole"))
386            .build(&tether_agent);
387        assert_eq!(sender_custom_role.name(), "theChannelSender");
388        assert_eq!(
389            sender_custom_role.generated_topic(),
390            "customRole/theChannelSender"
391        );
392
393        let sender_custom_id = ChannelSenderDefBuilder::new("theChannelSender")
394            .id(Some("customID"))
395            .build(&tether_agent);
396        assert_eq!(sender_custom_id.name(), "theChannelSender");
397        assert_eq!(
398            sender_custom_id.generated_topic(),
399            "tester/theChannelSender/customID"
400        );
401
402        let sender_custom_both = ChannelSenderDefBuilder::new("theChannelSender")
403            .role(Some("customRole"))
404            .id(Some("customID"))
405            .build(&tether_agent);
406        assert_eq!(sender_custom_both.name(), "theChannelSender");
407        assert_eq!(
408            sender_custom_both.generated_topic(),
409            "customRole/theChannelSender/customID"
410        );
411    }
412
413    #[test]
414    fn receiver_manual_topics() {
415        let tether_agent = TetherAgentBuilder::new("tester")
416            .auto_connect(false)
417            .build()
418            .expect("sorry, these tests require working localhost Broker");
419
420        let receiver_all = ChannelReceiverDefBuilder::new("everything")
421            .override_topic(Some("#"))
422            .build(&tether_agent);
423        assert_eq!(receiver_all.name(), "everything");
424        assert_eq!(receiver_all.generated_topic(), "#");
425
426        let receiver_nontether = ChannelReceiverDefBuilder::new("weird")
427            .override_topic(Some("foo/bar/baz/one/two/three"))
428            .build(&tether_agent);
429        assert_eq!(receiver_nontether.name(), "weird");
430        assert_eq!(
431            receiver_nontether.generated_topic(),
432            "foo/bar/baz/one/two/three"
433        );
434    }
435}