libits_client/mqtt/topic/
mod.rs1pub mod geo_extension;
10mod message_type;
11mod parse_error;
12mod queue;
13
14use std::{cmp, convert, fmt, hash, str, str::FromStr};
15
16use log::error;
17
18use crate::analyse::configuration::Configuration;
19use crate::mqtt::topic::geo_extension::{GeoExtension, Tile};
20use crate::mqtt::topic::message_type::MessageType;
21use crate::mqtt::topic::parse_error::ParseError;
22use crate::mqtt::topic::queue::Queue;
23
24#[derive(Default, Debug, Clone)]
25pub struct Topic {
27 project: String,
29
30 queue: Queue,
32 server: String,
33 message_type: MessageType,
34
35 uuid: String,
37
38 pub geo_extension: GeoExtension,
40}
41
42impl Topic {
43 fn empty() -> Topic {
44 Topic {
45 ..Default::default()
46 }
47 }
48
49 pub(crate) fn new<Q, T>(
50 queue: Option<Q>,
51 message_type: Option<T>,
52 uuid: Option<String>,
53 geo_extension: Option<GeoExtension>,
54 ) -> Topic
55 where
56 Q: Into<Queue> + Default,
57 T: Into<MessageType> + Default,
58 {
59 Topic {
60 project: "default".to_string(),
61 queue: match queue {
62 Some(into_queue) => into_queue.into(),
63 None => Queue::default(),
64 },
65 server: "v2x".to_string(),
66 message_type: match message_type {
67 Some(into_queue) => into_queue.into(),
68 None => MessageType::default(),
69 },
70 uuid: uuid.unwrap_or("+".to_string()),
71 geo_extension: geo_extension.unwrap_or_default(),
72
73 ..Default::default()
74 }
75 }
76
77 pub fn new_denm(component_name: String, geo_extension: &GeoExtension) -> Topic {
78 Topic::new(
79 Some("inQueue".to_string()),
80 Some("denm".to_string()),
81 Some(component_name),
82 Some(geo_extension.clone()),
84 )
85 }
86
87 pub fn project_base(&self) -> String {
88 format!(
89 "{}/{}/{}/{}",
90 self.project, self.queue, self.server, self.message_type
91 )
92 }
93
94 pub fn appropriate(&mut self, configuration: &Configuration) {
96 self.uuid = configuration.component_name(None);
97 self.queue = Queue::In;
98 }
99}
100
101impl hash::Hash for Topic {
102 fn hash<H: hash::Hasher>(&self, state: &mut H) {
103 self.project.hash(state);
104 self.queue.hash(state);
105 self.server.hash(state);
106 self.message_type.hash(state);
107 self.uuid.hash(state);
108 self.geo_extension.hash(state);
109 }
110}
111
112impl cmp::PartialEq for Topic {
113 fn eq(&self, other: &Self) -> bool {
114 self.project == other.project
115 && self.queue == other.queue
116 && self.server == other.server
117 && self.message_type == other.message_type
118 && self.uuid == other.uuid
119 && self.geo_extension == other.geo_extension
120 }
121}
122
123impl cmp::Eq for Topic {}
124
125impl cmp::PartialEq<String> for Topic {
126 fn eq(&self, other: &String) -> bool {
127 match Topic::from_str(other) {
128 Ok(topic) => self == &topic,
129 Err(error) => {
130 error!("We can't compare the topic with a bad string: {}", error);
131 false
132 }
133 }
134 }
135}
136
137impl convert::From<String> for Topic {
138 fn from(topic: String) -> Self {
139 Topic::from(topic.as_str())
140 }
141}
142
143impl convert::From<&str> for Topic {
144 fn from(topic: &str) -> Self {
145 match Topic::from_str(topic) {
146 Ok(topic) => topic,
147 Err(error) => panic!(
148 "Unable to convert the String {} as a Topic: {}, use from_str instead",
149 topic, error
150 ),
151 }
152 }
153}
154
155impl str::FromStr for Topic {
156 type Err = ParseError;
157
158 fn from_str(s: &str) -> Result<Self, Self::Err> {
159 s.trim_matches('/').split('/').enumerate().try_fold(
160 Topic::empty(),
161 |mut topic_struct, (i, element)| {
162 match i {
163 0 => topic_struct.project = element.to_string(),
165 1 => topic_struct.queue = Queue::from_str(element)?,
167 2 => topic_struct.server = element.to_string(),
169 3 => topic_struct.message_type = MessageType::from_str(element)?,
171 4 => topic_struct.uuid = element.to_string(),
173 _n => {
176 let result = Tile::from_str(element)?;
177 topic_struct.geo_extension.tiles.push(result)
178 }
179 }
180 Ok(topic_struct)
181 },
182 )
183 }
184}
185
186impl fmt::Display for Topic {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 write!(
189 f,
190 "{}/{}{}",
191 self.project_base(),
192 self.uuid,
193 self.geo_extension
194 )
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use crate::mqtt::topic::geo_extension::Tile;
201 use crate::mqtt::topic::message_type::MessageType;
202 use crate::mqtt::topic::queue::Queue;
203 use crate::mqtt::topic::Topic;
204 use std::str::FromStr;
205
206 #[test]
207 fn test_cam_topic_from_str() {
208 let topic_string = "default/outQueue/v2x/cam/car_1/0/1/2/3";
209 let topic_result = Topic::from_str(topic_string);
210 assert!(topic_result.is_ok());
211 let topic = topic_result.unwrap();
212 assert_eq!(topic.project, "default".to_string());
213 assert_eq!(topic.queue, Queue::Out);
214 assert_eq!(topic.server, "v2x".to_string());
215 assert_eq!(topic.message_type, MessageType::CAM);
216 assert_eq!(topic.uuid, "car_1".to_string());
217 assert_eq!(topic.geo_extension.tiles.len(), 4);
218 for i in 0..4 {
219 assert_eq!(topic.geo_extension.tiles[i], Tile::from(i as u8));
220 }
221 }
222
223 #[test]
224 fn test_denm_topic_from_str() {
225 let topic_string =
226 "default/outQueue/v2x/denm/wse_app_bcn1/1/2/0/2/2/2/2/3/3/0/0/3/2/0/2/0/1/0/1/0/3/1/";
227 let topic_result = Topic::from_str(topic_string);
228 assert!(topic_result.is_ok());
229 let topic = topic_result.unwrap();
230 assert_eq!(topic.project, "default".to_string());
231 assert_eq!(topic.queue, Queue::Out);
232 assert_eq!(topic.server, "v2x".to_string());
233 assert_eq!(topic.message_type, MessageType::DENM);
234 assert_eq!(topic.uuid, "wse_app_bcn1".to_string());
235 assert_eq!(topic.geo_extension.tiles.len(), 22);
236 }
237
238 #[test]
239 fn test_info_topic_from_str() {
240 let topic_string = "default/outQueue/v2x/info/broker";
241 let topic_result = Topic::from_str(topic_string);
242 assert!(topic_result.is_ok());
243 let topic = topic_result.unwrap();
244 assert_eq!(topic.project, "default".to_string());
245 assert_eq!(topic.queue, Queue::Out);
246 assert_eq!(topic.server, "v2x".to_string());
247 assert_eq!(topic.message_type, MessageType::INFO);
248 assert_eq!(topic.uuid, "broker".to_string());
249 assert_eq!(topic.geo_extension.tiles.len(), 0);
250 }
251
252 #[test]
253 fn test_in_queue_cam_topic_from_str() {
254 let topic_string = "default/inQueue/v2x/cam/car_1/0/1/2/3";
255 let topic_result = Topic::from_str(topic_string);
256 assert!(topic_result.is_ok());
257 let topic = topic_result.unwrap();
258 assert_eq!(topic.project, "default".to_string());
259 assert_eq!(topic.queue, Queue::In);
260 assert_eq!(topic.server, "v2x".to_string());
261 assert_eq!(topic.message_type, MessageType::CAM);
262 assert_eq!(topic.uuid, "car_1".to_string());
263 assert_eq!(topic.geo_extension.tiles.len(), 4);
264 for i in 0..4 {
265 assert_eq!(topic.geo_extension.tiles[i], Tile::from(i as u8));
266 }
267 }
268}