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 rand::{thread_rng, Rng};
8use xmpp_parsers::{
9    iq::Iq,
10    jid::Jid,
11    message::{Id, Message},
12    presence::Presence,
13};
14use xso::{AsXml, FromXml};
15
16use crate::xmlstream::XmppStreamElement;
17use crate::Error;
18
19pub(crate) fn make_id() -> String {
20    let id: u64 = thread_rng().gen();
21    format!("{}", id)
22}
23
24/// A stanza sent/received over the stream.
25#[derive(FromXml, AsXml, Debug)]
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        /// Was this session resumed?
127        ///
128        /// Not yet implemented for the Client
129        resumed: bool,
130    },
131    /// Stream end
132    Disconnected(Error),
133    /// Received stanza/nonza
134    Stanza(Stanza),
135}
136
137impl Event {
138    /// `Online` event?
139    pub fn is_online(&self) -> bool {
140        matches!(&self, Event::Online { .. })
141    }
142
143    /// Get the server-assigned JID for the `Online` event
144    pub fn get_jid(&self) -> Option<&Jid> {
145        match *self {
146            Event::Online { ref bound_jid, .. } => Some(bound_jid),
147            _ => None,
148        }
149    }
150
151    /// If this is a `Stanza` event, get its data
152    pub fn as_stanza(&self) -> Option<&Stanza> {
153        match *self {
154            Event::Stanza(ref stanza) => Some(stanza),
155            _ => None,
156        }
157    }
158
159    /// If this is a `Stanza` event, unwrap into its data
160    pub fn into_stanza(self) -> Option<Stanza> {
161        match self {
162            Event::Stanza(stanza) => Some(stanza),
163            _ => None,
164        }
165    }
166}