nostr_types/types/
relay_message.rs

1use crate::types::{Event, Id, SubscriptionId};
2use serde::de::Error as DeError;
3use serde::de::{Deserialize, Deserializer, IgnoredAny, SeqAccess, Visitor};
4use serde::ser::{Serialize, SerializeSeq, Serializer};
5use std::fmt;
6
7/// A message from a relay to a client
8#[derive(Clone, Debug, Eq, PartialEq)]
9pub enum RelayMessage {
10    /// Used to send authentication challenges
11    Auth(String),
12
13    /// Used to indicate that a subscription was ended on the server side
14    /// Every ClientMessage::Req _may_ trigger a RelayMessage::Closed response
15    /// The last parameter may have a colon-terminated machine-readable prefix of:
16    ///     duplicate, pow, blocked, rate-limited, invalid, auth-required,
17    ///     restricted, or error
18    Closed(SubscriptionId, String),
19
20    /// End of subscribed events notification
21    Eose(SubscriptionId),
22
23    /// An event matching a subscription
24    Event(SubscriptionId, Box<Event>),
25
26    /// A human readable notice for errors and other information
27    Notice(String),
28
29    /// A human readable notice for the end user
30    Notify(String),
31
32    /// Used to notify clients if an event was successuful
33    /// Every ClientMessage::Event will trigger a RelayMessage::OK response
34    /// The last parameter may have a colon-terminated machine-readable prefix of:
35    ///     duplicate, pow, blocked, rate-limited, invalid, auth-required,
36    ///     restricted or error
37    Ok(Id, bool, String),
38
39    /// The results of a COUNT command
40    Count(SubscriptionId, CountResult),
41}
42
43/// The count results
44#[derive(Debug, Clone, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
45pub struct CountResult {
46    /// The count
47    pub count: usize,
48
49    /// Whether the count is approximate
50    #[serde(default)]
51    pub approximate: bool,
52
53    /// Optional HyperLogLog data
54    #[serde(default)]
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub hll: Option<String>,
57}
58
59/// The reason why a relay issued an OK or CLOSED message
60#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
61pub enum Why {
62    /// Authentication is required
63    AuthRequired,
64
65    /// You have been blocked from this relay
66    Blocked,
67
68    /// Your request is a duplicate
69    Duplicate,
70
71    /// Other error
72    Error,
73
74    /// Your request is invalid
75    Invalid,
76
77    /// Proof-of-work is required
78    Pow,
79
80    /// Rejected due to rate limiting
81    RateLimited,
82
83    /// The action you requested is restricted to your identity
84    Restricted,
85}
86
87impl RelayMessage {
88    /// Translate the machine-readable prefix from the message
89    pub fn why(&self) -> Option<Why> {
90        let s = match *self {
91            RelayMessage::Closed(_, ref s) => s,
92            RelayMessage::Ok(_, _, ref s) => s,
93            _ => return None,
94        };
95
96        match s.split(':').next() {
97            Some("auth-required") => Some(Why::AuthRequired),
98            Some("blocked") => Some(Why::Blocked),
99            Some("duplicate") => Some(Why::Duplicate),
100            Some("error") => Some(Why::Error),
101            Some("invalid") => Some(Why::Invalid),
102            Some("pow") => Some(Why::Pow),
103            Some("rate-limited") => Some(Why::RateLimited),
104            Some("restricted") => Some(Why::Restricted),
105            _ => None,
106        }
107    }
108
109    // Mock data for testing
110    #[allow(dead_code)]
111    pub(crate) async fn mock() -> RelayMessage {
112        RelayMessage::Event(SubscriptionId::mock(), Box::new(Event::mock().await))
113    }
114}
115
116impl Serialize for RelayMessage {
117    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
118    where
119        S: Serializer,
120    {
121        match self {
122            RelayMessage::Auth(challenge) => {
123                let mut seq = serializer.serialize_seq(Some(2))?;
124                seq.serialize_element("AUTH")?;
125                seq.serialize_element(&challenge)?;
126                seq.end()
127            }
128            RelayMessage::Closed(id, message) => {
129                let mut seq = serializer.serialize_seq(Some(3))?;
130                seq.serialize_element("CLOSED")?;
131                seq.serialize_element(&id)?;
132                seq.serialize_element(&message)?;
133                seq.end()
134            }
135            RelayMessage::Eose(id) => {
136                let mut seq = serializer.serialize_seq(Some(2))?;
137                seq.serialize_element("EOSE")?;
138                seq.serialize_element(&id)?;
139                seq.end()
140            }
141            RelayMessage::Event(id, event) => {
142                let mut seq = serializer.serialize_seq(Some(3))?;
143                seq.serialize_element("EVENT")?;
144                seq.serialize_element(&id)?;
145                seq.serialize_element(&event)?;
146                seq.end()
147            }
148            RelayMessage::Notice(s) => {
149                let mut seq = serializer.serialize_seq(Some(2))?;
150                seq.serialize_element("NOTICE")?;
151                seq.serialize_element(&s)?;
152                seq.end()
153            }
154            RelayMessage::Notify(s) => {
155                let mut seq = serializer.serialize_seq(Some(2))?;
156                seq.serialize_element("NOTIFY")?;
157                seq.serialize_element(&s)?;
158                seq.end()
159            }
160            RelayMessage::Ok(id, ok, message) => {
161                let mut seq = serializer.serialize_seq(Some(4))?;
162                seq.serialize_element("OK")?;
163                seq.serialize_element(&id)?;
164                seq.serialize_element(&ok)?;
165                seq.serialize_element(&message)?;
166                seq.end()
167            }
168            RelayMessage::Count(sub, result) => {
169                let mut seq = serializer.serialize_seq(Some(3))?;
170                seq.serialize_element("COUNT")?;
171                seq.serialize_element(&sub)?;
172                seq.serialize_element(&result)?;
173                seq.end()
174            }
175        }
176    }
177}
178
179impl<'de> Deserialize<'de> for RelayMessage {
180    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
181    where
182        D: Deserializer<'de>,
183    {
184        deserializer.deserialize_seq(RelayMessageVisitor)
185    }
186}
187
188struct RelayMessageVisitor;
189
190impl<'de> Visitor<'de> for RelayMessageVisitor {
191    type Value = RelayMessage;
192
193    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
194        write!(f, "a sequence of strings")
195    }
196
197    fn visit_seq<A>(self, mut seq: A) -> Result<RelayMessage, A::Error>
198    where
199        A: SeqAccess<'de>,
200    {
201        let word: &str = seq
202            .next_element()?
203            .ok_or_else(|| DeError::custom("Message missing initial string field"))?;
204        let mut output: Option<RelayMessage> = None;
205        if word == "EVENT" {
206            let id: SubscriptionId = seq
207                .next_element()?
208                .ok_or_else(|| DeError::custom("Message missing id field"))?;
209            let event: Event = seq
210                .next_element()?
211                .ok_or_else(|| DeError::custom("Message missing event field"))?;
212            output = Some(RelayMessage::Event(id, Box::new(event)));
213        } else if word == "NOTICE" {
214            let s: String = seq
215                .next_element()?
216                .ok_or_else(|| DeError::custom("Message missing string field"))?;
217            output = Some(RelayMessage::Notice(s));
218        } else if word == "NOTIFY" {
219            let s: String = seq
220                .next_element()?
221                .ok_or_else(|| DeError::custom("Message missing string field"))?;
222            output = Some(RelayMessage::Notify(s));
223        } else if word == "EOSE" {
224            let id: SubscriptionId = seq
225                .next_element()?
226                .ok_or_else(|| DeError::custom("Message missing id field"))?;
227            output = Some(RelayMessage::Eose(id))
228        } else if word == "OK" {
229            let id: Id = seq
230                .next_element()?
231                .ok_or_else(|| DeError::custom("Message missing id field"))?;
232            let ok: bool = seq
233                .next_element()?
234                .ok_or_else(|| DeError::custom("Message missing ok field"))?;
235            let message: String = seq
236                .next_element()?
237                .ok_or_else(|| DeError::custom("Message missing string field"))?;
238            output = Some(RelayMessage::Ok(id, ok, message));
239        } else if word == "AUTH" {
240            let challenge: String = seq
241                .next_element()?
242                .ok_or_else(|| DeError::custom("Message missing challenge field"))?;
243            output = Some(RelayMessage::Auth(challenge));
244        } else if word == "CLOSED" {
245            let id: SubscriptionId = seq
246                .next_element()?
247                .ok_or_else(|| DeError::custom("Message messing id field"))?;
248            let message: String = seq
249                .next_element()?
250                .ok_or_else(|| DeError::custom("Message missing string field"))?;
251            output = Some(RelayMessage::Closed(id, message));
252        } else if word == "COUNT" {
253            let id: SubscriptionId = seq
254                .next_element()?
255                .ok_or_else(|| DeError::custom("Message messing id field"))?;
256            let count_result: CountResult = seq
257                .next_element()?
258                .ok_or_else(|| DeError::custom("Message messing count result object field"))?;
259            output = Some(RelayMessage::Count(id, count_result));
260        }
261
262        // Consume any trailing fields
263        while let Some(_ignored) = seq.next_element::<IgnoredAny>()? {}
264
265        match output {
266            Some(rm) => Ok(rm),
267            None => Err(DeError::custom(format!("Unknown Message: {word}"))),
268        }
269    }
270}
271
272#[cfg(test)]
273mod test {
274    use super::*;
275
276    test_serde_async! {RelayMessage, test_relay_message_serde}
277}