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