lib.rs

   1// Copyright (c) 2017, 2018 lumi <lumi@pew.im>
   2// Copyright (c) 2017, 2018, 2019 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
   3// Copyright (c) 2017, 2018, 2019 Maxime “pep” Buquet <pep@bouah.net>
   4// Copyright (c) 2017, 2018 Astro <astro@spaceboyz.net>
   5// Copyright (c) 2017 Bastien Orivel <eijebong@bananium.fr>
   6//
   7// This Source Code Form is subject to the terms of the Mozilla Public
   8// License, v. 2.0. If a copy of the MPL was not distributed with this
   9// file, You can obtain one at http://mozilla.org/MPL/2.0/.
  10
  11#![no_std]
  12#![deny(missing_docs)]
  13
  14//! Represents XMPP addresses, also known as JabberIDs (JIDs) for the [XMPP](https://xmpp.org/)
  15//! protocol. A [`Jid`] can have between one and three parts in the form `node@domain/resource`:
  16//! - the (optional) node part designates a specific account/service on a server, for example
  17//!   `username@server.com`
  18//! - the domain part designates a server, for example `irc.jabberfr.org`
  19//! - the (optional) resource part designates a more specific client, such as a participant in a
  20//!   groupchat (`jabberfr@chat.jabberfr.org/user`) or a specific client device associated with an
  21//!   account (`user@example.com/dino`)
  22//!
  23//! The [`Jid`] enum can be one of two variants, containing a more specific type:
  24//! - [`BareJid`] (`Jid::Bare` variant): a JID without a resource
  25//! - [`FullJid`] (`Jid::Full` variant): a JID with a resource
  26//!
  27//! Jids as per the XMPP protocol only ever contain valid UTF-8. However, creating any form of Jid
  28//! can fail in one of the following cases:
  29//! - wrong syntax: creating a Jid with an empty (yet declared) node or resource part, such as
  30//!   `@example.com` or `user@example.com/`
  31//! - stringprep error: some characters were invalid according to the stringprep algorithm, such as
  32//!   mixing left-to-write and right-to-left characters
  33
  34extern crate alloc;
  35
  36#[cfg(any(feature = "std", test))]
  37extern crate std;
  38
  39use alloc::borrow::Cow;
  40use alloc::format;
  41use alloc::string::{String, ToString};
  42use core::borrow::Borrow;
  43use core::cmp::Ordering;
  44use core::fmt;
  45use core::hash::{Hash, Hasher};
  46use core::mem;
  47use core::num::NonZeroU16;
  48use core::ops::Deref;
  49use core::str::FromStr;
  50
  51use memchr::memchr;
  52
  53use stringprep::{nameprep, nodeprep, resourceprep};
  54
  55#[cfg(feature = "serde")]
  56use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
  57
  58#[cfg(feature = "quote")]
  59use proc_macro2::TokenStream;
  60#[cfg(feature = "quote")]
  61use quote::{quote, ToTokens};
  62
  63#[cfg(feature = "minidom")]
  64use minidom::{IntoAttributeValue, Node};
  65
  66mod error;
  67pub use crate::error::Error;
  68
  69mod parts;
  70pub use parts::{DomainPart, DomainRef, NodePart, NodeRef, ResourcePart, ResourceRef};
  71
  72fn length_check(len: usize, error_empty: Error, error_too_long: Error) -> Result<(), Error> {
  73    if len == 0 {
  74        Err(error_empty)
  75    } else if len > 1023 {
  76        Err(error_too_long)
  77    } else {
  78        Ok(())
  79    }
  80}
  81
  82/// A struct representing a Jabber ID (JID).
  83///
  84/// This JID can either be "bare" (without a `/resource` suffix) or full (with
  85/// a resource suffix).
  86///
  87/// In many APIs, it is appropriate to use the more specific types
  88/// ([`BareJid`] or [`FullJid`]) instead, as these two JID types are generally
  89/// used in different contexts within XMPP.
  90///
  91/// This dynamic type on the other hand can be used in contexts where it is
  92/// not known, at compile-time, whether a JID is full or bare.
  93#[derive(Debug, Clone, Eq)]
  94pub struct Jid {
  95    normalized: String,
  96    at: Option<NonZeroU16>,
  97    slash: Option<NonZeroU16>,
  98}
  99
 100impl PartialEq for Jid {
 101    fn eq(&self, other: &Jid) -> bool {
 102        self.normalized == other.normalized
 103    }
 104}
 105
 106impl PartialOrd for Jid {
 107    fn partial_cmp(&self, other: &Jid) -> Option<Ordering> {
 108        self.normalized.partial_cmp(&other.normalized)
 109    }
 110}
 111
 112impl Ord for Jid {
 113    fn cmp(&self, other: &Jid) -> Ordering {
 114        self.normalized.cmp(&other.normalized)
 115    }
 116}
 117
 118impl Hash for Jid {
 119    fn hash<H: Hasher>(&self, state: &mut H) {
 120        self.normalized.hash(state)
 121    }
 122}
 123
 124impl FromStr for Jid {
 125    type Err = Error;
 126
 127    fn from_str(s: &str) -> Result<Self, Self::Err> {
 128        Self::new(s)
 129    }
 130}
 131
 132impl From<BareJid> for Jid {
 133    fn from(other: BareJid) -> Self {
 134        other.inner
 135    }
 136}
 137
 138impl From<FullJid> for Jid {
 139    fn from(other: FullJid) -> Self {
 140        other.inner
 141    }
 142}
 143
 144impl fmt::Display for Jid {
 145    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
 146        fmt.write_str(&self.normalized)
 147    }
 148}
 149
 150impl Jid {
 151    /// Constructs a Jabber ID from a string. This is of the form
 152    /// `node`@`domain`/`resource`, where node and resource parts are optional.
 153    /// If you want a non-fallible version, use [`Jid::from_parts`] instead.
 154    ///
 155    /// # Examples
 156    ///
 157    /// ```
 158    /// use jid::Jid;
 159    /// # use jid::Error;
 160    ///
 161    /// # fn main() -> Result<(), Error> {
 162    /// let jid = Jid::new("node@domain/resource")?;
 163    ///
 164    /// assert_eq!(jid.node().map(|x| x.as_str()), Some("node"));
 165    /// assert_eq!(jid.domain().as_str(), "domain");
 166    /// assert_eq!(jid.resource().map(|x| x.as_str()), Some("resource"));
 167    /// # Ok(())
 168    /// # }
 169    /// ```
 170    pub fn new(unnormalized: &str) -> Result<Jid, Error> {
 171        let bytes = unnormalized.as_bytes();
 172        let mut orig_at = memchr(b'@', bytes);
 173        let mut orig_slash = memchr(b'/', bytes);
 174        if orig_at.is_some() && orig_slash.is_some() && orig_at > orig_slash {
 175            // This is part of the resource, not a node@domain separator.
 176            orig_at = None;
 177        }
 178
 179        let normalized = match (orig_at, orig_slash) {
 180            (Some(at), Some(slash)) => {
 181                let node = nodeprep(&unnormalized[..at]).map_err(|_| Error::NodePrep)?;
 182                length_check(node.len(), Error::NodeEmpty, Error::NodeTooLong)?;
 183
 184                let domain = nameprep(&unnormalized[at + 1..slash]).map_err(|_| Error::NamePrep)?;
 185                length_check(domain.len(), Error::DomainEmpty, Error::DomainTooLong)?;
 186
 187                let resource =
 188                    resourceprep(&unnormalized[slash + 1..]).map_err(|_| Error::ResourcePrep)?;
 189                length_check(resource.len(), Error::ResourceEmpty, Error::ResourceTooLong)?;
 190
 191                orig_at = Some(node.len());
 192                orig_slash = Some(node.len() + domain.len() + 1);
 193                match (node, domain, resource) {
 194                    (Cow::Borrowed(_), Cow::Borrowed(_), Cow::Borrowed(_)) => {
 195                        unnormalized.to_string()
 196                    }
 197                    (node, domain, resource) => format!("{node}@{domain}/{resource}"),
 198                }
 199            }
 200            (Some(at), None) => {
 201                let node = nodeprep(&unnormalized[..at]).map_err(|_| Error::NodePrep)?;
 202                length_check(node.len(), Error::NodeEmpty, Error::NodeTooLong)?;
 203
 204                let domain = nameprep(&unnormalized[at + 1..]).map_err(|_| Error::NamePrep)?;
 205                length_check(domain.len(), Error::DomainEmpty, Error::DomainTooLong)?;
 206
 207                orig_at = Some(node.len());
 208                match (node, domain) {
 209                    (Cow::Borrowed(_), Cow::Borrowed(_)) => unnormalized.to_string(),
 210                    (node, domain) => format!("{node}@{domain}"),
 211                }
 212            }
 213            (None, Some(slash)) => {
 214                let domain = nameprep(&unnormalized[..slash]).map_err(|_| Error::NamePrep)?;
 215                length_check(domain.len(), Error::DomainEmpty, Error::DomainTooLong)?;
 216
 217                let resource =
 218                    resourceprep(&unnormalized[slash + 1..]).map_err(|_| Error::ResourcePrep)?;
 219                length_check(resource.len(), Error::ResourceEmpty, Error::ResourceTooLong)?;
 220
 221                orig_slash = Some(domain.len());
 222                match (domain, resource) {
 223                    (Cow::Borrowed(_), Cow::Borrowed(_)) => unnormalized.to_string(),
 224                    (domain, resource) => format!("{domain}/{resource}"),
 225                }
 226            }
 227            (None, None) => {
 228                let domain = nameprep(unnormalized).map_err(|_| Error::NamePrep)?;
 229                length_check(domain.len(), Error::DomainEmpty, Error::DomainTooLong)?;
 230
 231                domain.into_owned()
 232            }
 233        };
 234
 235        Ok(Self {
 236            normalized,
 237            at: orig_at.and_then(|x| NonZeroU16::new(x as u16)),
 238            slash: orig_slash.and_then(|x| NonZeroU16::new(x as u16)),
 239        })
 240    }
 241
 242    /// Returns the inner String of this JID.
 243    pub fn into_inner(self) -> String {
 244        self.normalized
 245    }
 246
 247    /// Build a [`Jid`] from typed parts. This method cannot fail because it uses parts that have
 248    /// already been parsed and stringprepped into [`NodePart`], [`DomainPart`], and [`ResourcePart`].
 249    ///
 250    /// This method allocates and does not consume the typed parts. To avoid
 251    /// allocation if both `node` and `resource` are known to be `None` and
 252    /// `domain` is owned, you can use `domain.into()`.
 253    pub fn from_parts(
 254        node: Option<&NodeRef>,
 255        domain: &DomainRef,
 256        resource: Option<&ResourceRef>,
 257    ) -> Self {
 258        match resource {
 259            Some(resource) => FullJid::from_parts(node, domain, resource).into(),
 260            None => BareJid::from_parts(node, domain).into(),
 261        }
 262    }
 263
 264    /// The optional node part of the JID as reference.
 265    pub fn node(&self) -> Option<&NodeRef> {
 266        self.at.map(|at| {
 267            let at = u16::from(at) as usize;
 268            NodeRef::from_str_unchecked(&self.normalized[..at])
 269        })
 270    }
 271
 272    /// The domain part of the JID as reference
 273    pub fn domain(&self) -> &DomainRef {
 274        match (self.at, self.slash) {
 275            (Some(at), Some(slash)) => {
 276                let at = u16::from(at) as usize;
 277                let slash = u16::from(slash) as usize;
 278                DomainRef::from_str_unchecked(&self.normalized[at + 1..slash])
 279            }
 280            (Some(at), None) => {
 281                let at = u16::from(at) as usize;
 282                DomainRef::from_str_unchecked(&self.normalized[at + 1..])
 283            }
 284            (None, Some(slash)) => {
 285                let slash = u16::from(slash) as usize;
 286                DomainRef::from_str_unchecked(&self.normalized[..slash])
 287            }
 288            (None, None) => DomainRef::from_str_unchecked(&self.normalized),
 289        }
 290    }
 291
 292    /// The optional resource of the Jabber ID. It is guaranteed to be present when the JID is
 293    /// a Full variant, which you can check with [`Jid::is_full`].
 294    pub fn resource(&self) -> Option<&ResourceRef> {
 295        self.slash.map(|slash| {
 296            let slash = u16::from(slash) as usize;
 297            ResourceRef::from_str_unchecked(&self.normalized[slash + 1..])
 298        })
 299    }
 300
 301    /// Allocate a new [`BareJid`] from this JID, discarding the resource.
 302    pub fn to_bare(&self) -> BareJid {
 303        BareJid::from_parts(self.node(), self.domain())
 304    }
 305
 306    /// Transforms this JID into a [`BareJid`], throwing away the resource.
 307    ///
 308    /// ```
 309    /// # use jid::{BareJid, Jid};
 310    /// let jid: Jid = "foo@bar/baz".parse().unwrap();
 311    /// let bare = jid.into_bare();
 312    /// assert_eq!(bare.to_string(), "foo@bar");
 313    /// ```
 314    pub fn into_bare(mut self) -> BareJid {
 315        if let Some(slash) = self.slash {
 316            // truncate the string
 317            self.normalized.truncate(slash.get() as usize);
 318            self.slash = None;
 319        }
 320        BareJid { inner: self }
 321    }
 322
 323    /// Checks if the JID is a full JID.
 324    pub fn is_full(&self) -> bool {
 325        self.slash.is_some()
 326    }
 327
 328    /// Checks if the JID is a bare JID.
 329    pub fn is_bare(&self) -> bool {
 330        self.slash.is_none()
 331    }
 332
 333    /// Return a reference to the canonical string representation of the JID.
 334    pub fn as_str(&self) -> &str {
 335        &self.normalized
 336    }
 337
 338    /// Try to convert this Jid to a [`FullJid`] if it contains a resource
 339    /// and return a [`BareJid`] otherwise.
 340    ///
 341    /// This is useful for match blocks:
 342    ///
 343    /// ```
 344    /// # use jid::Jid;
 345    /// let jid: Jid = "foo@bar".parse().unwrap();
 346    /// match jid.try_into_full() {
 347    ///     Ok(full) => println!("it is full: {:?}", full),
 348    ///     Err(bare) => println!("it is bare: {:?}", bare),
 349    /// }
 350    /// ```
 351    pub fn try_into_full(self) -> Result<FullJid, BareJid> {
 352        if self.slash.is_some() {
 353            Ok(FullJid { inner: self })
 354        } else {
 355            Err(BareJid { inner: self })
 356        }
 357    }
 358
 359    /// Try to convert this Jid reference to a [`&FullJid`][`FullJid`] if it
 360    /// contains a resource and return a [`&BareJid`][`BareJid`] otherwise.
 361    ///
 362    /// This is useful for match blocks:
 363    ///
 364    /// ```
 365    /// # use jid::Jid;
 366    /// let jid: Jid = "foo@bar".parse().unwrap();
 367    /// match jid.try_as_full() {
 368    ///     Ok(full) => println!("it is full: {:?}", full),
 369    ///     Err(bare) => println!("it is bare: {:?}", bare),
 370    /// }
 371    /// ```
 372    pub fn try_as_full(&self) -> Result<&FullJid, &BareJid> {
 373        if self.slash.is_some() {
 374            Ok(unsafe {
 375                // SAFETY: FullJid is #[repr(transparent)] of Jid
 376                // SOUNDNESS: we asserted that self.slash is set above
 377                mem::transmute::<&Jid, &FullJid>(self)
 378            })
 379        } else {
 380            Err(unsafe {
 381                // SAFETY: BareJid is #[repr(transparent)] of Jid
 382                // SOUNDNESS: we asserted that self.slash is unset above
 383                mem::transmute::<&Jid, &BareJid>(self)
 384            })
 385        }
 386    }
 387
 388    /// Try to convert this mutable Jid reference to a
 389    /// [`&mut FullJid`][`FullJid`] if it contains a resource and return a
 390    /// [`&mut BareJid`][`BareJid`] otherwise.
 391    pub fn try_as_full_mut(&mut self) -> Result<&mut FullJid, &mut BareJid> {
 392        if self.slash.is_some() {
 393            Ok(unsafe {
 394                // SAFETY: FullJid is #[repr(transparent)] of Jid
 395                // SOUNDNESS: we asserted that self.slash is set above
 396                mem::transmute::<&mut Jid, &mut FullJid>(self)
 397            })
 398        } else {
 399            Err(unsafe {
 400                // SAFETY: BareJid is #[repr(transparent)] of Jid
 401                // SOUNDNESS: we asserted that self.slash is unset above
 402                mem::transmute::<&mut Jid, &mut BareJid>(self)
 403            })
 404        }
 405    }
 406
 407    #[doc(hidden)]
 408    #[allow(non_snake_case)]
 409    #[deprecated(
 410        since = "0.11.0",
 411        note = "use Jid::from (for construction of Jid values) or Jid::try_into_full/Jid::try_as_full (for match blocks) instead"
 412    )]
 413    pub fn Bare(other: BareJid) -> Self {
 414        Self::from(other)
 415    }
 416
 417    #[doc(hidden)]
 418    #[allow(non_snake_case)]
 419    #[deprecated(
 420        since = "0.11.0",
 421        note = "use Jid::from (for construction of Jid values) or Jid::try_into_full/Jid::try_as_full (for match blocks) instead"
 422    )]
 423    pub fn Full(other: FullJid) -> Self {
 424        Self::from(other)
 425    }
 426}
 427
 428impl TryFrom<Jid> for FullJid {
 429    type Error = Error;
 430
 431    fn try_from(inner: Jid) -> Result<Self, Self::Error> {
 432        if inner.slash.is_none() {
 433            return Err(Error::ResourceMissingInFullJid);
 434        }
 435        Ok(Self { inner })
 436    }
 437}
 438
 439impl TryFrom<Jid> for BareJid {
 440    type Error = Error;
 441
 442    fn try_from(inner: Jid) -> Result<Self, Self::Error> {
 443        if inner.slash.is_some() {
 444            return Err(Error::ResourceInBareJid);
 445        }
 446        Ok(Self { inner })
 447    }
 448}
 449
 450impl PartialEq<Jid> for FullJid {
 451    fn eq(&self, other: &Jid) -> bool {
 452        &self.inner == other
 453    }
 454}
 455
 456impl PartialEq<Jid> for BareJid {
 457    fn eq(&self, other: &Jid) -> bool {
 458        &self.inner == other
 459    }
 460}
 461
 462impl PartialEq<FullJid> for Jid {
 463    fn eq(&self, other: &FullJid) -> bool {
 464        self == &other.inner
 465    }
 466}
 467
 468impl PartialEq<BareJid> for Jid {
 469    fn eq(&self, other: &BareJid) -> bool {
 470        self == &other.inner
 471    }
 472}
 473
 474/// A struct representing a full Jabber ID, with a resource part.
 475///
 476/// A full JID is composed of 3 components, of which only the node is optional:
 477///
 478/// - the (optional) node part is the part before the (optional) `@`.
 479/// - the domain part is the mandatory part between the (optional) `@` and before the `/`.
 480/// - the resource part after the `/`.
 481///
 482/// Unlike a [`BareJid`], it always contains a resource, and should only be used when you are
 483/// certain there is no case where a resource can be missing.  Otherwise, use a [`Jid`] or
 484/// [`BareJid`].
 485#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 486#[repr(transparent)] // WARNING: Jid::try_as_* relies on this for safety!
 487pub struct FullJid {
 488    inner: Jid,
 489}
 490
 491/// A struct representing a bare Jabber ID, without a resource part.
 492///
 493/// A bare JID is composed of 2 components, of which only the node is optional:
 494/// - the (optional) node part is the part before the (optional) `@`.
 495/// - the domain part is the mandatory part between the (optional) `@` and before the `/`.
 496///
 497/// Unlike a [`FullJid`], it can’t contain a resource, and should only be used when you are certain
 498/// there is no case where a resource can be set.  Otherwise, use a [`Jid`] or [`FullJid`].
 499#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 500#[repr(transparent)] // WARNING: Jid::try_as_* relies on this for safety!
 501pub struct BareJid {
 502    inner: Jid,
 503}
 504
 505impl Deref for FullJid {
 506    type Target = Jid;
 507
 508    fn deref(&self) -> &Self::Target {
 509        &self.inner
 510    }
 511}
 512
 513impl Deref for BareJid {
 514    type Target = Jid;
 515
 516    fn deref(&self) -> &Self::Target {
 517        &self.inner
 518    }
 519}
 520
 521impl Borrow<Jid> for FullJid {
 522    fn borrow(&self) -> &Jid {
 523        &self.inner
 524    }
 525}
 526
 527impl Borrow<Jid> for BareJid {
 528    fn borrow(&self) -> &Jid {
 529        &self.inner
 530    }
 531}
 532
 533impl fmt::Debug for FullJid {
 534    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
 535        fmt.debug_tuple("FullJid").field(&self.inner).finish()
 536    }
 537}
 538
 539impl fmt::Debug for BareJid {
 540    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
 541        fmt.debug_tuple("BareJid").field(&self.inner).finish()
 542    }
 543}
 544
 545impl fmt::Display for FullJid {
 546    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
 547        fmt::Display::fmt(&self.inner, fmt)
 548    }
 549}
 550
 551impl fmt::Display for BareJid {
 552    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
 553        fmt::Display::fmt(&self.inner, fmt)
 554    }
 555}
 556
 557#[cfg(feature = "serde")]
 558impl Serialize for Jid {
 559    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 560    where
 561        S: Serializer,
 562    {
 563        serializer.serialize_str(&self.normalized)
 564    }
 565}
 566
 567#[cfg(feature = "serde")]
 568impl Serialize for FullJid {
 569    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 570    where
 571        S: Serializer,
 572    {
 573        self.inner.serialize(serializer)
 574    }
 575}
 576
 577#[cfg(feature = "serde")]
 578impl Serialize for BareJid {
 579    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 580    where
 581        S: Serializer,
 582    {
 583        self.inner.serialize(serializer)
 584    }
 585}
 586
 587impl FromStr for FullJid {
 588    type Err = Error;
 589
 590    fn from_str(s: &str) -> Result<Self, Self::Err> {
 591        Self::new(s)
 592    }
 593}
 594
 595#[cfg(feature = "serde")]
 596impl<'de> Deserialize<'de> for Jid {
 597    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
 598    where
 599        D: Deserializer<'de>,
 600    {
 601        let s = String::deserialize(deserializer)?;
 602        Jid::new(&s).map_err(de::Error::custom)
 603    }
 604}
 605
 606#[cfg(feature = "serde")]
 607impl<'de> Deserialize<'de> for FullJid {
 608    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
 609    where
 610        D: Deserializer<'de>,
 611    {
 612        let jid = Jid::deserialize(deserializer)?;
 613        jid.try_into().map_err(de::Error::custom)
 614    }
 615}
 616
 617#[cfg(feature = "serde")]
 618impl<'de> Deserialize<'de> for BareJid {
 619    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
 620    where
 621        D: Deserializer<'de>,
 622    {
 623        let jid = Jid::deserialize(deserializer)?;
 624        jid.try_into().map_err(de::Error::custom)
 625    }
 626}
 627
 628#[cfg(feature = "quote")]
 629impl ToTokens for Jid {
 630    fn to_tokens(&self, tokens: &mut TokenStream) {
 631        let s = &self.normalized;
 632        tokens.extend(quote! {
 633            ::jid::Jid::new(#s).unwrap()
 634        });
 635    }
 636}
 637
 638#[cfg(feature = "quote")]
 639impl ToTokens for FullJid {
 640    fn to_tokens(&self, tokens: &mut TokenStream) {
 641        let s = &self.inner.normalized;
 642        tokens.extend(quote! {
 643            ::jid::FullJid::new(#s).unwrap()
 644        });
 645    }
 646}
 647
 648#[cfg(feature = "quote")]
 649impl ToTokens for BareJid {
 650    fn to_tokens(&self, tokens: &mut TokenStream) {
 651        let s = &self.inner.normalized;
 652        tokens.extend(quote! {
 653            ::jid::BareJid::new(#s).unwrap()
 654        });
 655    }
 656}
 657
 658impl FullJid {
 659    /// Constructs a full Jabber ID containing all three components. This is of the form
 660    /// `node@domain/resource`, where node part is optional.
 661    /// If you want a non-fallible version, use [`FullJid::from_parts`] instead.
 662    ///
 663    /// # Examples
 664    ///
 665    /// ```
 666    /// use jid::FullJid;
 667    /// # use jid::Error;
 668    ///
 669    /// # fn main() -> Result<(), Error> {
 670    /// let jid = FullJid::new("node@domain/resource")?;
 671    ///
 672    /// assert_eq!(jid.node().map(|x| x.as_str()), Some("node"));
 673    /// assert_eq!(jid.domain().as_str(), "domain");
 674    /// assert_eq!(jid.resource().as_str(), "resource");
 675    /// # Ok(())
 676    /// # }
 677    /// ```
 678    pub fn new(unnormalized: &str) -> Result<Self, Error> {
 679        Jid::new(unnormalized)?.try_into()
 680    }
 681
 682    /// Build a [`FullJid`] from typed parts. This method cannot fail because it uses parts that have
 683    /// already been parsed and stringprepped into [`NodePart`], [`DomainPart`], and [`ResourcePart`].
 684    /// This method allocates and does not consume the typed parts.
 685    pub fn from_parts(
 686        node: Option<&NodeRef>,
 687        domain: &DomainRef,
 688        resource: &ResourceRef,
 689    ) -> FullJid {
 690        let (at, slash, normalized) = if let Some(node) = node {
 691            // Parts are never empty so len > 0 for NonZeroU16::new is always Some
 692            (
 693                NonZeroU16::new(node.len() as u16),
 694                NonZeroU16::new((node.len() + 1 + domain.len()) as u16),
 695                format!(
 696                    "{}@{}/{}",
 697                    node.as_str(),
 698                    domain.as_str(),
 699                    resource.as_str()
 700                ),
 701            )
 702        } else {
 703            (
 704                None,
 705                NonZeroU16::new(domain.len() as u16),
 706                format!("{}/{}", domain.as_str(), resource.as_str()),
 707            )
 708        };
 709
 710        let inner = Jid {
 711            normalized,
 712            at,
 713            slash,
 714        };
 715
 716        Self { inner }
 717    }
 718
 719    /// The optional resource of the Jabber ID.  Since this is a full JID it is always present.
 720    pub fn resource(&self) -> &ResourceRef {
 721        self.inner.resource().unwrap()
 722    }
 723}
 724
 725impl FromStr for BareJid {
 726    type Err = Error;
 727
 728    fn from_str(s: &str) -> Result<Self, Self::Err> {
 729        Self::new(s)
 730    }
 731}
 732
 733impl BareJid {
 734    /// Constructs a bare Jabber ID, containing two components. This is of the form
 735    /// `node`@`domain`, where node part is optional.
 736    /// If you want a non-fallible version, use [`BareJid::from_parts`] instead.
 737    ///
 738    /// # Examples
 739    ///
 740    /// ```
 741    /// use jid::BareJid;
 742    /// # use jid::Error;
 743    ///
 744    /// # fn main() -> Result<(), Error> {
 745    /// let jid = BareJid::new("node@domain")?;
 746    ///
 747    /// assert_eq!(jid.node().map(|x| x.as_str()), Some("node"));
 748    /// assert_eq!(jid.domain().as_str(), "domain");
 749    /// # Ok(())
 750    /// # }
 751    /// ```
 752    pub fn new(unnormalized: &str) -> Result<Self, Error> {
 753        Jid::new(unnormalized)?.try_into()
 754    }
 755
 756    /// Build a [`BareJid`] from typed parts. This method cannot fail because it uses parts that have
 757    /// already been parsed and stringprepped into [`NodePart`] and [`DomainPart`].
 758    ///
 759    /// This method allocates and does not consume the typed parts. To avoid
 760    /// allocation if `node` is known to be `None` and `domain` is owned, you
 761    /// can use `domain.into()`.
 762    pub fn from_parts(node: Option<&NodeRef>, domain: &DomainRef) -> Self {
 763        let (at, normalized) = if let Some(node) = node {
 764            // Parts are never empty so len > 0 for NonZeroU16::new is always Some
 765            (
 766                NonZeroU16::new(node.len() as u16),
 767                format!("{}@{}", node.as_str(), domain.as_str()),
 768            )
 769        } else {
 770            (None, domain.to_string())
 771        };
 772
 773        let inner = Jid {
 774            normalized,
 775            at,
 776            slash: None,
 777        };
 778
 779        Self { inner }
 780    }
 781
 782    /// Constructs a [`BareJid`] from the bare JID, by specifying a [`ResourcePart`].
 783    /// If you'd like to specify a stringy resource, use [`BareJid::with_resource_str`] instead.
 784    ///
 785    /// # Examples
 786    ///
 787    /// ```
 788    /// use jid::{BareJid, ResourcePart};
 789    ///
 790    /// let resource = ResourcePart::new("resource").unwrap();
 791    /// let bare = BareJid::new("node@domain").unwrap();
 792    /// let full = bare.with_resource(&resource);
 793    ///
 794    /// assert_eq!(full.node().map(|x| x.as_str()), Some("node"));
 795    /// assert_eq!(full.domain().as_str(), "domain");
 796    /// assert_eq!(full.resource().as_str(), "resource");
 797    /// ```
 798    pub fn with_resource(&self, resource: &ResourceRef) -> FullJid {
 799        let slash = NonZeroU16::new(self.inner.normalized.len() as u16);
 800        let normalized = format!("{}/{resource}", self.inner.normalized);
 801        let inner = Jid {
 802            normalized,
 803            at: self.inner.at,
 804            slash,
 805        };
 806
 807        FullJid { inner }
 808    }
 809
 810    /// Constructs a [`FullJid`] from the bare JID, by specifying a stringy `resource`.
 811    /// If your resource has already been parsed into a [`ResourcePart`], use [`BareJid::with_resource`].
 812    ///
 813    /// # Examples
 814    ///
 815    /// ```
 816    /// use jid::BareJid;
 817    ///
 818    /// let bare = BareJid::new("node@domain").unwrap();
 819    /// let full = bare.with_resource_str("resource").unwrap();
 820    ///
 821    /// assert_eq!(full.node().map(|x| x.as_str()), Some("node"));
 822    /// assert_eq!(full.domain().as_str(), "domain");
 823    /// assert_eq!(full.resource().as_str(), "resource");
 824    /// ```
 825    pub fn with_resource_str(&self, resource: &str) -> Result<FullJid, Error> {
 826        let resource = ResourcePart::new(resource)?;
 827        Ok(self.with_resource(&resource))
 828    }
 829}
 830
 831#[cfg(feature = "minidom")]
 832impl IntoAttributeValue for Jid {
 833    fn into_attribute_value(self) -> Option<String> {
 834        Some(self.to_string())
 835    }
 836}
 837
 838#[cfg(feature = "minidom")]
 839impl From<Jid> for Node {
 840    fn from(jid: Jid) -> Self {
 841        Node::Text(jid.to_string())
 842    }
 843}
 844
 845#[cfg(feature = "minidom")]
 846impl IntoAttributeValue for FullJid {
 847    fn into_attribute_value(self) -> Option<String> {
 848        self.inner.into_attribute_value()
 849    }
 850}
 851
 852#[cfg(feature = "minidom")]
 853impl From<FullJid> for Node {
 854    fn from(jid: FullJid) -> Self {
 855        jid.inner.into()
 856    }
 857}
 858
 859#[cfg(feature = "minidom")]
 860impl IntoAttributeValue for BareJid {
 861    fn into_attribute_value(self) -> Option<String> {
 862        self.inner.into_attribute_value()
 863    }
 864}
 865
 866#[cfg(feature = "minidom")]
 867impl From<BareJid> for Node {
 868    fn from(other: BareJid) -> Self {
 869        other.inner.into()
 870    }
 871}
 872
 873#[cfg(test)]
 874mod tests {
 875    use super::*;
 876
 877    use std::collections::{HashMap, HashSet};
 878    use std::vec::Vec;
 879
 880    macro_rules! assert_size (
 881        ($t:ty, $sz:expr) => (
 882            assert_eq!(::core::mem::size_of::<$t>(), $sz);
 883        );
 884    );
 885
 886    #[cfg(target_pointer_width = "32")]
 887    #[test]
 888    fn test_size() {
 889        assert_size!(BareJid, 16);
 890        assert_size!(FullJid, 16);
 891        assert_size!(Jid, 16);
 892    }
 893
 894    #[cfg(target_pointer_width = "64")]
 895    #[test]
 896    fn test_size() {
 897        assert_size!(BareJid, 32);
 898        assert_size!(FullJid, 32);
 899        assert_size!(Jid, 32);
 900    }
 901
 902    #[test]
 903    fn can_parse_full_jids() {
 904        assert_eq!(
 905            FullJid::from_str("a@b.c/d"),
 906            Ok(FullJid::new("a@b.c/d").unwrap())
 907        );
 908        assert_eq!(
 909            FullJid::from_str("b.c/d"),
 910            Ok(FullJid::new("b.c/d").unwrap())
 911        );
 912
 913        assert_eq!(
 914            FullJid::from_str("a@b.c"),
 915            Err(Error::ResourceMissingInFullJid)
 916        );
 917        assert_eq!(
 918            FullJid::from_str("b.c"),
 919            Err(Error::ResourceMissingInFullJid)
 920        );
 921    }
 922
 923    #[test]
 924    fn can_parse_bare_jids() {
 925        assert_eq!(
 926            BareJid::from_str("a@b.c"),
 927            Ok(BareJid::new("a@b.c").unwrap())
 928        );
 929        assert_eq!(BareJid::from_str("b.c"), Ok(BareJid::new("b.c").unwrap()));
 930    }
 931
 932    #[test]
 933    fn can_parse_jids() {
 934        let full = FullJid::from_str("a@b.c/d").unwrap();
 935        let bare = BareJid::from_str("e@f.g").unwrap();
 936
 937        assert_eq!(Jid::from_str("a@b.c/d").unwrap(), full);
 938        assert_eq!(Jid::from_str("e@f.g").unwrap(), bare);
 939    }
 940
 941    #[test]
 942    fn full_to_bare_jid() {
 943        let bare: BareJid = FullJid::new("a@b.c/d").unwrap().to_bare();
 944        assert_eq!(bare, BareJid::new("a@b.c").unwrap());
 945    }
 946
 947    #[test]
 948    fn bare_to_full_jid_str() {
 949        assert_eq!(
 950            BareJid::new("a@b.c")
 951                .unwrap()
 952                .with_resource_str("d")
 953                .unwrap(),
 954            FullJid::new("a@b.c/d").unwrap()
 955        );
 956    }
 957
 958    #[test]
 959    fn bare_to_full_jid() {
 960        assert_eq!(
 961            BareJid::new("a@b.c")
 962                .unwrap()
 963                .with_resource(&ResourcePart::new("d").unwrap()),
 964            FullJid::new("a@b.c/d").unwrap()
 965        )
 966    }
 967
 968    #[test]
 969    fn node_from_jid() {
 970        let jid = Jid::new("a@b.c/d").unwrap();
 971
 972        assert_eq!(jid.node().map(|x| x.as_str()), Some("a"),);
 973    }
 974
 975    #[test]
 976    fn domain_from_jid() {
 977        let jid = Jid::new("a@b.c").unwrap();
 978
 979        assert_eq!(jid.domain().as_str(), "b.c");
 980    }
 981
 982    #[test]
 983    fn resource_from_jid() {
 984        let jid = Jid::new("a@b.c/d").unwrap();
 985
 986        assert_eq!(jid.resource().map(|x| x.as_str()), Some("d"),);
 987    }
 988
 989    #[test]
 990    fn jid_to_full_bare() {
 991        let full = FullJid::new("a@b.c/d").unwrap();
 992        let bare = BareJid::new("a@b.c").unwrap();
 993
 994        assert_eq!(FullJid::try_from(Jid::from(full.clone())), Ok(full.clone()));
 995        assert_eq!(
 996            FullJid::try_from(Jid::from(bare.clone())),
 997            Err(Error::ResourceMissingInFullJid),
 998        );
 999        assert_eq!(Jid::from(full.clone().to_bare()), bare.clone());
1000        assert_eq!(Jid::from(bare.clone()), bare);
1001    }
1002
1003    #[test]
1004    fn serialise() {
1005        assert_eq!(FullJid::new("a@b/c").unwrap().to_string(), "a@b/c");
1006        assert_eq!(BareJid::new("a@b").unwrap().to_string(), "a@b");
1007    }
1008
1009    #[test]
1010    fn hash() {
1011        let _map: HashMap<Jid, String> = HashMap::new();
1012    }
1013
1014    #[test]
1015    fn invalid_jids() {
1016        assert_eq!(BareJid::from_str(""), Err(Error::DomainEmpty));
1017        assert_eq!(BareJid::from_str("/c"), Err(Error::DomainEmpty));
1018        assert_eq!(BareJid::from_str("a@/c"), Err(Error::DomainEmpty));
1019        assert_eq!(BareJid::from_str("@b"), Err(Error::NodeEmpty));
1020        assert_eq!(BareJid::from_str("b/"), Err(Error::ResourceEmpty));
1021
1022        assert_eq!(FullJid::from_str(""), Err(Error::DomainEmpty));
1023        assert_eq!(FullJid::from_str("/c"), Err(Error::DomainEmpty));
1024        assert_eq!(FullJid::from_str("a@/c"), Err(Error::DomainEmpty));
1025        assert_eq!(FullJid::from_str("@b"), Err(Error::NodeEmpty));
1026        assert_eq!(FullJid::from_str("b/"), Err(Error::ResourceEmpty));
1027        assert_eq!(
1028            FullJid::from_str("a@b"),
1029            Err(Error::ResourceMissingInFullJid)
1030        );
1031        assert_eq!(BareJid::from_str("a@b/c"), Err(Error::ResourceInBareJid));
1032    }
1033
1034    #[test]
1035    fn display_jids() {
1036        assert_eq!(FullJid::new("a@b/c").unwrap().to_string(), "a@b/c");
1037        assert_eq!(BareJid::new("a@b").unwrap().to_string(), "a@b");
1038        assert_eq!(
1039            Jid::from(FullJid::new("a@b/c").unwrap()).to_string(),
1040            "a@b/c"
1041        );
1042        assert_eq!(Jid::from(BareJid::new("a@b").unwrap()).to_string(), "a@b");
1043    }
1044
1045    #[cfg(feature = "minidom")]
1046    #[test]
1047    fn minidom() {
1048        let elem: minidom::Element = "<message xmlns='ns1' from='a@b/c'/>".parse().unwrap();
1049        let to: Jid = elem.attr("from").unwrap().parse().unwrap();
1050        assert_eq!(to, Jid::from(FullJid::new("a@b/c").unwrap()));
1051
1052        let elem: minidom::Element = "<message xmlns='ns1' from='a@b'/>".parse().unwrap();
1053        let to: Jid = elem.attr("from").unwrap().parse().unwrap();
1054        assert_eq!(to, Jid::from(BareJid::new("a@b").unwrap()));
1055
1056        let elem: minidom::Element = "<message xmlns='ns1' from='a@b/c'/>".parse().unwrap();
1057        let to: FullJid = elem.attr("from").unwrap().parse().unwrap();
1058        assert_eq!(to, FullJid::new("a@b/c").unwrap());
1059
1060        let elem: minidom::Element = "<message xmlns='ns1' from='a@b'/>".parse().unwrap();
1061        let to: BareJid = elem.attr("from").unwrap().parse().unwrap();
1062        assert_eq!(to, BareJid::new("a@b").unwrap());
1063    }
1064
1065    #[cfg(feature = "minidom")]
1066    #[test]
1067    fn minidom_into_attr() {
1068        let full = FullJid::new("a@b/c").unwrap();
1069        let elem = minidom::Element::builder("message", "jabber:client")
1070            .attr("from", full.clone())
1071            .build();
1072        assert_eq!(elem.attr("from"), Some(full.to_string().as_str()));
1073
1074        let bare = BareJid::new("a@b").unwrap();
1075        let elem = minidom::Element::builder("message", "jabber:client")
1076            .attr("from", bare.clone())
1077            .build();
1078        assert_eq!(elem.attr("from"), Some(bare.to_string().as_str()));
1079
1080        let jid = Jid::from(bare.clone());
1081        let _elem = minidom::Element::builder("message", "jabber:client")
1082            .attr("from", jid)
1083            .build();
1084        assert_eq!(elem.attr("from"), Some(bare.to_string().as_str()));
1085    }
1086
1087    #[test]
1088    fn stringprep() {
1089        let full = FullJid::from_str("Test@☃.coM/Test™").unwrap();
1090        let equiv = FullJid::new("test@☃.com/TestTM").unwrap();
1091        assert_eq!(full, equiv);
1092    }
1093
1094    #[test]
1095    fn invalid_stringprep() {
1096        FullJid::from_str("a@b/🎉").unwrap_err();
1097    }
1098
1099    #[test]
1100    fn jid_from_parts() {
1101        let node = NodePart::new("node").unwrap();
1102        let domain = DomainPart::new("domain").unwrap();
1103        let resource = ResourcePart::new("resource").unwrap();
1104
1105        let jid = Jid::from_parts(Some(&node), &domain, Some(&resource));
1106        assert_eq!(jid, Jid::new("node@domain/resource").unwrap());
1107
1108        let barejid = BareJid::from_parts(Some(&node), &domain);
1109        assert_eq!(barejid, BareJid::new("node@domain").unwrap());
1110
1111        let fulljid = FullJid::from_parts(Some(&node), &domain, &resource);
1112        assert_eq!(fulljid, FullJid::new("node@domain/resource").unwrap());
1113    }
1114
1115    #[test]
1116    #[cfg(feature = "serde")]
1117    fn jid_ser_de() {
1118        let jid: Jid = Jid::new("node@domain").unwrap();
1119        serde_test::assert_tokens(&jid, &[serde_test::Token::Str("node@domain")]);
1120
1121        let jid: Jid = Jid::new("node@domain/resource").unwrap();
1122        serde_test::assert_tokens(&jid, &[serde_test::Token::Str("node@domain/resource")]);
1123
1124        let jid: BareJid = BareJid::new("node@domain").unwrap();
1125        serde_test::assert_tokens(&jid, &[serde_test::Token::Str("node@domain")]);
1126
1127        let jid: FullJid = FullJid::new("node@domain/resource").unwrap();
1128        serde_test::assert_tokens(&jid, &[serde_test::Token::Str("node@domain/resource")]);
1129    }
1130
1131    #[test]
1132    fn jid_into_parts_and_from_parts() {
1133        let node = NodePart::new("node").unwrap();
1134        let domain = DomainPart::new("domain").unwrap();
1135
1136        let jid1 = domain.with_node(&node);
1137        let jid2 = node.with_domain(&domain);
1138        let jid3 = BareJid::new("node@domain").unwrap();
1139        assert_eq!(jid1, jid2);
1140        assert_eq!(jid2, jid3);
1141    }
1142
1143    #[test]
1144    fn jid_match_replacement_try_as() {
1145        let jid1 = Jid::new("foo@bar").unwrap();
1146        let jid2 = Jid::new("foo@bar/baz").unwrap();
1147
1148        match jid1.try_as_full() {
1149            Err(_) => (),
1150            other => panic!("unexpected result: {:?}", other),
1151        };
1152
1153        match jid2.try_as_full() {
1154            Ok(_) => (),
1155            other => panic!("unexpected result: {:?}", other),
1156        };
1157    }
1158
1159    #[test]
1160    fn jid_match_replacement_try_as_mut() {
1161        let mut jid1 = Jid::new("foo@bar").unwrap();
1162        let mut jid2 = Jid::new("foo@bar/baz").unwrap();
1163
1164        match jid1.try_as_full_mut() {
1165            Err(_) => (),
1166            other => panic!("unexpected result: {:?}", other),
1167        };
1168
1169        match jid2.try_as_full_mut() {
1170            Ok(_) => (),
1171            other => panic!("unexpected result: {:?}", other),
1172        };
1173    }
1174
1175    #[test]
1176    fn jid_match_replacement_try_into() {
1177        let jid1 = Jid::new("foo@bar").unwrap();
1178        let jid2 = Jid::new("foo@bar/baz").unwrap();
1179
1180        match jid1.try_as_full() {
1181            Err(_) => (),
1182            other => panic!("unexpected result: {:?}", other),
1183        };
1184
1185        match jid2.try_as_full() {
1186            Ok(_) => (),
1187            other => panic!("unexpected result: {:?}", other),
1188        };
1189    }
1190
1191    #[test]
1192    fn lookup_jid_by_full_jid() {
1193        let mut map: HashSet<Jid> = HashSet::new();
1194        let jid1 = Jid::new("foo@bar").unwrap();
1195        let jid2 = Jid::new("foo@bar/baz").unwrap();
1196        let jid3 = FullJid::new("foo@bar/baz").unwrap();
1197
1198        map.insert(jid1);
1199        assert!(!map.contains(&jid2));
1200        assert!(!map.contains(&jid3));
1201        map.insert(jid2);
1202        assert!(map.contains(&jid3));
1203    }
1204
1205    #[test]
1206    fn lookup_full_jid_by_jid() {
1207        let mut map: HashSet<FullJid> = HashSet::new();
1208        let jid1 = FullJid::new("foo@bar/baz").unwrap();
1209        let jid2 = FullJid::new("foo@bar/fnord").unwrap();
1210        let jid3 = Jid::new("foo@bar/fnord").unwrap();
1211
1212        map.insert(jid1);
1213        assert!(!map.contains(&jid2));
1214        assert!(!map.contains(&jid3));
1215        map.insert(jid2);
1216        assert!(map.contains(&jid3));
1217    }
1218
1219    #[test]
1220    fn lookup_bare_jid_by_jid() {
1221        let mut map: HashSet<BareJid> = HashSet::new();
1222        let jid1 = BareJid::new("foo@bar").unwrap();
1223        let jid2 = BareJid::new("foo@baz").unwrap();
1224        let jid3 = Jid::new("foo@baz").unwrap();
1225
1226        map.insert(jid1);
1227        assert!(!map.contains(&jid2));
1228        assert!(!map.contains(&jid3));
1229        map.insert(jid2);
1230        assert!(map.contains(&jid3));
1231    }
1232
1233    #[test]
1234    fn normalizes_all_parts() {
1235        assert_eq!(
1236            Jid::new("ßA@IX.test/\u{2168}").unwrap().as_str(),
1237            "ssa@ix.test/IX"
1238        );
1239    }
1240
1241    #[test]
1242    fn rejects_unassigned_codepoints() {
1243        match Jid::new("\u{01f601}@example.com") {
1244            Err(Error::NodePrep) => (),
1245            other => panic!("unexpected result: {:?}", other),
1246        };
1247
1248        match Jid::new("foo@\u{01f601}.example.com") {
1249            Err(Error::NamePrep) => (),
1250            other => panic!("unexpected result: {:?}", other),
1251        };
1252
1253        match Jid::new("foo@example.com/\u{01f601}") {
1254            Err(Error::ResourcePrep) => (),
1255            other => panic!("unexpected result: {:?}", other),
1256        };
1257    }
1258
1259    #[test]
1260    fn accepts_domain_only_jid() {
1261        match Jid::new("example.com") {
1262            Ok(_) => (),
1263            other => panic!("unexpected result: {:?}", other),
1264        };
1265
1266        match BareJid::new("example.com") {
1267            Ok(_) => (),
1268            other => panic!("unexpected result: {:?}", other),
1269        };
1270
1271        match FullJid::new("example.com/x") {
1272            Ok(_) => (),
1273            other => panic!("unexpected result: {:?}", other),
1274        };
1275    }
1276
1277    #[test]
1278    fn is_bare_returns_true_iff_bare() {
1279        let bare = Jid::new("foo@bar").unwrap();
1280        let full = Jid::new("foo@bar/baz").unwrap();
1281
1282        assert!(bare.is_bare());
1283        assert!(!full.is_bare());
1284    }
1285
1286    #[test]
1287    fn is_full_returns_true_iff_full() {
1288        let bare = Jid::new("foo@bar").unwrap();
1289        let full = Jid::new("foo@bar/baz").unwrap();
1290
1291        assert!(!bare.is_full());
1292        assert!(full.is_full());
1293    }
1294
1295    #[test]
1296    fn reject_long_localpart() {
1297        let mut long = Vec::with_capacity(1028);
1298        long.resize(1024, b'a');
1299        let mut long = String::from_utf8(long).unwrap();
1300        long.push_str("@foo");
1301
1302        match Jid::new(&long) {
1303            Err(Error::NodeTooLong) => (),
1304            other => panic!("unexpected result: {:?}", other),
1305        }
1306
1307        match BareJid::new(&long) {
1308            Err(Error::NodeTooLong) => (),
1309            other => panic!("unexpected result: {:?}", other),
1310        }
1311    }
1312
1313    #[test]
1314    fn reject_long_domainpart() {
1315        let mut long = Vec::with_capacity(1028);
1316        long.push(b'x');
1317        long.push(b'@');
1318        long.resize(1026, b'a');
1319        let long = String::from_utf8(long).unwrap();
1320
1321        match Jid::new(&long) {
1322            Err(Error::DomainTooLong) => (),
1323            other => panic!("unexpected result: {:?}", other),
1324        }
1325
1326        match BareJid::new(&long) {
1327            Err(Error::DomainTooLong) => (),
1328            other => panic!("unexpected result: {:?}", other),
1329        }
1330    }
1331
1332    #[test]
1333    fn reject_long_resourcepart() {
1334        let mut long = Vec::with_capacity(1028);
1335        long.push(b'x');
1336        long.push(b'@');
1337        long.push(b'y');
1338        long.push(b'/');
1339        long.resize(1028, b'a');
1340        let long = String::from_utf8(long).unwrap();
1341
1342        match Jid::new(&long) {
1343            Err(Error::ResourceTooLong) => (),
1344            other => panic!("unexpected result: {:?}", other),
1345        }
1346
1347        match FullJid::new(&long) {
1348            Err(Error::ResourceTooLong) => (),
1349            other => panic!("unexpected result: {:?}", other),
1350        }
1351    }
1352}