Skip to main content

tokio_xmpp/
event.rs

1// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7use xmpp_parsers::{
8    iq::Iq,
9    jid::Jid,
10    message::{Id, Message},
11    presence::Presence,
12    stream_features::StreamFeatures,
13};
14use xso::{AsXml, FromXml};
15
16use crate::xmlstream::XmppStreamElement;
17use crate::Error;
18
19pub(crate) fn make_id() -> String {
20    let id: u64 = rand::random();
21    format!("{}", id)
22}
23
24/// A stanza sent/received over the stream.
25#[derive(FromXml, AsXml, Debug, PartialEq)]
26#[xml()]
27pub enum Stanza {
28    /// IQ stanza
29    #[xml(transparent)]
30    Iq(Iq),
31
32    /// Message stanza
33    #[xml(transparent)]
34    Message(Message),
35
36    /// Presence stanza
37    #[xml(transparent)]
38    Presence(Presence),
39}
40
41impl Stanza {
42    /// Assign a random ID to the stanza, if no ID has been assigned yet.
43    pub fn ensure_id(&mut self) -> &str {
44        match self {
45            Self::Iq(iq) => {
46                let id = iq.id_mut();
47                if id.is_empty() {
48                    *id = make_id();
49                }
50                id
51            }
52            Self::Message(message) => message.id.get_or_insert_with(|| Id(make_id())).0.as_ref(),
53            Self::Presence(presence) => presence.id.get_or_insert_with(make_id),
54        }
55    }
56}
57
58impl From<Iq> for Stanza {
59    fn from(other: Iq) -> Self {
60        Self::Iq(other)
61    }
62}
63
64impl From<Presence> for Stanza {
65    fn from(other: Presence) -> Self {
66        Self::Presence(other)
67    }
68}
69
70impl From<Message> for Stanza {
71    fn from(other: Message) -> Self {
72        Self::Message(other)
73    }
74}
75
76impl TryFrom<Stanza> for Message {
77    type Error = Stanza;
78
79    fn try_from(other: Stanza) -> Result<Self, Self::Error> {
80        match other {
81            Stanza::Message(st) => Ok(st),
82            other => Err(other),
83        }
84    }
85}
86
87impl TryFrom<Stanza> for Presence {
88    type Error = Stanza;
89
90    fn try_from(other: Stanza) -> Result<Self, Self::Error> {
91        match other {
92            Stanza::Presence(st) => Ok(st),
93            other => Err(other),
94        }
95    }
96}
97
98impl TryFrom<Stanza> for Iq {
99    type Error = Stanza;
100
101    fn try_from(other: Stanza) -> Result<Self, Stanza> {
102        match other {
103            Stanza::Iq(st) => Ok(st),
104            other => Err(other),
105        }
106    }
107}
108
109impl From<Stanza> for XmppStreamElement {
110    fn from(other: Stanza) -> Self {
111        Self::Stanza(other)
112    }
113}
114
115/// High-level event on the Stream implemented by Client and Component
116#[derive(Debug)]
117pub enum Event {
118    /// Stream is connected and initialized
119    Online {
120        /// Server-set Jabber-Id for your session
121        ///
122        /// This may turn out to be a different JID resource than
123        /// expected, so use this one instead of the JID with which
124        /// the connection was setup.
125        bound_jid: Jid,
126        /// Features supported by the server
127        features: StreamFeatures,
128        /// Was this session resumed?
129        ///
130        /// Not yet implemented for the Client
131        resumed: bool,
132    },
133    /// Stream end
134    Disconnected(Error),
135    /// Received stanza/nonza
136    Stanza(Stanza),
137}
138
139impl Event {
140    /// `Online` event?
141    pub fn is_online(&self) -> bool {
142        matches!(&self, Event::Online { .. })
143    }
144
145    /// Get the server-assigned JID for the `Online` event
146    pub fn get_jid(&self) -> Option<&Jid> {
147        match *self {
148            Event::Online { ref bound_jid, .. } => Some(bound_jid),
149            _ => None,
150        }
151    }
152
153    /// If this is a `Stanza` event, get its data
154    pub fn as_stanza(&self) -> Option<&Stanza> {
155        match *self {
156            Event::Stanza(ref stanza) => Some(stanza),
157            _ => None,
158        }
159    }
160
161    /// If this is a `Stanza` event, unwrap into its data
162    pub fn into_stanza(self) -> Option<Stanza> {
163        match self {
164            Event::Stanza(stanza) => Some(stanza),
165            _ => None,
166        }
167    }
168}