tether_agent/plugs/
three_part_topic.rs1use anyhow::anyhow;
2use log::{debug, error};
3use serde::{Deserialize, Serialize};
4
5use crate::TetherAgent;
6
7#[derive(Serialize, Deserialize, Debug)]
8pub struct ThreePartTopic {
9 role: String,
10 id: String,
11 plug_name: String,
12 full_topic: String,
13}
14
15#[derive(Serialize, Deserialize, Debug)]
16pub enum TetherOrCustomTopic {
17 Tether(ThreePartTopic),
18 Custom(String),
19}
20
21impl TetherOrCustomTopic {
22 pub fn full_topic_string(&self) -> String {
23 match self {
24 TetherOrCustomTopic::Tether(three_part_topic) => String::from(three_part_topic.topic()),
25 TetherOrCustomTopic::Custom(t) => String::from(t),
26 }
27 }
28}
29
30impl ThreePartTopic {
31 pub fn new_for_publish(
33 role: Option<&str>,
34 id: Option<&str>,
35 plug_name: &str,
36 agent: &TetherAgent,
37 ) -> ThreePartTopic {
38 let role = role.unwrap_or(agent.role());
39 let id = id.unwrap_or(agent.id());
40 let full_topic = build_topic(role, id, plug_name);
41 ThreePartTopic {
42 role: role.into(),
43 id: id.into(),
44 plug_name: plug_name.into(),
45 full_topic,
46 }
47 }
48
49 pub fn new_for_subscribe(
53 plug_name: &str,
54 role_part_override: Option<&str>,
55 id_part_override: Option<&str>,
56 plug_name_part_override: Option<&str>,
57 ) -> ThreePartTopic {
58 let role = role_part_override.unwrap_or("+");
59 let id = id_part_override.unwrap_or("+");
60 let plug_name_part = match plug_name_part_override {
61 Some(s) => {
62 if !&s.eq("+") {
63 error!("The only valid override for the Plug Name part is a wildcard (+)");
64 }
65 s
66 }
67 None => plug_name,
68 };
69 let full_topic = build_topic(role, id, plug_name_part);
70
71 ThreePartTopic {
72 role: role.into(),
73 id: id.into(),
74 plug_name: plug_name_part.into(),
75 full_topic,
76 }
77 }
78
79 pub fn new(role: &str, id: &str, plug_name: &str) -> ThreePartTopic {
80 ThreePartTopic {
81 role: role.into(),
82 id: id.into(),
83 plug_name: plug_name.into(),
84 full_topic: build_topic(role, id, plug_name),
85 }
86 }
87
88 pub fn topic(&self) -> &str {
89 &self.full_topic
90 }
91
92 pub fn role(&self) -> &str {
93 &self.role
94 }
95
96 pub fn id(&self) -> &str {
97 &self.id
98 }
99
100 pub fn plug_name(&self) -> &str {
101 &self.plug_name
102 }
103
104 pub fn set_role(&mut self, role: &str) {
105 self.role = role.into();
106 self.update_full_topic();
107 }
108
109 pub fn set_id(&mut self, id: &str) {
110 self.id = id.into();
111 self.update_full_topic();
112 }
113
114 pub fn set_plug_name(&mut self, plug_name: &str) {
115 self.plug_name = plug_name.into();
116 self.update_full_topic();
117 }
118
119 fn update_full_topic(&mut self) {
120 self.full_topic = build_topic(&self.role, &self.id, &self.plug_name);
121 }
122}
123
124impl TryFrom<&str> for ThreePartTopic {
125 type Error = anyhow::Error;
126
127 fn try_from(value: &str) -> Result<Self, Self::Error> {
129 let parts = value.split('/').collect::<Vec<&str>>();
130
131 if parts.len() != 3 {
132 return Err(anyhow!(
133 "Did not find exactly three parts in the topic {}",
134 value
135 ));
136 } else {
137 debug!("parts: {:?}", parts);
138 }
139
140 let role = parts.first().expect("the role part should exist");
141 let id = parts.get(1).expect("the id part should exist");
142 let plug_name = parts.get(2).expect("the plug_name part should exist");
143
144 Ok(ThreePartTopic::new(role, id, plug_name))
145 }
146}
147
148pub fn build_topic(role: &str, id: &str, plug_name: &str) -> String {
149 format!("{role}/{id}/{plug_name}")
150}
151
152pub fn parse_plug_name(topic: &str) -> Option<&str> {
153 let parts: Vec<&str> = topic.split('/').collect();
154 match parts.get(2) {
155 Some(s) => Some(*s),
156 None => None,
157 }
158}
159
160pub fn parse_agent_id(topic: &str) -> Option<&str> {
161 let parts: Vec<&str> = topic.split('/').collect();
162 match parts.get(1) {
163 Some(s) => Some(*s),
164 None => None,
165 }
166}
167
168pub fn parse_agent_role(topic: &str) -> Option<&str> {
169 let parts: Vec<&str> = topic.split('/').collect();
170 match parts.first() {
171 Some(s) => Some(*s),
172 None => None,
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use crate::three_part_topic::{parse_agent_id, parse_agent_role, parse_plug_name};
179
180 #[test]
181 fn util_parsers() {
182 assert_eq!(parse_agent_role("one/two/three"), Some("one"));
183 assert_eq!(parse_agent_id("one/two/three"), Some("two"));
184 assert_eq!(parse_plug_name("one/two/three"), Some("three"));
185 assert_eq!(parse_plug_name("just/two"), None);
186 }
187}