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 }