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 }