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};
13use xso::{AsXml, FromXml};
14
15use crate::xmlstream::XmppStreamElement;
16use crate::Error;
17
18pub(crate) fn make_id() -> String {
19    let id: u64 = rand::random();
20    format!("{}", id)
21}
22
23/// A stanza sent/received over the stream.
24// WARNING: do not add variants to this enum! Adding variants which refer
25// to anything but IQ, Message or Presence stanzas will cause the
26// stream management counters to be off.
27#[derive(FromXml, AsXml, Debug, PartialEq)]
28#[xml()]
29pub enum Stanza {
30    /// IQ stanza
31    #[xml(transparent)]
32    Iq(Iq),
33
34    /// Message stanza
35    #[xml(transparent)]
36    Message(Message),
37
38    /// Presence stanza
39    #[xml(transparent)]
40    Presence(Presence),
41}
42
43impl Stanza {
44    /// Assign a random ID to the stanza, if no ID has been assigned yet.
45    pub fn ensure_id(&mut self) -> &str {
46        match self {
47            Self::Iq(iq) => {
48                let id = iq.id_mut();
49                if id.is_empty() {
50                    *id = make_id();
51                }
52                id
53            }
54            Self::Message(message) => message.id.get_or_insert_with(|| Id(make_id())).0.as_ref(),
55            Self::Presence(presence) => presence.id.get_or_insert_with(make_id),
56        }
57    }
58}
59
60impl From<Iq> for Stanza {
61    fn from(other: Iq) -> Self {
62        Self::Iq(other)
63    }
64}
65
66impl From<Presence> for Stanza {
67    fn from(other: Presence) -> Self {
68        Self::Presence(other)
69    }
70}
71
72impl From<Message> for Stanza {
73    fn from(other: Message) -> Self {
74        Self::Message(other)
75    }
76}
77
78impl TryFrom<Stanza> for Message {
79    type Error = Stanza;
80
81    fn try_from(other: Stanza) -> Result<Self, Self::Error> {
82        match other {
83            Stanza::Message(st) => Ok(st),
84            other => Err(other),
85        }
86    }
87}
88
89impl TryFrom<Stanza> for Presence {
90    type Error = Stanza;
91
92    fn try_from(other: Stanza) -> Result<Self, Self::Error> {
93        match other {
94            Stanza::Presence(st) => Ok(st),
95            other => Err(other),
96        }
97    }
98}
99
100impl TryFrom<Stanza> for Iq {
101    type Error = Stanza;
102
103    fn try_from(other: Stanza) -> Result<Self, Stanza> {
104        match other {
105            Stanza::Iq(st) => Ok(st),
106            other => Err(other),
107        }
108    }
109}
110
111impl From<Stanza> for XmppStreamElement {
112    fn from(other: Stanza) -> Self {
113        Self::Stanza(other)
114    }
115}
116
117/// High-level event on the Stream implemented by Client and Component
118#[derive(Debug)]
119pub enum Event {
120    /// Stream is connected and initialized
121    Online {
122        /// Server-set Jabber-Id for your session
123        ///
124        /// This may turn out to be a different JID resource than
125        /// expected, so use this one instead of the JID with which
126        /// the connection was setup.
127        bound_jid: Jid,
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}