lib

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

id.rs (4958B)


      1 use crate::Error as TransportError;
      2 use alloc::string::{String, ToString};
      3 use core::{fmt, str::FromStr};
      4 use radroots_protocol::capability::v1::{
      5     Error as ProtocolError, MAX_TRANSPORT_KIND_BYTES, TransportKind as ProtocolTransportKind,
      6 };
      7 
      8 /// Maximum encoded length of a transport identity.
      9 pub const TRANSPORT_ID_MAX_BYTES: usize = MAX_TRANSPORT_KIND_BYTES;
     10 
     11 /// Validated, extensible transport identity.
     12 ///
     13 /// Identities contain 1-64 canonical lowercase ASCII bytes. They begin and
     14 /// end with a letter or digit and may use single `-` separators internally.
     15 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
     16 pub struct TransportId(ProtocolTransportKind);
     17 
     18 impl TransportId {
     19     /// Process-local transport.
     20     pub const LOCAL: Self = Self(ProtocolTransportKind::LOCAL);
     21     /// Nostr relay transport.
     22     pub const NOSTR: Self = Self(ProtocolTransportKind::NOSTR);
     23     /// Reticulum mesh transport.
     24     pub const RETICULUM: Self = Self(ProtocolTransportKind::RETICULUM);
     25     /// Daemon-mediated transport.
     26     pub const RADROOTSD: Self = Self(ProtocolTransportKind::RADROOTSD);
     27 
     28     /// Parses an exact canonical identity.
     29     pub fn parse(value: impl AsRef<str>) -> Result<Self, TransportError> {
     30         ProtocolTransportKind::parse(value.as_ref())
     31             .map(Self)
     32             .map_err(map_protocol_error)
     33     }
     34 
     35     /// Parses an exact canonical identity.
     36     pub fn parse_canonical(value: impl AsRef<str>) -> Result<Self, TransportError> {
     37         Self::parse(value)
     38     }
     39 
     40     /// Returns the canonical identity text.
     41     pub fn as_str(&self) -> &str {
     42         self.0.as_str()
     43     }
     44 
     45     /// Returns an owned canonical identity label.
     46     pub fn canonical_label(&self) -> String {
     47         self.as_str().to_string()
     48     }
     49 }
     50 
     51 fn map_protocol_error(error: ProtocolError) -> TransportError {
     52     match error {
     53         ProtocolError::EmptyTransportKind => TransportError::EmptyTransportKind,
     54         ProtocolError::InvalidTransportKind { .. } => TransportError::InvalidTransportKind,
     55         _ => TransportError::InvalidTransportKind,
     56     }
     57 }
     58 
     59 impl AsRef<str> for TransportId {
     60     fn as_ref(&self) -> &str {
     61         self.as_str()
     62     }
     63 }
     64 
     65 impl fmt::Display for TransportId {
     66     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
     67         formatter.write_str(self.as_str())
     68     }
     69 }
     70 
     71 impl FromStr for TransportId {
     72     type Err = TransportError;
     73 
     74     fn from_str(value: &str) -> Result<Self, Self::Err> {
     75         Self::parse(value)
     76     }
     77 }
     78 
     79 impl TryFrom<&str> for TransportId {
     80     type Error = TransportError;
     81 
     82     fn try_from(value: &str) -> Result<Self, Self::Error> {
     83         Self::parse(value)
     84     }
     85 }
     86 
     87 impl TryFrom<String> for TransportId {
     88     type Error = TransportError;
     89 
     90     fn try_from(value: String) -> Result<Self, Self::Error> {
     91         Self::parse(value)
     92     }
     93 }
     94 
     95 impl From<ProtocolTransportKind> for TransportId {
     96     fn from(value: ProtocolTransportKind) -> Self {
     97         Self(value)
     98     }
     99 }
    100 
    101 impl From<TransportId> for ProtocolTransportKind {
    102     fn from(value: TransportId) -> Self {
    103         value.0
    104     }
    105 }
    106 
    107 #[cfg(feature = "serde")]
    108 impl serde::Serialize for TransportId {
    109     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    110     where
    111         S: serde::Serializer,
    112     {
    113         self.0.serialize(serializer)
    114     }
    115 }
    116 
    117 #[cfg(feature = "serde")]
    118 impl<'de> serde::Deserialize<'de> for TransportId {
    119     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    120     where
    121         D: serde::Deserializer<'de>,
    122     {
    123         <ProtocolTransportKind as serde::Deserialize>::deserialize(deserializer).map(Self)
    124     }
    125 }
    126 
    127 #[cfg(test)]
    128 mod tests {
    129     use super::*;
    130 
    131     #[test]
    132     fn transport_ids_cover_conversion_and_validation_surfaces() {
    133         let id = TransportId::parse_canonical("custom-transport").unwrap();
    134         assert_eq!(id.as_str(), "custom-transport");
    135         assert_eq!(id.as_ref(), "custom-transport");
    136         assert_eq!(id.to_string(), "custom-transport");
    137         assert_eq!(id.canonical_label(), "custom-transport");
    138         assert_eq!(TransportId::from_str("custom-transport").unwrap(), id);
    139         assert_eq!(TransportId::try_from("custom-transport").unwrap(), id);
    140         assert_eq!(
    141             TransportId::try_from(String::from("custom-transport")).unwrap(),
    142             id
    143         );
    144         let protocol_id: ProtocolTransportKind = id.into();
    145         assert_eq!(TransportId::from(protocol_id), id);
    146         assert_eq!(
    147             TransportId::parse(""),
    148             Err(TransportError::EmptyTransportKind)
    149         );
    150         assert_eq!(
    151             TransportId::parse("Invalid"),
    152             Err(TransportError::InvalidTransportKind)
    153         );
    154 
    155         #[cfg(feature = "serde")]
    156         {
    157             let encoded = serde_json::to_string(&id).unwrap();
    158             assert_eq!(serde_json::from_str::<TransportId>(&encoded).unwrap(), id);
    159             assert!(serde_json::from_str::<TransportId>("\"Invalid\"").is_err());
    160         }
    161     }
    162 }