rumqttc/v5/mqttbytes/
mod.rs1use std::{str::Utf8Error, vec};
2
3pub mod v5;
8
9#[repr(u8)]
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
12#[allow(clippy::enum_variant_names)]
13pub enum QoS {
14 AtMostOnce = 0,
15 AtLeastOnce = 1,
16 ExactlyOnce = 2,
17}
18
19impl Default for QoS {
20 fn default() -> Self {
21 Self::AtMostOnce
22 }
23}
24
25pub fn qos(num: u8) -> Option<QoS> {
27 match num {
28 0 => Some(QoS::AtMostOnce),
29 1 => Some(QoS::AtLeastOnce),
30 2 => Some(QoS::ExactlyOnce),
31 _ => None,
32 }
33}
34
35pub fn has_wildcards(s: &str) -> bool {
37 s.contains('+') || s.contains('#')
38}
39
40pub fn valid_topic(topic: &str) -> bool {
42 if topic.contains('+') || topic.contains('#') {
44 return false;
45 }
46
47 true
48}
49
50pub fn valid_filter(filter: &str) -> bool {
54 if filter.is_empty() {
55 return false;
56 }
57
58 let mut hirerarchy = filter.split('/').rev();
60
61 let last = hirerarchy.next().unwrap();
65
66 if last.len() != 1 && (last.contains('#') || last.contains('+')) {
70 return false;
71 }
72
73 for entry in hirerarchy {
75 if entry.contains('#') {
79 return false;
80 }
81
82 if entry.len() > 1 && entry.contains('+') {
85 return false;
86 }
87 }
88
89 true
90}
91
92pub fn matches(topic: &str, filter: &str) -> bool {
98 if !topic.is_empty() && topic[..1].contains('$') {
99 return false;
100 }
101
102 let mut topics = topic.split('/');
103 let mut filters = filter.split('/');
104
105 for f in filters.by_ref() {
106 if f == "#" {
108 return true;
109 }
110
111 let top = topics.next();
115 match top {
116 Some("#") => return false,
117 Some(_) if f == "+" => continue,
118 Some(t) if f != t => return false,
119 Some(_) => continue,
120 None => return false,
121 }
122 }
123
124 if topics.next().is_some() {
126 return false;
127 }
128
129 true
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
134pub enum Error {
135 #[error("Invalid return code received as response for connect = {0}")]
136 InvalidConnectReturnCode(u8),
137 #[error("Invalid reason = {0}")]
138 InvalidReason(u8),
139 #[error("Invalid remaining length = {0}")]
140 InvalidRemainingLength(usize),
141 #[error("Invalid protocol used")]
142 InvalidProtocol,
143 #[error("Invalid protocol level")]
144 InvalidProtocolLevel(u8),
145 #[error("Invalid packet format")]
146 IncorrectPacketFormat,
147 #[error("Invalid packet type = {0}")]
148 InvalidPacketType(u8),
149 #[error("Invalid retain forward rule = {0}")]
150 InvalidRetainForwardRule(u8),
151 #[error("Invalid QoS level = {0}")]
152 InvalidQoS(u8),
153 #[error("Invalid subscribe reason code = {0}")]
154 InvalidSubscribeReasonCode(u8),
155 #[error("Packet received has id Zero")]
156 PacketIdZero,
157 #[error("Empty Subscription")]
158 EmptySubscription,
159 #[error("Subscription had id Zero")]
160 SubscriptionIdZero,
161 #[error("Payload size is incorrect")]
162 PayloadSizeIncorrect,
163 #[error("Payload is too long")]
164 PayloadTooLong,
165 #[error("Max Payload size of {max:?} has been exceeded by packet of {pkt_size:?} bytes")]
166 PayloadSizeLimitExceeded { pkt_size: usize, max: usize },
167 #[error("Payload is required")]
168 PayloadRequired,
169 #[error("Payload is required = {0}")]
170 PayloadNotUtf8(#[from] Utf8Error),
171 #[error("Topic not utf-8")]
172 TopicNotUtf8,
173 #[error("Promised boundary crossed, contains {0} bytes")]
174 BoundaryCrossed(usize),
175 #[error("Packet is malformed")]
176 MalformedPacket,
177 #[error("Remaining length is malformed")]
178 MalformedRemainingLength,
179 #[error("Invalid property type = {0}")]
180 InvalidPropertyType(u8),
181 #[error("Insufficient number of bytes to frame packet, {0} more bytes required")]
185 InsufficientBytes(usize),
186}