jid: rewrite public types

This moves InnerJid into Jid and reformulates BareJid and FullJid in
terms of Jid.

Doing this has the key advantage that FullJid and BareJid can deref to
and borrow as Jid. This, in turn, has the advantage that they can be
used much more flexibly in HashMaps. However, this is (as we say in
Germany) future music; this commit only does the internal reworking.

Oh and also, it saves 20% memory on Jid objects.

Fixes #122 more thoroughly, or rather the original intent behind it.
This commit is contained in:
Jonas Schäfer 2024-04-15 17:03:57 +02:00
parent fb63ac8e50
commit 2e9c9411a3
21 changed files with 464 additions and 448 deletions

View file

@ -1,180 +0,0 @@
// Copyright (c) 2023 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#![deny(missing_docs)]
//! Provides a type for Jabber IDs.
//!
//! For usage, check the documentation on the `Jid` struct.
use crate::Error;
use core::num::NonZeroU16;
use memchr::memchr;
use std::borrow::Cow;
use std::str::FromStr;
use stringprep::{nameprep, nodeprep, resourceprep};
use crate::parts::{DomainRef, NodeRef, ResourceRef};
fn length_check(len: usize, error_empty: Error, error_too_long: Error) -> Result<(), Error> {
if len == 0 {
Err(error_empty)
} else if len > 1023 {
Err(error_too_long)
} else {
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub(crate) struct InnerJid {
pub(crate) normalized: String,
pub(crate) at: Option<NonZeroU16>,
pub(crate) slash: Option<NonZeroU16>,
}
impl InnerJid {
pub(crate) fn new(unnormalized: &str) -> Result<InnerJid, Error> {
let bytes = unnormalized.as_bytes();
let mut orig_at = memchr(b'@', bytes);
let mut orig_slash = memchr(b'/', bytes);
if orig_at.is_some() && orig_slash.is_some() && orig_at > orig_slash {
// This is part of the resource, not a node@domain separator.
orig_at = None;
}
let normalized = match (orig_at, orig_slash) {
(Some(at), Some(slash)) => {
let node = nodeprep(&unnormalized[..at]).map_err(|_| Error::NodePrep)?;
length_check(node.len(), Error::NodeEmpty, Error::NodeTooLong)?;
let domain = nameprep(&unnormalized[at + 1..slash]).map_err(|_| Error::NamePrep)?;
length_check(domain.len(), Error::DomainEmpty, Error::DomainTooLong)?;
let resource =
resourceprep(&unnormalized[slash + 1..]).map_err(|_| Error::ResourcePrep)?;
length_check(resource.len(), Error::ResourceEmpty, Error::ResourceTooLong)?;
orig_at = Some(node.len());
orig_slash = Some(node.len() + domain.len() + 1);
match (node, domain, resource) {
(Cow::Borrowed(_), Cow::Borrowed(_), Cow::Borrowed(_)) => {
unnormalized.to_string()
}
(node, domain, resource) => format!("{node}@{domain}/{resource}"),
}
}
(Some(at), None) => {
let node = nodeprep(&unnormalized[..at]).map_err(|_| Error::NodePrep)?;
length_check(node.len(), Error::NodeEmpty, Error::NodeTooLong)?;
let domain = nameprep(&unnormalized[at + 1..]).map_err(|_| Error::NamePrep)?;
length_check(domain.len(), Error::DomainEmpty, Error::DomainTooLong)?;
orig_at = Some(node.len());
match (node, domain) {
(Cow::Borrowed(_), Cow::Borrowed(_)) => unnormalized.to_string(),
(node, domain) => format!("{node}@{domain}"),
}
}
(None, Some(slash)) => {
let domain = nameprep(&unnormalized[..slash]).map_err(|_| Error::NamePrep)?;
length_check(domain.len(), Error::DomainEmpty, Error::DomainTooLong)?;
let resource =
resourceprep(&unnormalized[slash + 1..]).map_err(|_| Error::ResourcePrep)?;
length_check(resource.len(), Error::ResourceEmpty, Error::ResourceTooLong)?;
orig_slash = Some(domain.len());
match (domain, resource) {
(Cow::Borrowed(_), Cow::Borrowed(_)) => unnormalized.to_string(),
(domain, resource) => format!("{domain}/{resource}"),
}
}
(None, None) => {
let domain = nameprep(unnormalized).map_err(|_| Error::NamePrep)?;
length_check(domain.len(), Error::DomainEmpty, Error::DomainTooLong)?;
domain.into_owned()
}
};
Ok(InnerJid {
normalized,
at: orig_at.and_then(|x| NonZeroU16::new(x as u16)),
slash: orig_slash.and_then(|x| NonZeroU16::new(x as u16)),
})
}
pub(crate) fn node(&self) -> Option<&NodeRef> {
self.at.map(|at| {
let at = u16::from(at) as usize;
NodeRef::from_str_unchecked(&self.normalized[..at])
})
}
pub(crate) fn domain(&self) -> &DomainRef {
match (self.at, self.slash) {
(Some(at), Some(slash)) => {
let at = u16::from(at) as usize;
let slash = u16::from(slash) as usize;
DomainRef::from_str_unchecked(&self.normalized[at + 1..slash])
}
(Some(at), None) => {
let at = u16::from(at) as usize;
DomainRef::from_str_unchecked(&self.normalized[at + 1..])
}
(None, Some(slash)) => {
let slash = u16::from(slash) as usize;
DomainRef::from_str_unchecked(&self.normalized[..slash])
}
(None, None) => DomainRef::from_str_unchecked(&self.normalized),
}
}
pub(crate) fn resource(&self) -> Option<&ResourceRef> {
self.slash.map(|slash| {
let slash = u16::from(slash) as usize;
ResourceRef::from_str_unchecked(&self.normalized[slash + 1..])
})
}
#[inline(always)]
pub(crate) fn as_str(&self) -> &str {
self.normalized.as_str()
}
}
impl FromStr for InnerJid {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
InnerJid::new(s)
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! assert_size (
($t:ty, $sz:expr) => (
assert_eq!(::std::mem::size_of::<$t>(), $sz);
);
);
#[cfg(target_pointer_width = "32")]
#[test]
fn test_size() {
assert_size!(InnerJid, 16);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_size() {
assert_size!(InnerJid, 32);
}
}

View file

@ -31,9 +31,15 @@
//! mixing left-to-write and right-to-left characters
use core::num::NonZeroU16;
use std::borrow::Cow;
use std::fmt;
use std::ops::Deref;
use std::str::FromStr;
use memchr::memchr;
use stringprep::{nameprep, nodeprep, resourceprep};
#[cfg(feature = "serde")]
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
@ -42,53 +48,66 @@ use proc_macro2::TokenStream;
#[cfg(feature = "quote")]
use quote::{quote, ToTokens};
#[cfg(feature = "minidom")]
use minidom::{IntoAttributeValue, Node};
mod error;
pub use crate::error::Error;
mod inner;
use inner::InnerJid;
mod parts;
pub use parts::{DomainPart, DomainRef, NodePart, NodeRef, ResourcePart, ResourceRef};
/// An enum representing a Jabber ID. It can be either a `FullJid` or a `BareJid`.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Jid {
/// Contains a [`BareJid`], without a resource part
Bare(BareJid),
fn length_check(len: usize, error_empty: Error, error_too_long: Error) -> Result<(), Error> {
if len == 0 {
Err(error_empty)
} else if len > 1023 {
Err(error_too_long)
} else {
Ok(())
}
}
/// Contains a [`FullJid`], with a resource part
Full(FullJid),
/// A struct representing a Jabber ID (JID).
///
/// This JID can either be "bare" (without a `/resource` suffix) or full (with
/// a resource suffix).
///
/// In many APIs, it is appropriate to use the more specific types
/// ([`BareJid`] or [`FullJid`]) instead, as these two JID types are generally
/// used in different contexts within XMPP.
///
/// This dynamic type on the other hand can be used in contexts where it is
/// not known, at compile-time, whether a JID is full or bare.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Jid {
normalized: String,
at: Option<NonZeroU16>,
slash: Option<NonZeroU16>,
}
impl FromStr for Jid {
type Err = Error;
fn from_str(s: &str) -> Result<Jid, Error> {
Jid::new(s)
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::new(s)
}
}
impl From<BareJid> for Jid {
fn from(bare_jid: BareJid) -> Jid {
Jid::Bare(bare_jid)
fn from(other: BareJid) -> Self {
other.inner
}
}
impl From<FullJid> for Jid {
fn from(full_jid: FullJid) -> Jid {
Jid::Full(full_jid)
fn from(other: FullJid) -> Self {
other.inner
}
}
impl fmt::Display for Jid {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
Jid::Bare(bare) => bare.fmt(fmt),
Jid::Full(full) => full.fmt(fmt),
}
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str(&self.normalized)
}
}
@ -112,20 +131,81 @@ impl Jid {
/// # Ok(())
/// # }
/// ```
pub fn new(s: &str) -> Result<Jid, Error> {
let inner = InnerJid::new(s)?;
if inner.slash.is_some() {
Ok(Jid::Full(FullJid { inner }))
} else {
Ok(Jid::Bare(BareJid { inner }))
pub fn new(unnormalized: &str) -> Result<Jid, Error> {
let bytes = unnormalized.as_bytes();
let mut orig_at = memchr(b'@', bytes);
let mut orig_slash = memchr(b'/', bytes);
if orig_at.is_some() && orig_slash.is_some() && orig_at > orig_slash {
// This is part of the resource, not a node@domain separator.
orig_at = None;
}
let normalized = match (orig_at, orig_slash) {
(Some(at), Some(slash)) => {
let node = nodeprep(&unnormalized[..at]).map_err(|_| Error::NodePrep)?;
length_check(node.len(), Error::NodeEmpty, Error::NodeTooLong)?;
let domain = nameprep(&unnormalized[at + 1..slash]).map_err(|_| Error::NamePrep)?;
length_check(domain.len(), Error::DomainEmpty, Error::DomainTooLong)?;
let resource =
resourceprep(&unnormalized[slash + 1..]).map_err(|_| Error::ResourcePrep)?;
length_check(resource.len(), Error::ResourceEmpty, Error::ResourceTooLong)?;
orig_at = Some(node.len());
orig_slash = Some(node.len() + domain.len() + 1);
match (node, domain, resource) {
(Cow::Borrowed(_), Cow::Borrowed(_), Cow::Borrowed(_)) => {
unnormalized.to_string()
}
(node, domain, resource) => format!("{node}@{domain}/{resource}"),
}
}
(Some(at), None) => {
let node = nodeprep(&unnormalized[..at]).map_err(|_| Error::NodePrep)?;
length_check(node.len(), Error::NodeEmpty, Error::NodeTooLong)?;
let domain = nameprep(&unnormalized[at + 1..]).map_err(|_| Error::NamePrep)?;
length_check(domain.len(), Error::DomainEmpty, Error::DomainTooLong)?;
orig_at = Some(node.len());
match (node, domain) {
(Cow::Borrowed(_), Cow::Borrowed(_)) => unnormalized.to_string(),
(node, domain) => format!("{node}@{domain}"),
}
}
(None, Some(slash)) => {
let domain = nameprep(&unnormalized[..slash]).map_err(|_| Error::NamePrep)?;
length_check(domain.len(), Error::DomainEmpty, Error::DomainTooLong)?;
let resource =
resourceprep(&unnormalized[slash + 1..]).map_err(|_| Error::ResourcePrep)?;
length_check(resource.len(), Error::ResourceEmpty, Error::ResourceTooLong)?;
orig_slash = Some(domain.len());
match (domain, resource) {
(Cow::Borrowed(_), Cow::Borrowed(_)) => unnormalized.to_string(),
(domain, resource) => format!("{domain}/{resource}"),
}
}
(None, None) => {
let domain = nameprep(unnormalized).map_err(|_| Error::NamePrep)?;
length_check(domain.len(), Error::DomainEmpty, Error::DomainTooLong)?;
domain.into_owned()
}
};
Ok(Self {
normalized,
at: orig_at.and_then(|x| NonZeroU16::new(x as u16)),
slash: orig_slash.and_then(|x| NonZeroU16::new(x as u16)),
})
}
/// Returns the inner String of this JID.
pub fn into_inner(self) -> String {
match self {
Jid::Bare(BareJid { inner }) | Jid::Full(FullJid { inner }) => inner.normalized,
}
self.normalized
}
/// Build a [`Jid`] from typed parts. This method cannot fail because it uses parts that have
@ -138,117 +218,220 @@ impl Jid {
node: Option<&NodeRef>,
domain: &DomainRef,
resource: Option<&ResourceRef>,
) -> Jid {
if let Some(resource) = resource {
Jid::Full(FullJid::from_parts(node, domain, resource))
} else {
Jid::Bare(BareJid::from_parts(node, domain))
) -> Self {
match resource {
Some(resource) => FullJid::from_parts(node, domain, resource).into(),
None => BareJid::from_parts(node, domain).into(),
}
}
/// The optional node part of the JID as reference.
pub fn node(&self) -> Option<&NodeRef> {
match self {
Jid::Bare(BareJid { inner }) | Jid::Full(FullJid { inner }) => inner.node(),
}
self.at.map(|at| {
let at = u16::from(at) as usize;
NodeRef::from_str_unchecked(&self.normalized[..at])
})
}
/// The domain part of the JID as reference
pub fn domain(&self) -> &DomainRef {
match self {
Jid::Bare(BareJid { inner }) | Jid::Full(FullJid { inner }) => inner.domain(),
match (self.at, self.slash) {
(Some(at), Some(slash)) => {
let at = u16::from(at) as usize;
let slash = u16::from(slash) as usize;
DomainRef::from_str_unchecked(&self.normalized[at + 1..slash])
}
(Some(at), None) => {
let at = u16::from(at) as usize;
DomainRef::from_str_unchecked(&self.normalized[at + 1..])
}
(None, Some(slash)) => {
let slash = u16::from(slash) as usize;
DomainRef::from_str_unchecked(&self.normalized[..slash])
}
(None, None) => DomainRef::from_str_unchecked(&self.normalized),
}
}
/// The optional resource of the Jabber ID. It is guaranteed to be present when the JID is
/// a Full variant, which you can check with [`Jid::is_full`].
pub fn resource(&self) -> Option<&ResourceRef> {
match self {
Jid::Bare(BareJid { inner }) | Jid::Full(FullJid { inner }) => inner.resource(),
}
self.slash.map(|slash| {
let slash = u16::from(slash) as usize;
ResourceRef::from_str_unchecked(&self.normalized[slash + 1..])
})
}
/// Allocate a new [`BareJid`] from this JID, discarding the resource.
pub fn to_bare(&self) -> BareJid {
match self {
Jid::Full(jid) => jid.to_bare(),
Jid::Bare(jid) => jid.clone(),
}
BareJid::from_parts(self.node(), self.domain())
}
/// Transforms this JID into a [`BareJid`], throwing away the resource.
pub fn into_bare(self) -> BareJid {
match self {
Jid::Full(jid) => jid.into_bare(),
Jid::Bare(jid) => jid,
///
/// ```
/// # use jid::{BareJid, Jid};
/// let jid: Jid = "foo@bar/baz".parse().unwrap();
/// let bare = jid.into_bare();
/// assert_eq!(bare.to_string(), "foo@bar");
/// ```
pub fn into_bare(mut self) -> BareJid {
if let Some(slash) = self.slash {
// truncate the string
self.normalized.truncate(slash.get() as usize);
self.slash = None;
}
BareJid { inner: self }
}
/// Checks if the JID contains a [`FullJid`]
/// Checks if the JID is a full JID.
pub fn is_full(&self) -> bool {
match self {
Self::Full(_) => true,
Self::Bare(_) => false,
}
self.slash.is_some()
}
/// Checks if the JID contains a [`BareJid`]
/// Checks if the JID is a bare JID.
pub fn is_bare(&self) -> bool {
!self.is_full()
self.slash.is_none()
}
/// Return a reference to the canonical string representation of the JID.
pub fn as_str(&self) -> &str {
match self {
Jid::Bare(BareJid { inner }) | Jid::Full(FullJid { inner }) => inner.as_str(),
&self.normalized
}
/// Try to convert this Jid to a [`FullJid`] if it contains a resource
/// and return a [`BareJid`] otherwise.
///
/// This is useful for match blocks:
///
/// ```
/// # use jid::Jid;
/// let jid: Jid = "foo@bar".parse().unwrap();
/// match jid.try_into_full() {
/// Ok(full) => println!("it is full: {:?}", full),
/// Err(bare) => println!("it is bare: {:?}", bare),
/// }
/// ```
pub fn try_into_full(self) -> Result<FullJid, BareJid> {
if self.slash.is_some() {
Ok(FullJid { inner: self })
} else {
Err(BareJid { inner: self })
}
}
/// Try to convert this Jid reference to a [`&FullJid`][`FullJid`] if it
/// contains a resource and return a [`&BareJid`][`BareJid`] otherwise.
///
/// This is useful for match blocks:
///
/// ```
/// # use jid::Jid;
/// let jid: Jid = "foo@bar".parse().unwrap();
/// match jid.try_as_full() {
/// Ok(full) => println!("it is full: {:?}", full),
/// Err(bare) => println!("it is bare: {:?}", bare),
/// }
/// ```
pub fn try_as_full(&self) -> Result<&FullJid, &BareJid> {
if self.slash.is_some() {
Ok(unsafe {
// SAFETY: FullJid is #[repr(transparent)] of Jid
// SOUNDNESS: we asserted that self.slash is set above
std::mem::transmute::<&Jid, &FullJid>(self)
})
} else {
Err(unsafe {
// SAFETY: BareJid is #[repr(transparent)] of Jid
// SOUNDNESS: we asserted that self.slash is unset above
std::mem::transmute::<&Jid, &BareJid>(self)
})
}
}
/// Try to convert this mutable Jid reference to a
/// [`&mut FullJid`][`FullJid`] if it contains a resource and return a
/// [`&mut BareJid`][`BareJid`] otherwise.
pub fn try_as_full_mut(&mut self) -> Result<&mut FullJid, &mut BareJid> {
if self.slash.is_some() {
Ok(unsafe {
// SAFETY: FullJid is #[repr(transparent)] of Jid
// SOUNDNESS: we asserted that self.slash is set above
std::mem::transmute::<&mut Jid, &mut FullJid>(self)
})
} else {
Err(unsafe {
// SAFETY: BareJid is #[repr(transparent)] of Jid
// SOUNDNESS: we asserted that self.slash is unset above
std::mem::transmute::<&mut Jid, &mut BareJid>(self)
})
}
}
#[doc(hidden)]
#[allow(non_snake_case)]
#[deprecated(
since = "0.11.0",
note = "use Jid::from (for construction of Jid values) or Jid::try_into_full/Jid::try_as_full (for match blocks) instead"
)]
pub fn Bare(other: BareJid) -> Self {
Self::from(other)
}
#[doc(hidden)]
#[allow(non_snake_case)]
#[deprecated(
since = "0.11.0",
note = "use Jid::from (for construction of Jid values) or Jid::try_into_full/Jid::try_as_full (for match blocks) instead"
)]
pub fn Full(other: BareJid) -> Self {
Self::from(other)
}
}
impl TryFrom<Jid> for FullJid {
type Error = Error;
fn try_from(jid: Jid) -> Result<Self, Self::Error> {
match jid {
Jid::Full(full) => Ok(full),
Jid::Bare(_) => Err(Error::ResourceMissingInFullJid),
fn try_from(inner: Jid) -> Result<Self, Self::Error> {
if inner.slash.is_none() {
return Err(Error::ResourceMissingInFullJid);
}
Ok(Self { inner })
}
}
impl TryFrom<Jid> for BareJid {
type Error = Error;
fn try_from(inner: Jid) -> Result<Self, Self::Error> {
if inner.slash.is_some() {
return Err(Error::ResourceInBareJid);
}
Ok(Self { inner })
}
}
impl PartialEq<Jid> for FullJid {
fn eq(&self, other: &Jid) -> bool {
match other {
Jid::Full(full) => self == full,
Jid::Bare(_) => false,
}
&self.inner == other
}
}
impl PartialEq<Jid> for BareJid {
fn eq(&self, other: &Jid) -> bool {
match other {
Jid::Full(_) => false,
Jid::Bare(bare) => self == bare,
}
&self.inner == other
}
}
impl PartialEq<FullJid> for Jid {
fn eq(&self, other: &FullJid) -> bool {
match self {
Jid::Full(full) => full == other,
Jid::Bare(_) => false,
}
self == &other.inner
}
}
impl PartialEq<BareJid> for Jid {
fn eq(&self, other: &BareJid) -> bool {
match self {
Jid::Full(_) => false,
Jid::Bare(bare) => bare == other,
}
self == &other.inner
}
}
@ -263,9 +446,10 @@ impl PartialEq<BareJid> for Jid {
/// Unlike a [`BareJid`], it always contains a resource, and should only be used when you are
/// certain there is no case where a resource can be missing. Otherwise, use a [`Jid`] or
/// [`BareJid`].
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)] // WARNING: Jid::try_as_* relies on this for safety!
pub struct FullJid {
inner: InnerJid,
inner: Jid,
}
/// A struct representing a bare Jabber ID, without a resource part.
@ -276,32 +460,59 @@ pub struct FullJid {
///
/// Unlike a [`FullJid`], it cant contain a resource, and should only be used when you are certain
/// there is no case where a resource can be set. Otherwise, use a [`Jid`] or [`FullJid`].
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)] // WARNING: Jid::try_as_* relies on this for safety!
pub struct BareJid {
inner: InnerJid,
inner: Jid,
}
impl Deref for FullJid {
type Target = Jid;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl Deref for BareJid {
type Target = Jid;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl fmt::Debug for FullJid {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_tuple("FullJid").field(&self.inner).finish()
}
}
impl fmt::Debug for BareJid {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_tuple("BareJid").field(&self.inner).finish()
}
}
impl fmt::Display for FullJid {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt.write_str(&self.inner.normalized)
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.inner, fmt)
}
}
impl fmt::Display for BareJid {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt.write_str(&self.inner.normalized)
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.inner, fmt)
}
}
#[cfg(feature = "serde")]
impl Serialize for Jid {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.normalized)
}
}
@ -311,7 +522,7 @@ impl Serialize for FullJid {
where
S: Serializer,
{
serializer.serialize_str(&self.inner.normalized)
self.inner.serialize(serializer)
}
}
@ -321,15 +532,26 @@ impl Serialize for BareJid {
where
S: Serializer,
{
serializer.serialize_str(&self.inner.normalized)
self.inner.serialize(serializer)
}
}
impl FromStr for FullJid {
type Err = Error;
fn from_str(s: &str) -> Result<FullJid, Error> {
FullJid::new(s)
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::new(s)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Jid {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Jid::new(&s).map_err(de::Error::custom)
}
}
@ -339,8 +561,8 @@ impl<'de> Deserialize<'de> for FullJid {
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
FullJid::from_str(&s).map_err(de::Error::custom)
let jid = Jid::deserialize(deserializer)?;
jid.try_into().map_err(de::Error::custom)
}
}
@ -350,17 +572,17 @@ impl<'de> Deserialize<'de> for BareJid {
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
BareJid::from_str(&s).map_err(de::Error::custom)
let jid = Jid::deserialize(deserializer)?;
jid.try_into().map_err(de::Error::custom)
}
}
#[cfg(feature = "quote")]
impl ToTokens for Jid {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend(match self {
Jid::Full(full) => quote! { Jid::Full(#full) },
Jid::Bare(bare) => quote! { Jid::Bare(#bare) },
let s = &self.normalized;
tokens.extend(quote! {
::jid::Jid::new(#s).unwrap()
});
}
}
@ -368,18 +590,20 @@ impl ToTokens for Jid {
#[cfg(feature = "quote")]
impl ToTokens for FullJid {
fn to_tokens(&self, tokens: &mut TokenStream) {
let inner = &self.inner.normalized;
let t = quote! { FullJid::new(#inner).unwrap() };
tokens.extend(t);
let s = &self.inner.normalized;
tokens.extend(quote! {
::jid::FullJid::new(#s).unwrap()
});
}
}
#[cfg(feature = "quote")]
impl ToTokens for BareJid {
fn to_tokens(&self, tokens: &mut TokenStream) {
let inner = &self.inner.normalized;
let t = quote! { BareJid::new(#inner).unwrap() };
tokens.extend(t);
let s = &self.inner.normalized;
tokens.extend(quote! {
::jid::BareJid::new(#s).unwrap()
});
}
}
@ -403,18 +627,8 @@ impl FullJid {
/// # Ok(())
/// # }
/// ```
pub fn new(s: &str) -> Result<FullJid, Error> {
let inner = InnerJid::new(s)?;
if inner.slash.is_some() {
Ok(FullJid { inner })
} else {
Err(Error::ResourceMissingInFullJid)
}
}
/// Returns the inner String of this JID.
pub fn into_inner(self) -> String {
self.inner.normalized
pub fn new(unnormalized: &str) -> Result<Self, Error> {
Jid::new(unnormalized)?.try_into()
}
/// Build a [`FullJid`] from typed parts. This method cannot fail because it uses parts that have
@ -445,62 +659,26 @@ impl FullJid {
)
};
let inner = InnerJid {
let inner = Jid {
normalized,
at,
slash,
};
FullJid { inner }
}
/// The optional node part of the JID as reference.
pub fn node(&self) -> Option<&NodeRef> {
self.inner.node()
}
/// The domain part of the JID as reference
pub fn domain(&self) -> &DomainRef {
self.inner.domain()
Self { inner }
}
/// The optional resource of the Jabber ID. Since this is a full JID it is always present.
pub fn resource(&self) -> &ResourceRef {
self.inner.resource().unwrap()
}
/// Allocate a new [`BareJid`] from this full JID, discarding the resource.
pub fn to_bare(&self) -> BareJid {
let slash = self.inner.slash.unwrap().get() as usize;
let normalized = self.inner.normalized[..slash].to_string();
let inner = InnerJid {
normalized,
at: self.inner.at,
slash: None,
};
BareJid { inner }
}
/// Transforms this full JID into a [`BareJid`], discarding the resource.
pub fn into_bare(mut self) -> BareJid {
let slash = self.inner.slash.unwrap().get() as usize;
self.inner.normalized.truncate(slash);
self.inner.normalized.shrink_to_fit();
self.inner.slash = None;
BareJid { inner: self.inner }
}
/// Return a reference to the canonical string representation of the JID.
pub fn as_str(&self) -> &str {
self.inner.as_str()
}
}
impl FromStr for BareJid {
type Err = Error;
fn from_str(s: &str) -> Result<BareJid, Error> {
BareJid::new(s)
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::new(s)
}
}
@ -523,18 +701,8 @@ impl BareJid {
/// # Ok(())
/// # }
/// ```
pub fn new(s: &str) -> Result<BareJid, Error> {
let inner = InnerJid::new(s)?;
if inner.slash.is_none() {
Ok(BareJid { inner })
} else {
Err(Error::ResourceInBareJid)
}
}
/// Returns the inner String of this JID.
pub fn into_inner(self) -> String {
self.inner.normalized
pub fn new(unnormalized: &str) -> Result<Self, Error> {
Jid::new(unnormalized)?.try_into()
}
/// Build a [`BareJid`] from typed parts. This method cannot fail because it uses parts that have
@ -543,7 +711,7 @@ impl BareJid {
/// This method allocates and does not consume the typed parts. To avoid
/// allocation if `node` is known to be `None` and `domain` is owned, you
/// can use `domain.into()`.
pub fn from_parts(node: Option<&NodeRef>, domain: &DomainRef) -> BareJid {
pub fn from_parts(node: Option<&NodeRef>, domain: &DomainRef) -> Self {
let (at, normalized) = if let Some(node) = node {
// Parts are never empty so len > 0 for NonZeroU16::new is always Some
(
@ -554,23 +722,13 @@ impl BareJid {
(None, domain.to_string())
};
let inner = InnerJid {
let inner = Jid {
normalized,
at,
slash: None,
};
BareJid { inner }
}
/// The optional node part of the JID as reference.
pub fn node(&self) -> Option<&NodeRef> {
self.inner.node()
}
/// The domain part of the JID as reference
pub fn domain(&self) -> &DomainRef {
self.inner.domain()
Self { inner }
}
/// Constructs a [`BareJid`] from the bare JID, by specifying a [`ResourcePart`].
@ -592,7 +750,7 @@ impl BareJid {
pub fn with_resource(&self, resource: &ResourceRef) -> FullJid {
let slash = NonZeroU16::new(self.inner.normalized.len() as u16);
let normalized = format!("{}/{resource}", self.inner.normalized);
let inner = InnerJid {
let inner = Jid {
normalized,
at: self.inner.at,
slash,
@ -620,16 +778,8 @@ impl BareJid {
let resource = ResourcePart::new(resource)?;
Ok(self.with_resource(&resource))
}
/// Return a reference to the canonical string representation of the JID.
pub fn as_str(&self) -> &str {
self.inner.as_str()
}
}
#[cfg(feature = "minidom")]
use minidom::{IntoAttributeValue, Node};
#[cfg(feature = "minidom")]
impl IntoAttributeValue for Jid {
fn into_attribute_value(self) -> Option<String> {
@ -639,7 +789,7 @@ impl IntoAttributeValue for Jid {
#[cfg(feature = "minidom")]
impl From<Jid> for Node {
fn from(jid: Jid) -> Node {
fn from(jid: Jid) -> Self {
Node::Text(jid.to_string())
}
}
@ -647,28 +797,28 @@ impl From<Jid> for Node {
#[cfg(feature = "minidom")]
impl IntoAttributeValue for FullJid {
fn into_attribute_value(self) -> Option<String> {
Some(self.to_string())
self.inner.into_attribute_value()
}
}
#[cfg(feature = "minidom")]
impl From<FullJid> for Node {
fn from(jid: FullJid) -> Node {
Node::Text(jid.to_string())
fn from(jid: FullJid) -> Self {
jid.inner.into()
}
}
#[cfg(feature = "minidom")]
impl IntoAttributeValue for BareJid {
fn into_attribute_value(self) -> Option<String> {
Some(self.to_string())
self.inner.into_attribute_value()
}
}
#[cfg(feature = "minidom")]
impl From<BareJid> for Node {
fn from(jid: BareJid) -> Node {
Node::Text(jid.to_string())
fn from(other: BareJid) -> Self {
other.inner.into()
}
}
@ -689,7 +839,7 @@ mod tests {
fn test_size() {
assert_size!(BareJid, 16);
assert_size!(FullJid, 16);
assert_size!(Jid, 20);
assert_size!(Jid, 16);
}
#[cfg(target_pointer_width = "64")]
@ -697,7 +847,7 @@ mod tests {
fn test_size() {
assert_size!(BareJid, 32);
assert_size!(FullJid, 32);
assert_size!(Jid, 40);
assert_size!(Jid, 32);
}
#[test]
@ -735,8 +885,8 @@ mod tests {
let full = FullJid::from_str("a@b.c/d").unwrap();
let bare = BareJid::from_str("e@f.g").unwrap();
assert_eq!(Jid::from_str("a@b.c/d"), Ok(Jid::Full(full)));
assert_eq!(Jid::from_str("e@f.g"), Ok(Jid::Bare(bare)));
assert_eq!(Jid::from_str("a@b.c/d").unwrap(), full);
assert_eq!(Jid::from_str("e@f.g").unwrap(), bare);
}
#[test]
@ -792,13 +942,13 @@ mod tests {
let full = FullJid::new("a@b.c/d").unwrap();
let bare = BareJid::new("a@b.c").unwrap();
assert_eq!(FullJid::try_from(Jid::Full(full.clone())), Ok(full.clone()));
assert_eq!(FullJid::try_from(Jid::from(full.clone())), Ok(full.clone()));
assert_eq!(
FullJid::try_from(Jid::Bare(bare.clone())),
FullJid::try_from(Jid::from(bare.clone())),
Err(Error::ResourceMissingInFullJid),
);
assert_eq!(Jid::Bare(full.clone().to_bare()), bare.clone());
assert_eq!(Jid::Bare(bare.clone()), bare);
assert_eq!(Jid::from(full.clone().to_bare()), bare.clone());
assert_eq!(Jid::from(bare.clone()), bare);
}
#[test]
@ -836,10 +986,10 @@ mod tests {
assert_eq!(FullJid::new("a@b/c").unwrap().to_string(), "a@b/c");
assert_eq!(BareJid::new("a@b").unwrap().to_string(), "a@b");
assert_eq!(
Jid::Full(FullJid::new("a@b/c").unwrap()).to_string(),
Jid::from(FullJid::new("a@b/c").unwrap()).to_string(),
"a@b/c"
);
assert_eq!(Jid::Bare(BareJid::new("a@b").unwrap()).to_string(), "a@b");
assert_eq!(Jid::from(BareJid::new("a@b").unwrap()).to_string(), "a@b");
}
#[cfg(feature = "minidom")]
@ -847,11 +997,11 @@ mod tests {
fn minidom() {
let elem: minidom::Element = "<message xmlns='ns1' from='a@b/c'/>".parse().unwrap();
let to: Jid = elem.attr("from").unwrap().parse().unwrap();
assert_eq!(to, Jid::Full(FullJid::new("a@b/c").unwrap()));
assert_eq!(to, Jid::from(FullJid::new("a@b/c").unwrap()));
let elem: minidom::Element = "<message xmlns='ns1' from='a@b'/>".parse().unwrap();
let to: Jid = elem.attr("from").unwrap().parse().unwrap();
assert_eq!(to, Jid::Bare(BareJid::new("a@b").unwrap()));
assert_eq!(to, Jid::from(BareJid::new("a@b").unwrap()));
let elem: minidom::Element = "<message xmlns='ns1' from='a@b/c'/>".parse().unwrap();
let to: FullJid = elem.attr("from").unwrap().parse().unwrap();
@ -877,7 +1027,7 @@ mod tests {
.build();
assert_eq!(elem.attr("from"), Some(bare.to_string().as_str()));
let jid = Jid::Bare(bare.clone());
let jid = Jid::from(bare.clone());
let _elem = minidom::Element::builder("message", "jabber:client")
.attr("from", jid)
.build();
@ -939,4 +1089,52 @@ mod tests {
assert_eq!(jid1, jid2);
assert_eq!(jid2, jid3);
}
#[test]
fn jid_match_replacement_try_as() {
let jid1 = Jid::new("foo@bar").unwrap();
let jid2 = Jid::new("foo@bar/baz").unwrap();
match jid1.try_as_full() {
Err(_) => (),
other => panic!("unexpected result: {:?}", other),
};
match jid2.try_as_full() {
Ok(_) => (),
other => panic!("unexpected result: {:?}", other),
};
}
#[test]
fn jid_match_replacement_try_as_mut() {
let mut jid1 = Jid::new("foo@bar").unwrap();
let mut jid2 = Jid::new("foo@bar/baz").unwrap();
match jid1.try_as_full_mut() {
Err(_) => (),
other => panic!("unexpected result: {:?}", other),
};
match jid2.try_as_full_mut() {
Ok(_) => (),
other => panic!("unexpected result: {:?}", other),
};
}
#[test]
fn jid_match_replacement_try_into() {
let jid1 = Jid::new("foo@bar").unwrap();
let jid2 = Jid::new("foo@bar/baz").unwrap();
match jid1.try_as_full() {
Err(_) => (),
other => panic!("unexpected result: {:?}", other),
};
match jid2.try_as_full() {
Ok(_) => (),
other => panic!("unexpected result: {:?}", other),
};
}
}

View file

@ -5,7 +5,7 @@ use std::str::FromStr;
use stringprep::{nameprep, nodeprep, resourceprep};
use crate::{BareJid, Error, InnerJid, Jid};
use crate::{BareJid, Error, Jid};
fn length_check(len: usize, error_empty: Error, error_too_long: Error) -> Result<(), Error> {
if len == 0 {
@ -250,18 +250,18 @@ impl DomainRef {
impl From<DomainPart> for BareJid {
fn from(other: DomainPart) -> Self {
BareJid {
inner: InnerJid {
normalized: other.0,
at: None,
slash: None,
},
inner: other.into(),
}
}
}
impl From<DomainPart> for Jid {
fn from(other: DomainPart) -> Self {
Jid::Bare(other.into())
Jid {
normalized: other.0,
at: None,
slash: None,
}
}
}

View file

@ -88,7 +88,7 @@ impl From<BindResponse> for FullJid {
impl From<BindResponse> for Jid {
fn from(bind: BindResponse) -> Jid {
Jid::Full(bind.jid)
Jid::from(bind.jid)
}
}

View file

@ -71,8 +71,8 @@ mod tests {
assert_size!(Enable, 0);
assert_size!(Disable, 0);
assert_size!(Private, 0);
assert_size!(Received, 152);
assert_size!(Sent, 152);
assert_size!(Received, 140);
assert_size!(Sent, 140);
}
#[cfg(target_pointer_width = "64")]
@ -81,8 +81,8 @@ mod tests {
assert_size!(Enable, 0);
assert_size!(Disable, 0);
assert_size!(Private, 0);
assert_size!(Received, 288);
assert_size!(Sent, 288);
assert_size!(Received, 264);
assert_size!(Sent, 264);
}
#[test]

View file

@ -40,13 +40,13 @@ mod tests {
#[cfg(target_pointer_width = "32")]
#[test]
fn test_size() {
assert_size!(Delay, 48);
assert_size!(Delay, 44);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_size() {
assert_size!(Delay, 80);
assert_size!(Delay, 72);
}
#[test]

View file

@ -248,7 +248,7 @@ mod tests {
assert_size!(DiscoInfoQuery, 12);
assert_size!(DiscoInfoResult, 48);
assert_size!(Item, 44);
assert_size!(Item, 40);
assert_size!(DiscoItemsQuery, 52);
assert_size!(DiscoItemsResult, 64);
}
@ -261,7 +261,7 @@ mod tests {
assert_size!(DiscoInfoQuery, 24);
assert_size!(DiscoInfoResult, 96);
assert_size!(Item, 88);
assert_size!(Item, 80);
assert_size!(DiscoItemsQuery, 104);
assert_size!(DiscoItemsResult, 128);
}

View file

@ -32,13 +32,13 @@ mod tests {
#[cfg(target_pointer_width = "32")]
#[test]
fn test_size() {
assert_size!(Forwarded, 152);
assert_size!(Forwarded, 140);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_size() {
assert_size!(Forwarded, 288);
assert_size!(Forwarded, 264);
}
#[test]

View file

@ -232,15 +232,15 @@ mod tests {
#[cfg(target_pointer_width = "32")]
#[test]
fn test_size() {
assert_size!(IqType, 96);
assert_size!(Iq, 148);
assert_size!(IqType, 92);
assert_size!(Iq, 156);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_size() {
assert_size!(IqType, 192);
assert_size!(Iq, 296);
assert_size!(IqType, 184);
assert_size!(Iq, 272);
}
#[test]

View file

@ -46,14 +46,14 @@ mod tests {
#[test]
fn test_size() {
assert_size!(JidPrepQuery, 12);
assert_size!(JidPrepResponse, 20);
assert_size!(JidPrepResponse, 16);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_size() {
assert_size!(JidPrepQuery, 24);
assert_size!(JidPrepResponse, 40);
assert_size!(JidPrepResponse, 32);
}
#[test]

View file

@ -692,7 +692,7 @@ mod tests {
assert_size!(Reason, 1);
assert_size!(ReasonElement, 16);
assert_size!(SessionId, 12);
assert_size!(Jingle, 112);
assert_size!(Jingle, 104);
}
#[cfg(target_pointer_width = "64")]
@ -711,7 +711,7 @@ mod tests {
assert_size!(Reason, 1);
assert_size!(ReasonElement, 32);
assert_size!(SessionId, 24);
assert_size!(Jingle, 224);
assert_size!(Jingle, 208);
}
#[test]

View file

@ -284,7 +284,7 @@ mod tests {
assert_size!(Mode, 1);
assert_size!(CandidateId, 12);
assert_size!(StreamId, 12);
assert_size!(Candidate, 60);
assert_size!(Candidate, 56);
assert_size!(TransportPayload, 16);
assert_size!(Transport, 44);
}
@ -296,7 +296,7 @@ mod tests {
assert_size!(Mode, 1);
assert_size!(CandidateId, 24);
assert_size!(StreamId, 24);
assert_size!(Candidate, 96);
assert_size!(Candidate, 88);
assert_size!(TransportPayload, 32);
assert_size!(Transport, 88);
}

View file

@ -166,7 +166,7 @@ mod tests {
fn test_size() {
assert_size!(QueryId, 12);
assert_size!(Query, 120);
assert_size!(Result_, 176);
assert_size!(Result_, 164);
assert_size!(Complete, 1);
assert_size!(Fin, 44);
}
@ -176,7 +176,7 @@ mod tests {
fn test_size() {
assert_size!(QueryId, 24);
assert_size!(Query, 240);
assert_size!(Result_, 336);
assert_size!(Result_, 312);
assert_size!(Complete, 1);
assert_size!(Fin, 88);
}

View file

@ -307,7 +307,7 @@ mod tests {
assert_size!(Body, 12);
assert_size!(Subject, 12);
assert_size!(Thread, 12);
assert_size!(Message, 104);
assert_size!(Message, 96);
}
#[cfg(target_pointer_width = "64")]
@ -317,7 +317,7 @@ mod tests {
assert_size!(Body, 24);
assert_size!(Subject, 24);
assert_size!(Thread, 24);
assert_size!(Message, 208);
assert_size!(Message, 192);
}
#[test]

View file

@ -379,7 +379,7 @@ mod tests {
fn test_size() {
assert_size!(Show, 1);
assert_size!(Type, 1);
assert_size!(Presence, 80);
assert_size!(Presence, 72);
}
#[cfg(target_pointer_width = "64")]
@ -387,7 +387,7 @@ mod tests {
fn test_size() {
assert_size!(Show, 1);
assert_size!(Type, 1);
assert_size!(Presence, 160);
assert_size!(Presence, 144);
}
#[test]

View file

@ -200,11 +200,11 @@ mod tests {
node: NodeName(String::from("foo")),
affiliations: vec![
Affiliation {
jid: Jid::Bare(BareJid::from_str("hamlet@denmark.lit").unwrap()),
jid: Jid::from(BareJid::from_str("hamlet@denmark.lit").unwrap()),
affiliation: AffiliationAttribute::Owner,
},
Affiliation {
jid: Jid::Bare(BareJid::from_str("polonius@denmark.lit").unwrap()),
jid: Jid::from(BareJid::from_str("polonius@denmark.lit").unwrap()),
affiliation: AffiliationAttribute::Outcast,
},
],
@ -335,22 +335,22 @@ mod tests {
node: NodeName(String::from("foo")),
subscriptions: vec![
SubscriptionElem {
jid: Jid::Bare(BareJid::from_str("hamlet@denmark.lit").unwrap()),
jid: Jid::from(BareJid::from_str("hamlet@denmark.lit").unwrap()),
subscription: Subscription::Subscribed,
subid: None,
},
SubscriptionElem {
jid: Jid::Bare(BareJid::from_str("polonius@denmark.lit").unwrap()),
jid: Jid::from(BareJid::from_str("polonius@denmark.lit").unwrap()),
subscription: Subscription::Unconfigured,
subid: None,
},
SubscriptionElem {
jid: Jid::Bare(BareJid::from_str("bernardo@denmark.lit").unwrap()),
jid: Jid::from(BareJid::from_str("bernardo@denmark.lit").unwrap()),
subscription: Subscription::Subscribed,
subid: Some(String::from("123-abc")),
},
SubscriptionElem {
jid: Jid::Bare(BareJid::from_str("bernardo@denmark.lit").unwrap()),
jid: Jid::from(BareJid::from_str("bernardo@denmark.lit").unwrap()),
subscription: Subscription::Subscribed,
subid: Some(String::from("004-yyy")),
},

View file

@ -319,7 +319,7 @@ mod tests {
fn test_size() {
assert_size!(ErrorType, 1);
assert_size!(DefinedCondition, 1);
assert_size!(StanzaError, 96);
assert_size!(StanzaError, 92);
}
#[cfg(target_pointer_width = "64")]
@ -327,7 +327,7 @@ mod tests {
fn test_size() {
assert_size!(ErrorType, 1);
assert_size!(DefinedCondition, 1);
assert_size!(StanzaError, 192);
assert_size!(StanzaError, 184);
}
#[test]

View file

@ -44,14 +44,14 @@ mod tests {
#[cfg(target_pointer_width = "32")]
#[test]
fn test_size() {
assert_size!(StanzaId, 32);
assert_size!(StanzaId, 24);
assert_size!(OriginId, 12);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_size() {
assert_size!(StanzaId, 64);
assert_size!(StanzaId, 56);
assert_size!(OriginId, 24);
}

View file

@ -75,7 +75,7 @@ async fn main() -> Result<(), Option<()>> {
Event::RoomJoined(jid) => {
println!("Joined room {}.", jid);
client
.send_message(Jid::Bare(jid), MessageType::Groupchat, "en", "Hello world!")
.send_message(Jid::from(jid), MessageType::Groupchat, "en", "Hello world!")
.await;
}
Event::RoomLeft(jid) => {

View file

@ -25,8 +25,8 @@ pub async fn handle_message_chat<C: ServerConnector>(
for payload in &message.payloads {
if let Ok(_) = MucUser::try_from(payload.clone()) {
let event = match from.clone() {
Jid::Bare(bare) => {
let event = match from.clone().try_into_full() {
Err(bare) => {
// TODO: Can a service message be of type Chat/Normal and not Groupchat?
warn!("Received misformed MessageType::Chat in muc#user namespace from a bare JID.");
Event::ServiceMessage(
@ -36,7 +36,7 @@ pub async fn handle_message_chat<C: ServerConnector>(
time_info.clone(),
)
}
Jid::Full(full) => Event::RoomPrivateMessage(
Ok(full) => Event::RoomPrivateMessage(
message.id.clone(),
full.to_bare(),
full.resource().to_string(),

View file

@ -28,17 +28,15 @@ pub async fn handle_message_group_chat<C: ServerConnector>(
}
if let Some((_lang, body)) = message.get_best_body(langs) {
let event = match from.clone() {
Jid::Full(full) => Event::RoomMessage(
let event = match from.clone().try_into_full() {
Ok(full) => Event::RoomMessage(
message.id.clone(),
from.to_bare(),
full.resource().to_string(),
body.clone(),
time_info,
),
Jid::Bare(bare) => {
Event::ServiceMessage(message.id.clone(), bare, body.clone(), time_info)
}
Err(bare) => Event::ServiceMessage(message.id.clone(), bare, body.clone(), time_info),
};
events.push(event)
}