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