lib.rs (9355B)
1 #![forbid(unsafe_code)] 2 3 use radroots_mesh::{ 4 RADROOTS_MESH_RETICULUM_POLICY_ID, RADROOTS_MESH_UNAVAILABLE_MESSAGE, RadrootsMeshPayloadPolicy, 5 }; 6 use radroots_mesh_agent_proto::{ 7 RADROOTS_MESH_AGENT_SCHEMA_ID, RADROOTS_MESH_AGENT_SCHEMA_NAMESPACE, schema_sha256_hex, 8 }; 9 use radroots_transport::TransportId; 10 use radroots_transport::target::TargetScope; 11 use radroots_transport_reticulum::{RADROOTS_RETICULUM_ENDPOINT_URI, RADROOTS_RETICULUM_SCOPE_ID}; 12 13 pub const RADROOTS_MESH_AGENT_CLIENT_SCHEMA_ID: &str = RADROOTS_MESH_AGENT_SCHEMA_ID; 14 pub const RADROOTS_MESH_AGENT_CLIENT_SCHEMA_NAMESPACE: &str = RADROOTS_MESH_AGENT_SCHEMA_NAMESPACE; 15 16 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 17 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] 18 #[derive(Clone, Debug, PartialEq, Eq)] 19 pub struct MeshAgentStatusRequest { 20 pub include_transports: bool, 21 } 22 23 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 24 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] 25 #[derive(Clone, Debug, PartialEq, Eq)] 26 pub struct MeshAgentStatusResponse { 27 pub transports: Vec<MeshAgentTransportStatus>, 28 } 29 30 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 31 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] 32 #[derive(Clone, Debug, PartialEq, Eq)] 33 pub struct MeshAgentTransportStatus { 34 pub transport: MeshAgentTransportKind, 35 pub profile_id: String, 36 pub endpoint_uri: String, 37 pub configured: bool, 38 pub implementation: MeshAgentImplementation, 39 pub maturity: MeshAgentCapabilityMaturity, 40 pub availability: MeshAgentCapabilityAvailability, 41 pub usable_for_delivery: bool, 42 pub message: String, 43 } 44 45 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 46 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] 47 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 48 pub enum MeshAgentImplementation { 49 Real, 50 Mock, 51 } 52 53 impl MeshAgentImplementation { 54 pub fn schema_name(self) -> &'static str { 55 match self { 56 Self::Real => "real", 57 Self::Mock => "mock", 58 } 59 } 60 } 61 62 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 63 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] 64 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 65 pub enum MeshAgentCapabilityMaturity { 66 Preview, 67 Stable, 68 } 69 70 impl MeshAgentCapabilityMaturity { 71 pub fn schema_name(self) -> &'static str { 72 match self { 73 Self::Preview => "preview", 74 Self::Stable => "stable", 75 } 76 } 77 } 78 79 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 80 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] 81 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 82 pub enum MeshAgentCapabilityAvailability { 83 Available, 84 Degraded, 85 Unavailable, 86 } 87 88 impl MeshAgentCapabilityAvailability { 89 pub fn schema_name(self) -> &'static str { 90 match self { 91 Self::Available => "available", 92 Self::Degraded => "degraded", 93 Self::Unavailable => "unavailable", 94 } 95 } 96 } 97 98 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 99 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] 100 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 101 pub enum MeshAgentTransportKind { 102 Reticulum, 103 } 104 105 impl MeshAgentTransportKind { 106 pub fn schema_name(self) -> &'static str { 107 match self { 108 Self::Reticulum => "reticulum", 109 } 110 } 111 112 pub fn transport_kind(self) -> TransportId { 113 match self { 114 Self::Reticulum => TransportId::RETICULUM, 115 } 116 } 117 } 118 119 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 120 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] 121 #[derive(Clone, Debug, PartialEq, Eq)] 122 pub struct MeshAgentPublishRequest { 123 pub publish_request_id: String, 124 pub payload_cbor: Vec<u8>, 125 pub event_id: String, 126 pub target_fingerprint: String, 127 } 128 129 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 130 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] 131 #[derive(Clone, Debug, PartialEq, Eq)] 132 pub struct MeshAgentPublishResponse { 133 pub publish_request_id: String, 134 pub status: MeshAgentResponseStatus, 135 pub transport_receipts: Vec<MeshAgentTransportReceipt>, 136 pub event_id: String, 137 } 138 139 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 140 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] 141 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 142 pub enum MeshAgentResponseStatus { 143 Accepted, 144 Deferred, 145 Rejected, 146 } 147 148 impl MeshAgentResponseStatus { 149 pub fn schema_name(self) -> &'static str { 150 match self { 151 Self::Accepted => "accepted", 152 Self::Deferred => "deferred", 153 Self::Rejected => "rejected", 154 } 155 } 156 } 157 158 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 159 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] 160 #[derive(Clone, Debug, PartialEq, Eq)] 161 pub struct MeshAgentTransportReceipt { 162 pub transport_kind: MeshAgentTransportKind, 163 pub endpoint_uri: String, 164 pub outcome: MeshAgentTransportOutcome, 165 pub message: String, 166 } 167 168 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 169 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] 170 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 171 pub enum MeshAgentTransportOutcome { 172 Accepted, 173 Delivered, 174 Forwarded, 175 StoredByGateway, 176 DeferredUntilImplemented, 177 Rejected, 178 RouteUnavailable, 179 Timeout, 180 TransportUnavailable, 181 } 182 183 impl MeshAgentTransportOutcome { 184 pub fn schema_name(self) -> &'static str { 185 match self { 186 Self::Accepted => "accepted", 187 Self::Delivered => "delivered", 188 Self::Forwarded => "forwarded", 189 Self::StoredByGateway => "storedByGateway", 190 Self::DeferredUntilImplemented => "deferredUntilImplemented", 191 Self::Rejected => "rejected", 192 Self::RouteUnavailable => "routeUnavailable", 193 Self::Timeout => "timeout", 194 Self::TransportUnavailable => "transportUnavailable", 195 } 196 } 197 198 pub fn is_success(self) -> bool { 199 matches!( 200 self, 201 Self::Accepted | Self::Delivered | Self::Forwarded | Self::StoredByGateway 202 ) 203 } 204 } 205 206 pub trait RadrootsMeshAgentClient { 207 fn status(&self, request: MeshAgentStatusRequest) -> MeshAgentStatusResponse; 208 fn publish(&self, request: MeshAgentPublishRequest) -> MeshAgentPublishResponse; 209 fn schema_sha256_hex(&self) -> String; 210 } 211 212 #[derive(Clone, Debug, PartialEq, Eq)] 213 pub struct RadrootsMockMeshAgentClient { 214 profile_id: String, 215 endpoint_uri: String, 216 scope: TargetScope, 217 policy: RadrootsMeshPayloadPolicy, 218 } 219 220 impl RadrootsMockMeshAgentClient { 221 pub fn reticulum_unavailable() -> Self { 222 Self { 223 profile_id: RADROOTS_MESH_RETICULUM_POLICY_ID.to_owned(), 224 endpoint_uri: RADROOTS_RETICULUM_ENDPOINT_URI.to_owned(), 225 scope: TargetScope::parse(RADROOTS_RETICULUM_SCOPE_ID).expect("Reticulum scope"), 226 policy: RadrootsMeshPayloadPolicy::reticulum_unavailable(), 227 } 228 } 229 230 pub fn scope(&self) -> &TargetScope { 231 &self.scope 232 } 233 234 pub fn policy(&self) -> &RadrootsMeshPayloadPolicy { 235 &self.policy 236 } 237 } 238 239 impl Default for RadrootsMockMeshAgentClient { 240 fn default() -> Self { 241 Self::reticulum_unavailable() 242 } 243 } 244 245 impl RadrootsMeshAgentClient for RadrootsMockMeshAgentClient { 246 fn status(&self, request: MeshAgentStatusRequest) -> MeshAgentStatusResponse { 247 let transports = if request.include_transports { 248 vec![MeshAgentTransportStatus { 249 transport: MeshAgentTransportKind::Reticulum, 250 profile_id: self.profile_id.clone(), 251 endpoint_uri: self.endpoint_uri.clone(), 252 configured: true, 253 implementation: MeshAgentImplementation::Real, 254 maturity: MeshAgentCapabilityMaturity::Preview, 255 availability: MeshAgentCapabilityAvailability::Unavailable, 256 usable_for_delivery: false, 257 message: RADROOTS_MESH_UNAVAILABLE_MESSAGE.to_owned(), 258 }] 259 } else { 260 Vec::new() 261 }; 262 MeshAgentStatusResponse { transports } 263 } 264 265 fn publish(&self, request: MeshAgentPublishRequest) -> MeshAgentPublishResponse { 266 MeshAgentPublishResponse { 267 publish_request_id: request.publish_request_id, 268 status: MeshAgentResponseStatus::Rejected, 269 transport_receipts: vec![MeshAgentTransportReceipt { 270 transport_kind: MeshAgentTransportKind::Reticulum, 271 endpoint_uri: self.endpoint_uri.clone(), 272 outcome: MeshAgentTransportOutcome::TransportUnavailable, 273 message: RADROOTS_MESH_UNAVAILABLE_MESSAGE.to_owned(), 274 }], 275 event_id: request.event_id, 276 } 277 } 278 279 fn schema_sha256_hex(&self) -> String { 280 schema_sha256_hex() 281 } 282 }