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// WARNING: do not add variants to this enum! Adding variants which refer
26// to anything but IQ, Message or Presence stanzas will cause the
27// stream management counters to be off.
28#[derive(FromXml, AsXml, Debug, PartialEq)]
29#[xml()]
30pub enum Stanza {
31    /// IQ stanza
32    #[xml(transparent)]
33    Iq(Iq),
34
35    /// Message stanza
36    #[xml(transparent)]
37    Message(Message),
38
39    /// Presence stanza
40    #[xml(transparent)]
41    Presence(Presence),
42}
43
44impl Stanza {
45    /// Assign a random ID to the stanza, if no ID has been assigned yet.
46    pub fn ensure_id(&mut self) -> &str {
47        match self {
48            Self::Iq(iq) => {
49                let id = iq.id_mut();
50                if id.is_empty() {
51                    *id = make_id();
52                }
53                id
54            }
55            Self::Message(message) => message.id.get_or_insert_with(|| Id(make_id())).0.as_ref(),
56            Self::Presence(presence) => presence.id.get_or_insert_with(make_id),
57        }
58    }
59}
60
61impl From<Iq> for Stanza {
62    fn from(other: Iq) -> Self {
63        Self::Iq(other)
64    }
65}
66
67impl From<Presence> for Stanza {
68    fn from(other: Presence) -> Self {
69        Self::Presence(other)
70    }
71}
72
73impl From<Message> for Stanza {
74    fn from(other: Message) -> Self {
75        Self::Message(other)
76    }
77}
78
79impl TryFrom<Stanza> for Message {
80    type Error = Stanza;
81
82    fn try_from(other: Stanza) -> Result<Self, Self::Error> {
83        match other {
84            Stanza::Message(st) => Ok(st),
85            other => Err(other),
86        }
87    }
88}
89
90impl TryFrom<Stanza> for Presence {
91    type Error = Stanza;
92
93    fn try_from(other: Stanza) -> Result<Self, Self::Error> {
94        match other {
95            Stanza::Presence(st) => Ok(st),
96            other => Err(other),
97        }
98    }
99}
100
101impl TryFrom<Stanza> for Iq {
102    type Error = Stanza;
103
104    fn try_from(other: Stanza) -> Result<Self, Stanza> {
105        match other {
106            Stanza::Iq(st) => Ok(st),
107            other => Err(other),
108        }
109    }
110}
111
112impl From<Stanza> for XmppStreamElement {
113    fn from(other: Stanza) -> Self {
114        Self::Stanza(other)
115    }
116}
117
118/// High-level event on the Stream implemented by Client and Component
119#[derive(Debug)]
120pub enum Event {
121    /// Stream is connected and initialized
122    Online {
123        /// Server-set Jabber-Id for your session
124        ///
125        /// This may turn out to be a different JID resource than
126        /// expected, so use this one instead of the JID with which
127        /// the connection was setup.
128        bound_jid: Jid,
129        /// Features supported by the server
130        features: StreamFeatures,
131        /// Was this session resumed?
132        ///
133        /// Not yet implemented for the Client
134        resumed: bool,
135    },
136    /// Stream end
137    Disconnected(Error),
138    /// Received stanza/nonza
139    Stanza(Stanza),
140}
141
142impl Event {
143    /// `Online` event?
144    pub fn is_online(&self) -> bool {
145        matches!(&self, Event::Online { .. })
146    }
147
148    /// Get the server-assigned JID for the `Online` event
149    pub fn get_jid(&self) -> Option<&Jid> {
150        match *self {
151            Event::Online { ref bound_jid, .. } => Some(bound_jid),
152            _ => None,
153        }
154    }
155
156    /// If this is a `Stanza` event, get its data
157    pub fn as_stanza(&self) -> Option<&Stanza> {
158        match *self {
159            Event::Stanza(ref stanza) => Some(stanza),
160            _ => None,
161        }
162    }
163
164    /// If this is a `Stanza` event, unwrap into its data
165    pub fn into_stanza(self) -> Option<Stanza> {
166        match self {
167            Event::Stanza(stanza) => Some(stanza),
168            _ => None,
169        }
170    }
171}