notedeck

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

message.rs (8527B)


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