domus

One damus client to rule them all
git clone git://jb55.com/domus
Log | Files | Refs | README

message.rs (9017B)


      1 use crate::Error;
      2 use crate::Event;
      3 use crate::Result;
      4 use serde_json::Value;
      5 
      6 use ewebsock::{WsEvent, WsMessage};
      7 
      8 #[derive(Debug, Eq, PartialEq)]
      9 pub struct CommandResult {
     10     event_id: String,
     11     status: bool,
     12     message: String,
     13 }
     14 
     15 #[derive(Debug, Eq, PartialEq)]
     16 pub enum RelayMessage {
     17     OK(CommandResult),
     18     Eose(String),
     19     Event(String, Event),
     20     Notice(String),
     21 }
     22 
     23 #[derive(Debug)]
     24 pub enum RelayEvent {
     25     Opened,
     26     Closed,
     27     Other(WsMessage),
     28     Message(RelayMessage),
     29 }
     30 
     31 impl TryFrom<WsEvent> for RelayEvent {
     32     type Error = Error;
     33 
     34     fn try_from(message: WsEvent) -> Result<Self> {
     35         match message {
     36             WsEvent::Opened => Ok(RelayEvent::Opened),
     37             WsEvent::Closed => Ok(RelayEvent::Closed),
     38             WsEvent::Message(ws_msg) => ws_msg.try_into(),
     39             WsEvent::Error(s) => Err(s.into()),
     40         }
     41     }
     42 }
     43 
     44 impl TryFrom<WsMessage> for RelayEvent {
     45     type Error = Error;
     46 
     47     fn try_from(wsmsg: WsMessage) -> Result<Self> {
     48         match wsmsg {
     49             WsMessage::Text(s) => RelayMessage::from_json(&s).map(RelayEvent::Message),
     50             wsmsg => Ok(RelayEvent::Other(wsmsg)),
     51         }
     52     }
     53 }
     54 
     55 impl RelayMessage {
     56     pub fn eose(subid: String) -> Self {
     57         RelayMessage::Eose(subid)
     58     }
     59 
     60     pub fn notice(msg: String) -> Self {
     61         RelayMessage::Notice(msg)
     62     }
     63 
     64     pub fn ok(event_id: String, status: bool, message: String) -> Self {
     65         RelayMessage::OK(CommandResult {
     66             event_id: event_id,
     67             status,
     68             message: message,
     69         })
     70     }
     71 
     72     pub fn event(ev: Event, sub_id: String) -> Self {
     73         RelayMessage::Event(sub_id, ev)
     74     }
     75 
     76     // I was lazy and took this from the nostr crate. thx yuki!
     77     pub fn from_json(msg: &str) -> Result<Self> {
     78         if msg.is_empty() {
     79             return Err(Error::MessageEmpty);
     80         }
     81 
     82         let v: Vec<Value> = serde_json::from_str(msg).map_err(|_| Error::MessageDecodeFailed)?;
     83 
     84         // Notice
     85         // Relay response format: ["NOTICE", <message>]
     86         if v[0] == "NOTICE" {
     87             if v.len() != 2 {
     88                 return Err(Error::MessageDecodeFailed);
     89             }
     90             let v_notice: String =
     91                 serde_json::from_value(v[1].clone()).map_err(|_| Error::MessageDecodeFailed)?;
     92             return Ok(Self::notice(v_notice));
     93         }
     94 
     95         // Event
     96         // Relay response format: ["EVENT", <subscription id>, <event JSON>]
     97         if v[0] == "EVENT" {
     98             if v.len() != 3 {
     99                 return Err(Error::MessageDecodeFailed);
    100             }
    101 
    102             let event =
    103                 Event::from_json(&v[2].to_string()).map_err(|_| Error::MessageDecodeFailed)?;
    104 
    105             let subscription_id: String =
    106                 serde_json::from_value(v[1].clone()).map_err(|_| Error::MessageDecodeFailed)?;
    107 
    108             return Ok(Self::event(event, subscription_id));
    109         }
    110 
    111         // EOSE (NIP-15)
    112         // Relay response format: ["EOSE", <subscription_id>]
    113         if v[0] == "EOSE" {
    114             if v.len() != 2 {
    115                 return Err(Error::MessageDecodeFailed);
    116             }
    117 
    118             let subscription_id: String =
    119                 serde_json::from_value(v[1].clone()).map_err(|_| Error::MessageDecodeFailed)?;
    120 
    121             return Ok(Self::eose(subscription_id));
    122         }
    123 
    124         // OK (NIP-20)
    125         // Relay response format: ["OK", <event_id>, <true|false>, <message>]
    126         if v[0] == "OK" {
    127             if v.len() != 4 {
    128                 return Err(Error::MessageDecodeFailed);
    129             }
    130 
    131             let event_id: String =
    132                 serde_json::from_value(v[1].clone()).map_err(|_| Error::MessageDecodeFailed)?;
    133 
    134             let status: bool =
    135                 serde_json::from_value(v[2].clone()).map_err(|_| Error::MessageDecodeFailed)?;
    136 
    137             let message: String =
    138                 serde_json::from_value(v[3].clone()).map_err(|_| Error::MessageDecodeFailed)?;
    139 
    140             return Ok(Self::ok(event_id, status, message));
    141         }
    142 
    143         Err(Error::MessageDecodeFailed)
    144     }
    145 }
    146 
    147 #[cfg(test)]
    148 mod tests {
    149     use super::*;
    150 
    151     #[test]
    152     fn test_handle_valid_notice() -> Result<()> {
    153         let valid_notice_msg = r#"["NOTICE","Invalid event format!"]"#;
    154         let handled_valid_notice_msg = RelayMessage::notice("Invalid event format!".to_string());
    155 
    156         assert_eq!(
    157             RelayMessage::from_json(valid_notice_msg)?,
    158             handled_valid_notice_msg
    159         );
    160 
    161         Ok(())
    162     }
    163     #[test]
    164     fn test_handle_invalid_notice() {
    165         //Missing content
    166         let invalid_notice_msg = r#"["NOTICE"]"#;
    167         //The content is not string
    168         let invalid_notice_msg_content = r#"["NOTICE": 404]"#;
    169 
    170         assert_eq!(
    171             RelayMessage::from_json(invalid_notice_msg).unwrap_err(),
    172             Error::MessageDecodeFailed
    173         );
    174         assert_eq!(
    175             RelayMessage::from_json(invalid_notice_msg_content).unwrap_err(),
    176             Error::MessageDecodeFailed
    177         );
    178     }
    179 
    180     #[test]
    181     fn test_handle_valid_event() -> Result<()> {
    182         let valid_event_msg = r#"["EVENT", "random_string", {"id":"70b10f70c1318967eddf12527799411b1a9780ad9c43858f5e5fcd45486a13a5","pubkey":"379e863e8357163b5bce5d2688dc4f1dcc2d505222fb8d74db600f30535dfdfe","created_at":1612809991,"kind":1,"tags":[],"content":"test","sig":"273a9cd5d11455590f4359500bccb7a89428262b96b3ea87a756b770964472f8c3e87f5d5e64d8d2e859a71462a3f477b554565c4f2f326cb01dd7620db71502"}]"#;
    183 
    184         let id = "70b10f70c1318967eddf12527799411b1a9780ad9c43858f5e5fcd45486a13a5";
    185         let pubkey = "379e863e8357163b5bce5d2688dc4f1dcc2d505222fb8d74db600f30535dfdfe";
    186         let created_at = 1612809991;
    187         let kind = 1;
    188         let tags = vec![];
    189         let content = "test";
    190         let sig = "273a9cd5d11455590f4359500bccb7a89428262b96b3ea87a756b770964472f8c3e87f5d5e64d8d2e859a71462a3f477b554565c4f2f326cb01dd7620db71502";
    191 
    192         let handled_event = Event::new_dummy(id, pubkey, created_at, kind, tags, content, sig);
    193 
    194         assert_eq!(
    195             RelayMessage::from_json(valid_event_msg)?,
    196             RelayMessage::event(handled_event?, "random_string".to_string())
    197         );
    198 
    199         Ok(())
    200     }
    201 
    202     #[test]
    203     fn test_handle_invalid_event() {
    204         //Mising Event field
    205         let invalid_event_msg = r#"["EVENT", "random_string"]"#;
    206         //Event JSON with incomplete content
    207         let invalid_event_msg_content = r#"["EVENT", "random_string", {"id":"70b10f70c1318967eddf12527799411b1a9780ad9c43858f5e5fcd45486a13a5","pubkey":"379e863e8357163b5bce5d2688dc4f1dcc2d505222fb8d74db600f30535dfdfe"}]"#;
    208 
    209         assert_eq!(
    210             RelayMessage::from_json(invalid_event_msg).unwrap_err(),
    211             Error::MessageDecodeFailed
    212         );
    213 
    214         assert_eq!(
    215             RelayMessage::from_json(invalid_event_msg_content).unwrap_err(),
    216             Error::MessageDecodeFailed
    217         );
    218     }
    219 
    220     #[test]
    221     fn test_handle_valid_eose() -> Result<()> {
    222         let valid_eose_msg = r#"["EOSE","random-subscription-id"]"#;
    223         let handled_valid_eose_msg = RelayMessage::eose("random-subscription-id".to_string());
    224 
    225         assert_eq!(
    226             RelayMessage::from_json(valid_eose_msg)?,
    227             handled_valid_eose_msg
    228         );
    229 
    230         Ok(())
    231     }
    232     #[test]
    233     fn test_handle_invalid_eose() {
    234         // Missing subscription ID
    235         assert_eq!(
    236             RelayMessage::from_json(r#"["EOSE"]"#).unwrap_err(),
    237             Error::MessageDecodeFailed
    238         );
    239 
    240         // The subscription ID is not string
    241         assert_eq!(
    242             RelayMessage::from_json(r#"["EOSE", 404]"#).unwrap_err(),
    243             Error::MessageDecodeFailed
    244         );
    245     }
    246 
    247     #[test]
    248     fn test_handle_valid_ok() -> Result<()> {
    249         let valid_ok_msg = r#"["OK", "b1a649ebe8b435ec71d3784793f3bbf4b93e64e17568a741aecd4c7ddeafce30", true, "pow: difficulty 25>=24"]"#;
    250         let handled_valid_ok_msg = RelayMessage::ok(
    251             "b1a649ebe8b435ec71d3784793f3bbf4b93e64e17568a741aecd4c7ddeafce30".to_string(),
    252             true,
    253             "pow: difficulty 25>=24".into(),
    254         );
    255 
    256         assert_eq!(RelayMessage::from_json(valid_ok_msg)?, handled_valid_ok_msg);
    257 
    258         Ok(())
    259     }
    260     #[test]
    261     fn test_handle_invalid_ok() {
    262         // Missing params
    263         assert_eq!(
    264             RelayMessage::from_json(
    265                 r#"["OK", "b1a649ebe8b435ec71d3784793f3bbf4b93e64e17568a741aecd4c7ddeafce30"]"#
    266             )
    267             .unwrap_err(),
    268             Error::MessageDecodeFailed
    269         );
    270 
    271         // Invalid status
    272         assert_eq!(
    273             RelayMessage::from_json(r#"["OK", "b1a649ebe8b435ec71d3784793f3bbf4b93e64e17568a741aecd4c7ddeafce30", hello, ""]"#).unwrap_err(),
    274             Error::MessageDecodeFailed
    275         );
    276 
    277         // Invalid message
    278         assert_eq!(
    279             RelayMessage::from_json(r#"["OK", "b1a649ebe8b435ec71d3784793f3bbf4b93e64e17568a741aecd4c7ddeafce30", hello, 404]"#).unwrap_err(),
    280             Error::MessageDecodeFailed
    281         );
    282     }
    283 }