lib

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

method.rs (3699B)


      1 //! Standard and extension NIP-46 method identifiers.
      2 
      3 use crate::error::RadrootsNostrConnectError;
      4 use serde::{Deserialize, Deserializer, Serialize, Serializer};
      5 use std::fmt;
      6 use std::str::FromStr;
      7 
      8 /// Maximum UTF-8 byte length of a NIP-46 method identifier.
      9 pub const METHOD_MAX_BYTES: usize = 64;
     10 
     11 /// A validated extension method identifier.
     12 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
     13 pub struct CustomMethod(String);
     14 
     15 impl CustomMethod {
     16     fn new(value: String) -> Result<Self, RadrootsNostrConnectError> {
     17         validate_custom_method(&value)?;
     18         Ok(Self(value))
     19     }
     20 
     21     #[must_use]
     22     pub fn as_str(&self) -> &str {
     23         &self.0
     24     }
     25 }
     26 
     27 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
     28 pub enum Method {
     29     Connect,
     30     GetPublicKey,
     31     GetSessionCapability,
     32     SignEvent,
     33     Nip04Encrypt,
     34     Nip04Decrypt,
     35     Nip44Encrypt,
     36     Nip44Decrypt,
     37     Ping,
     38     SwitchRelays,
     39     Logout,
     40     Custom(CustomMethod),
     41 }
     42 
     43 impl Method {
     44     /// Creates a bounded custom method identifier.
     45     pub fn custom(value: impl Into<String>) -> Result<Self, RadrootsNostrConnectError> {
     46         CustomMethod::new(value.into()).map(Self::Custom)
     47     }
     48 
     49     /// Returns the canonical wire identifier.
     50     #[must_use]
     51     pub fn as_str(&self) -> &str {
     52         match self {
     53             Self::Connect => "connect",
     54             Self::GetPublicKey => "get_public_key",
     55             Self::GetSessionCapability => "get_session_capability",
     56             Self::SignEvent => "sign_event",
     57             Self::Nip04Encrypt => "nip04_encrypt",
     58             Self::Nip04Decrypt => "nip04_decrypt",
     59             Self::Nip44Encrypt => "nip44_encrypt",
     60             Self::Nip44Decrypt => "nip44_decrypt",
     61             Self::Ping => "ping",
     62             Self::SwitchRelays => "switch_relays",
     63             Self::Logout => "logout",
     64             Self::Custom(value) => value.as_str(),
     65         }
     66     }
     67 }
     68 
     69 impl fmt::Display for Method {
     70     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     71         f.write_str(self.as_str())
     72     }
     73 }
     74 
     75 impl FromStr for Method {
     76     type Err = RadrootsNostrConnectError;
     77 
     78     fn from_str(value: &str) -> Result<Self, Self::Err> {
     79         match value {
     80             "connect" => Ok(Self::Connect),
     81             "get_public_key" => Ok(Self::GetPublicKey),
     82             "get_session_capability" => Ok(Self::GetSessionCapability),
     83             "sign_event" => Ok(Self::SignEvent),
     84             "nip04_encrypt" => Ok(Self::Nip04Encrypt),
     85             "nip04_decrypt" => Ok(Self::Nip04Decrypt),
     86             "nip44_encrypt" => Ok(Self::Nip44Encrypt),
     87             "nip44_decrypt" => Ok(Self::Nip44Decrypt),
     88             "ping" => Ok(Self::Ping),
     89             "switch_relays" => Ok(Self::SwitchRelays),
     90             "logout" => Ok(Self::Logout),
     91             other => Self::custom(other),
     92         }
     93     }
     94 }
     95 
     96 impl Serialize for Method {
     97     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     98     where
     99         S: Serializer,
    100     {
    101         serializer.serialize_str(self.as_str())
    102     }
    103 }
    104 
    105 impl<'de> Deserialize<'de> for Method {
    106     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    107     where
    108         D: Deserializer<'de>,
    109     {
    110         let value = String::deserialize(deserializer)?;
    111         Self::from_str(&value).map_err(serde::de::Error::custom)
    112     }
    113 }
    114 
    115 fn validate_custom_method(value: &str) -> Result<(), RadrootsNostrConnectError> {
    116     if value.is_empty()
    117         || value.len() > METHOD_MAX_BYTES
    118         || !value
    119             .bytes()
    120             .all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || byte == b'_')
    121     {
    122         return Err(RadrootsNostrConnectError::InvalidMethod(value.to_owned()));
    123     }
    124     Ok(())
    125 }