1mod uattributesvalidator;
15mod upayloadformat;
16mod upriority;
17
18use std::time::SystemTime;
19
20pub use uattributesvalidator::*;
21pub use upriority::*;
22
23pub use crate::up_core_api::uattributes::*;
24use crate::UUID;
25
26#[derive(Debug)]
27pub enum UAttributesError {
28 ValidationError(String),
29 ParsingError(String),
30}
31
32impl UAttributesError {
33 pub fn validation_error<T>(message: T) -> UAttributesError
34 where
35 T: Into<String>,
36 {
37 Self::ValidationError(message.into())
38 }
39
40 pub fn parsing_error<T>(message: T) -> UAttributesError
41 where
42 T: Into<String>,
43 {
44 Self::ParsingError(message.into())
45 }
46}
47
48impl std::fmt::Display for UAttributesError {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 match self {
51 Self::ValidationError(e) => f.write_fmt(format_args!("Validation failure: {}", e)),
52 Self::ParsingError(e) => f.write_fmt(format_args!("Parsing error: {}", e)),
53 }
54 }
55}
56
57impl std::error::Error for UAttributesError {}
58
59impl UAttributes {
60 pub fn is_publish(&self) -> bool {
74 self.type_.enum_value() == Ok(UMessageType::UMESSAGE_TYPE_PUBLISH)
75 }
76
77 pub fn is_request(&self) -> bool {
91 self.type_.enum_value() == Ok(UMessageType::UMESSAGE_TYPE_REQUEST)
92 }
93
94 pub fn is_response(&self) -> bool {
108 self.type_.enum_value() == Ok(UMessageType::UMESSAGE_TYPE_RESPONSE)
109 }
110
111 pub fn is_notification(&self) -> bool {
125 self.type_.enum_value() == Ok(UMessageType::UMESSAGE_TYPE_NOTIFICATION)
126 }
127
128 pub fn check_expired(&self) -> Result<(), UAttributesError> {
136 let ttl = match self.ttl {
137 Some(t) if t > 0 => u64::from(t),
138 _ => return Ok(()),
139 };
140
141 if let Some(creation_time) = self.id.as_ref().and_then(UUID::get_time) {
142 let delta = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
143 Ok(duration) => {
144 if let Ok(duration) = u64::try_from(duration.as_millis()) {
145 duration - creation_time
146 } else {
147 return Err(UAttributesError::validation_error(
148 "Invalid system time: too far in the future",
149 ));
150 }
151 }
152 Err(e) => return Err(UAttributesError::validation_error(e.to_string())),
153 };
154 if delta >= ttl {
155 return Err(UAttributesError::validation_error("message is expired"));
156 }
157 }
158 Ok(())
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use std::ops::Sub;
165 use std::time::{Duration, UNIX_EPOCH};
166
167 use super::*;
168 use test_case::test_case;
169
170 fn build_n_ms_in_past(n_ms_in_past: u64) -> UUID {
176 let duration_since_unix_epoch = SystemTime::now()
177 .duration_since(UNIX_EPOCH)
178 .expect("current system time is set to a point in time before UNIX Epoch");
179 UUID::build_for_timestamp(
180 duration_since_unix_epoch.sub(Duration::from_millis(n_ms_in_past)),
181 )
182 }
183
184 #[test_case(None, None, false; "for message without ID nor TTL")]
185 #[test_case(None, Some(0), false; "for message without ID with TTL 0")]
186 #[test_case(None, Some(500), false; "for message without ID with TTL")]
187 #[test_case(Some(build_n_ms_in_past(1000)), None, false; "for message with ID without TTL")]
188 #[test_case(Some(build_n_ms_in_past(1000)), Some(0), false; "for message with ID and TTL 0")]
189 #[test_case(Some(build_n_ms_in_past(1000)), Some(500), true; "for message with ID and expired TTL")]
190 #[test_case(Some(build_n_ms_in_past(1000)), Some(2000), false; "for message with ID and non-expired TTL")]
191 fn test_is_expired(id: Option<UUID>, ttl: Option<u32>, should_be_expired: bool) {
192 let attributes = UAttributes {
193 type_: UMessageType::UMESSAGE_TYPE_NOTIFICATION.into(),
194 priority: UPriority::UPRIORITY_CS1.into(),
195 id: id.into(),
196 ttl,
197 ..Default::default()
198 };
199
200 assert!(attributes.check_expired().is_err() == should_be_expired);
201 }
202}