model.rs (10082B)
1 use crate::RadrootsMeshError; 2 use alloc::string::{String, ToString}; 3 use alloc::vec::Vec; 4 use radroots_transport::Error as TransportError; 5 use radroots_transport::target::TargetScope; 6 7 pub const RADROOTS_MESH_FRAME_VERSION: u16 = 1; 8 pub const RADROOTS_MESH_UNAVAILABLE_MESSAGE: &str = 9 "Reticulum mesh delivery is unavailable until implementation"; 10 pub const RADROOTS_MESH_RETICULUM_POLICY_ID: &str = "reticulum_mesh_delivery"; 11 12 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 13 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 14 pub enum RadrootsMeshFrameType { 15 Hello, 16 EventHeadAnnounce, 17 EventRequest, 18 EventChunk, 19 EventAck, 20 RouteProbe, 21 } 22 23 impl RadrootsMeshFrameType { 24 pub fn code(self) -> u64 { 25 match self { 26 Self::Hello => 0, 27 Self::EventHeadAnnounce => 1, 28 Self::EventRequest => 2, 29 Self::EventChunk => 3, 30 Self::EventAck => 4, 31 Self::RouteProbe => 5, 32 } 33 } 34 35 pub fn parse_code(value: u64) -> Result<Self, RadrootsMeshError> { 36 match value { 37 0 => Ok(Self::Hello), 38 1 => Ok(Self::EventHeadAnnounce), 39 2 => Ok(Self::EventRequest), 40 3 => Ok(Self::EventChunk), 41 4 => Ok(Self::EventAck), 42 5 => Ok(Self::RouteProbe), 43 _ => Err(RadrootsMeshError::UnknownFrameType), 44 } 45 } 46 47 pub fn label(self) -> &'static str { 48 match self { 49 Self::Hello => "hello", 50 Self::EventHeadAnnounce => "event_head_announce", 51 Self::EventRequest => "event_request", 52 Self::EventChunk => "event_chunk", 53 Self::EventAck => "event_ack", 54 Self::RouteProbe => "route_probe", 55 } 56 } 57 } 58 59 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 60 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 61 pub enum RadrootsMeshScope { 62 Local, 63 Community, 64 Custom(String), 65 } 66 67 impl RadrootsMeshScope { 68 pub fn custom(value: impl AsRef<str>) -> Result<Self, RadrootsMeshError> { 69 let scope = TargetScope::parse(value.as_ref()).map_err(|err| match err { 70 TransportError::EmptyTargetScope => RadrootsMeshError::EmptyCustomScope, 71 _ => RadrootsMeshError::InvalidCustomScope, 72 })?; 73 Ok(Self::Custom(scope.as_str().to_string())) 74 } 75 76 pub fn label(&self) -> &str { 77 match self { 78 Self::Local => "local", 79 Self::Community => "community", 80 Self::Custom(value) => value.as_str(), 81 } 82 } 83 84 pub fn parse(value: &str) -> Result<Self, RadrootsMeshError> { 85 match value { 86 "local" => Ok(Self::Local), 87 "community" => Ok(Self::Community), 88 value if value.starts_with("custom:") => Self::custom(&value["custom:".len()..]), 89 _ => Err(RadrootsMeshError::UnknownScope), 90 } 91 } 92 93 pub fn cbor_label(&self) -> String { 94 match self { 95 Self::Custom(value) => { 96 let mut label = String::from("custom:"); 97 label.push_str(value); 98 label 99 } 100 _ => self.label().to_string(), 101 } 102 } 103 } 104 105 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 106 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 107 pub enum RadrootsMeshPrivacyClass { 108 PublicEvent, 109 PrivateEvent, 110 } 111 112 impl RadrootsMeshPrivacyClass { 113 pub fn label(self) -> &'static str { 114 match self { 115 Self::PublicEvent => "public_event", 116 Self::PrivateEvent => "private_event", 117 } 118 } 119 } 120 121 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 122 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 123 pub enum RadrootsMeshCompressionPolicy { 124 Disabled, 125 } 126 127 impl RadrootsMeshCompressionPolicy { 128 pub fn label(self) -> &'static str { 129 match self { 130 Self::Disabled => "disabled", 131 } 132 } 133 } 134 135 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 136 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 137 pub enum RadrootsMeshPolicyDenyReason { 138 DeliveryUnavailable, 139 PayloadBudgetExceeded, 140 CustomScopeUnavailable, 141 } 142 143 impl RadrootsMeshPolicyDenyReason { 144 pub fn label(self) -> &'static str { 145 match self { 146 Self::DeliveryUnavailable => "delivery_unavailable", 147 Self::PayloadBudgetExceeded => "payload_budget_exceeded", 148 Self::CustomScopeUnavailable => "custom_scope_unavailable", 149 } 150 } 151 152 pub fn message(self) -> &'static str { 153 match self { 154 Self::DeliveryUnavailable => RADROOTS_MESH_UNAVAILABLE_MESSAGE, 155 Self::PayloadBudgetExceeded => "mesh payload exceeds the configured delivery budget", 156 Self::CustomScopeUnavailable => { 157 "mesh custom scopes are not enabled for the configured policy" 158 } 159 } 160 } 161 } 162 163 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 164 #[derive(Clone, Debug, PartialEq, Eq)] 165 pub enum RadrootsMeshAdmissionDecision { 166 Accepted, 167 Denied { 168 reason: RadrootsMeshPolicyDenyReason, 169 }, 170 } 171 172 impl RadrootsMeshAdmissionDecision { 173 pub fn label(&self) -> &'static str { 174 match self { 175 Self::Accepted => "accepted", 176 Self::Denied { .. } => "denied", 177 } 178 } 179 180 pub fn deny_reason(&self) -> Option<RadrootsMeshPolicyDenyReason> { 181 match self { 182 Self::Accepted => None, 183 Self::Denied { reason } => Some(*reason), 184 } 185 } 186 187 pub fn message(&self) -> &'static str { 188 match self { 189 Self::Accepted => "mesh payload delivery is admitted", 190 Self::Denied { reason } => reason.message(), 191 } 192 } 193 194 pub fn usable_for_delivery(&self) -> bool { 195 matches!(self, Self::Accepted) 196 } 197 } 198 199 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 200 #[derive(Clone, Debug, PartialEq, Eq)] 201 pub struct RadrootsMeshAdmissionInput { 202 pub scope: RadrootsMeshScope, 203 pub privacy_class: RadrootsMeshPrivacyClass, 204 pub payload_bytes: u64, 205 pub frame_bytes: u64, 206 } 207 208 impl RadrootsMeshAdmissionInput { 209 pub fn new( 210 scope: RadrootsMeshScope, 211 privacy_class: RadrootsMeshPrivacyClass, 212 payload_bytes: u64, 213 frame_bytes: u64, 214 ) -> Self { 215 Self { 216 scope, 217 privacy_class, 218 payload_bytes, 219 frame_bytes, 220 } 221 } 222 } 223 224 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 225 #[derive(Clone, Debug, PartialEq, Eq)] 226 pub struct RadrootsMeshPayloadPolicy { 227 pub max_payload_bytes: u64, 228 pub max_frame_bytes: u64, 229 pub compression: RadrootsMeshCompressionPolicy, 230 pub custom_scopes_enabled: bool, 231 } 232 233 impl RadrootsMeshPayloadPolicy { 234 pub fn reticulum_unavailable() -> Self { 235 Self { 236 max_payload_bytes: 0, 237 max_frame_bytes: 0, 238 compression: RadrootsMeshCompressionPolicy::Disabled, 239 custom_scopes_enabled: false, 240 } 241 } 242 243 pub fn policy_id(&self) -> &'static str { 244 RADROOTS_MESH_RETICULUM_POLICY_ID 245 } 246 247 pub fn usable_for_delivery(&self) -> bool { 248 self.max_payload_bytes > 0 && self.max_frame_bytes > 0 249 } 250 251 pub fn evaluate(&self, input: &RadrootsMeshAdmissionInput) -> RadrootsMeshAdmissionDecision { 252 if !self.usable_for_delivery() { 253 return RadrootsMeshAdmissionDecision::Denied { 254 reason: RadrootsMeshPolicyDenyReason::DeliveryUnavailable, 255 }; 256 } 257 if matches!(input.scope, RadrootsMeshScope::Custom(_)) && !self.custom_scopes_enabled { 258 return RadrootsMeshAdmissionDecision::Denied { 259 reason: RadrootsMeshPolicyDenyReason::CustomScopeUnavailable, 260 }; 261 } 262 if input.payload_bytes > self.max_payload_bytes || input.frame_bytes > self.max_frame_bytes 263 { 264 return RadrootsMeshAdmissionDecision::Denied { 265 reason: RadrootsMeshPolicyDenyReason::PayloadBudgetExceeded, 266 }; 267 } 268 RadrootsMeshAdmissionDecision::Accepted 269 } 270 } 271 272 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 273 #[derive(Clone, Debug, PartialEq, Eq)] 274 pub enum RadrootsMeshPayload { 275 EmptyMap, 276 Bytes(Vec<u8>), 277 } 278 279 impl RadrootsMeshPayload { 280 pub fn empty() -> Self { 281 Self::EmptyMap 282 } 283 284 pub fn validate(&self) -> Result<(), RadrootsMeshError> { 285 match self { 286 Self::EmptyMap => Ok(()), 287 Self::Bytes(_) => Err(RadrootsMeshError::PayloadTransmissionForbidden), 288 } 289 } 290 } 291 292 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 293 #[derive(Clone, Debug, PartialEq, Eq)] 294 pub struct RadrootsMeshFrame { 295 pub version: u16, 296 pub frame_type: RadrootsMeshFrameType, 297 pub scope_id: RadrootsMeshScope, 298 pub message_id: String, 299 pub created_at_ms: u64, 300 pub ttl: u64, 301 pub payload: RadrootsMeshPayload, 302 } 303 304 impl RadrootsMeshFrame { 305 pub fn new( 306 frame_type: RadrootsMeshFrameType, 307 scope_id: RadrootsMeshScope, 308 message_id: impl Into<String>, 309 created_at_ms: u64, 310 ttl: u64, 311 ) -> Self { 312 Self { 313 version: RADROOTS_MESH_FRAME_VERSION, 314 frame_type, 315 scope_id, 316 message_id: message_id.into(), 317 created_at_ms, 318 ttl, 319 payload: RadrootsMeshPayload::empty(), 320 } 321 } 322 323 pub fn validate(&self) -> Result<(), RadrootsMeshError> { 324 if self.version != RADROOTS_MESH_FRAME_VERSION { 325 return Err(RadrootsMeshError::UnsupportedVersion); 326 } 327 if self.message_id.trim().is_empty() { 328 return Err(RadrootsMeshError::EmptyMessageId); 329 } 330 if self.ttl == 0 { 331 return Err(RadrootsMeshError::InvalidTtl); 332 } 333 self.payload.validate() 334 } 335 }