mqtt_codec_kit/common/
topic_name.rs1use std::{
4 borrow::{Borrow, BorrowMut},
5 io::{self, Read, Write},
6 ops::{Deref, DerefMut},
7 str::FromStr,
8};
9
10use crate::common::{Decodable, Encodable};
11
12#[inline]
13fn is_invalid_topic_name(topic_name: &str) -> bool {
14 topic_name.is_empty()
15 || topic_name.as_bytes().len() > 65535
16 || topic_name.chars().any(|ch| ch == '#' || ch == '+')
17}
18
19#[derive(Debug, Eq, PartialEq, Clone, Hash, Ord, PartialOrd)]
24pub struct TopicName(String);
25
26impl TopicName {
27 pub fn new<S: Into<String>>(topic_name: S) -> Result<TopicName, TopicNameError> {
30 let topic_name = topic_name.into();
31 if is_invalid_topic_name(&topic_name) {
32 Err(TopicNameError(topic_name))
33 } else {
34 Ok(TopicName(topic_name))
35 }
36 }
37
38 pub unsafe fn new_unchecked(topic_name: String) -> TopicName {
47 TopicName(topic_name)
48 }
49}
50
51impl From<TopicName> for String {
52 fn from(topic_name: TopicName) -> String {
53 topic_name.0
54 }
55}
56
57impl FromStr for TopicName {
58 type Err = TopicNameError;
59
60 fn from_str(s: &str) -> Result<Self, Self::Err> {
61 TopicName::new(s)
62 }
63}
64
65impl Deref for TopicName {
66 type Target = TopicNameRef;
67
68 fn deref(&self) -> &TopicNameRef {
69 unsafe { TopicNameRef::new_unchecked(&self.0) }
70 }
71}
72
73impl DerefMut for TopicName {
74 fn deref_mut(&mut self) -> &mut Self::Target {
75 unsafe { TopicNameRef::new_mut_unchecked(&mut self.0) }
76 }
77}
78
79impl Borrow<TopicNameRef> for TopicName {
80 fn borrow(&self) -> &TopicNameRef {
81 Deref::deref(self)
82 }
83}
84
85impl BorrowMut<TopicNameRef> for TopicName {
86 fn borrow_mut(&mut self) -> &mut TopicNameRef {
87 DerefMut::deref_mut(self)
88 }
89}
90
91impl Encodable for TopicName {
92 fn encode<W: Write>(&self, writer: &mut W) -> Result<(), io::Error> {
93 (&self.0[..]).encode(writer)
94 }
95
96 fn encoded_length(&self) -> u32 {
97 (&self.0[..]).encoded_length()
98 }
99}
100
101impl Decodable for TopicName {
102 type Error = TopicNameDecodeError;
103 type Cond = ();
104
105 fn decode_with<R: Read>(reader: &mut R, _rest: ()) -> Result<TopicName, TopicNameDecodeError> {
106 let topic_name = String::decode(reader)?;
107 Ok(TopicName::new(topic_name)?)
108 }
109}
110
111#[derive(Debug, thiserror::Error)]
112#[error("invalid topic filter ({0})")]
113pub struct TopicNameError(pub String);
114
115#[derive(Debug, thiserror::Error)]
117#[error(transparent)]
118pub enum TopicNameDecodeError {
119 IoError(#[from] io::Error),
120 InvalidTopicName(#[from] TopicNameError),
121}
122
123#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
125#[repr(transparent)]
126pub struct TopicNameRef(str);
127
128impl TopicNameRef {
129 pub fn new<S: AsRef<str> + ?Sized>(topic_name: &S) -> Result<&TopicNameRef, TopicNameError> {
132 let topic_name = topic_name.as_ref();
133 if is_invalid_topic_name(topic_name) {
134 Err(TopicNameError(topic_name.to_owned()))
135 } else {
136 Ok(unsafe { &*(topic_name as *const str as *const TopicNameRef) })
137 }
138 }
139
140 pub fn new_mut<S: AsMut<str> + ?Sized>(
143 topic_name: &mut S,
144 ) -> Result<&mut TopicNameRef, TopicNameError> {
145 let topic_name = topic_name.as_mut();
146 if is_invalid_topic_name(topic_name) {
147 Err(TopicNameError(topic_name.to_owned()))
148 } else {
149 Ok(unsafe { &mut *(topic_name as *mut str as *mut TopicNameRef) })
150 }
151 }
152
153 pub unsafe fn new_unchecked<S: AsRef<str> + ?Sized>(topic_name: &S) -> &TopicNameRef {
162 let topic_name = topic_name.as_ref();
163 &*(topic_name as *const str as *const TopicNameRef)
164 }
165
166 pub unsafe fn new_mut_unchecked<S: AsMut<str> + ?Sized>(
175 topic_name: &mut S,
176 ) -> &mut TopicNameRef {
177 let topic_name = topic_name.as_mut();
178 &mut *(topic_name as *mut str as *mut TopicNameRef)
179 }
180
181 pub fn is_server_specific(&self) -> bool {
185 self.0.starts_with('$')
186 }
187}
188
189impl Deref for TopicNameRef {
190 type Target = str;
191
192 fn deref(&self) -> &str {
193 &self.0
194 }
195}
196
197impl ToOwned for TopicNameRef {
198 type Owned = TopicName;
199
200 fn to_owned(&self) -> Self::Owned {
201 TopicName(self.0.to_owned())
202 }
203}
204
205impl Encodable for TopicNameRef {
206 fn encode<W: Write>(&self, writer: &mut W) -> Result<(), io::Error> {
207 (&self.0[..]).encode(writer)
208 }
209
210 fn encoded_length(&self) -> u32 {
211 (&self.0[..]).encoded_length()
212 }
213}
214
215#[derive(Debug, Eq, PartialEq, Clone)]
217pub struct TopicNameHeader(TopicName);
218
219impl TopicNameHeader {
220 pub fn new(topic_name: String) -> Result<TopicNameHeader, TopicNameDecodeError> {
221 match TopicName::new(topic_name) {
222 Ok(h) => Ok(TopicNameHeader(h)),
223 Err(err) => Err(TopicNameDecodeError::InvalidTopicName(err)),
224 }
225 }
226}
227
228impl From<TopicNameHeader> for TopicName {
229 fn from(hdr: TopicNameHeader) -> Self {
230 hdr.0
231 }
232}
233
234impl Encodable for TopicNameHeader {
235 fn encode<W: Write>(&self, writer: &mut W) -> Result<(), io::Error> {
236 (&self.0[..]).encode(writer)
237 }
238
239 fn encoded_length(&self) -> u32 {
240 (&self.0[..]).encoded_length()
241 }
242}
243
244impl Decodable for TopicNameHeader {
245 type Error = TopicNameDecodeError;
246 type Cond = ();
247
248 fn decode_with<R: Read>(
249 reader: &mut R,
250 _rest: (),
251 ) -> Result<TopicNameHeader, TopicNameDecodeError> {
252 TopicNameHeader::new(Decodable::decode(reader)?)
253 }
254}
255
256#[cfg(test)]
257mod test {
258 use super::*;
259
260 #[test]
261 fn topic_name_sys() {
262 let topic_name = "$SYS".to_owned();
263 TopicName::new(topic_name).unwrap();
264
265 let topic_name = "$SYS/broker/connection/test.cosm-energy/state".to_owned();
266 TopicName::new(topic_name).unwrap();
267 }
268
269 #[test]
270 fn topic_name_slash() {
271 TopicName::new("/").unwrap();
272 }
273
274 #[test]
275 fn topic_name_basic() {
276 TopicName::new("/finance").unwrap();
277 TopicName::new("/finance//def").unwrap();
278 }
279}