lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

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 }