diff --git a/jid/CHANGELOG.md b/jid/CHANGELOG.md index 9306df2f..a316a397 100644 --- a/jid/CHANGELOG.md +++ b/jid/CHANGELOG.md @@ -15,6 +15,11 @@ Version xxx, release xxx: - `str`-like reference types have been added for `DomainPart`, `NodePart` and `ResourcePart`, called `DomainRef`, `NodeRef` and `ResourceRef` respectively. + - Convenience methods to combine `DomainPart` and `NodePart` to a + `BareJid` have been added, including + `impl From for BareJid` and + `impl From for Jid`, both of which are (unlike + `::from_parts`) copy-free. Version 0.10.0, release 2023-08-17: * Breaking diff --git a/jid/src/lib.rs b/jid/src/lib.rs index b10c3d64..c268af70 100644 --- a/jid/src/lib.rs +++ b/jid/src/lib.rs @@ -130,7 +130,10 @@ impl Jid { /// Build a [`Jid`] from typed parts. This method cannot fail because it uses parts that have /// already been parsed and stringprepped into [`NodePart`], [`DomainPart`], and [`ResourcePart`]. - /// This method allocates and does not consume the typed parts. + /// + /// This method allocates and does not consume the typed parts. To avoid + /// allocation if both `node` and `resource` are known to be `None` and + /// `domain` is owned, you can use `domain.into()`. pub fn from_parts( node: Option<&NodeRef>, domain: &DomainRef, @@ -523,8 +526,11 @@ impl BareJid { } /// Build a [`BareJid`] from typed parts. This method cannot fail because it uses parts that have - /// already been parsed and stringprepped into [`NodePart`] and [`DomainPart`]. This method allocates - /// and does not consume the typed parts. + /// already been parsed and stringprepped into [`NodePart`] and [`DomainPart`]. + /// + /// 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 { let (at, normalized) = if let Some(node) = node { // Parts are never empty so len > 0 for NonZeroU16::new is always Some @@ -904,4 +910,16 @@ mod tests { let jid: FullJid = FullJid::new("node@domain/resource").unwrap(); serde_test::assert_tokens(&jid, &[serde_test::Token::Str("node@domain/resource")]); } + + #[test] + fn jid_into_parts_and_from_parts() { + let node = NodePart::new("node").unwrap(); + let domain = DomainPart::new("domain").unwrap(); + + let jid1 = domain.with_node(&node); + let jid2 = node.with_domain(&domain); + let jid3 = BareJid::new("node@domain").unwrap(); + assert_eq!(jid1, jid2); + assert_eq!(jid2, jid3); + } } diff --git a/jid/src/parts.rs b/jid/src/parts.rs index 2ed44cd9..2c9a6f34 100644 --- a/jid/src/parts.rs +++ b/jid/src/parts.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use stringprep::{nameprep, nodeprep, resourceprep}; -use crate::Error; +use crate::{BareJid, Error, InnerJid, Jid}; fn length_check(len: usize, error_empty: Error, error_too_long: Error) -> Result<(), Error> { if len == 0 { @@ -239,6 +239,46 @@ def_part_types! { pub struct ref ResourceRef(str); } +impl DomainRef { + /// Construct a bare JID (a JID without a resource) from this domain and + /// the given node (local part). + pub fn with_node(&self, node: &NodeRef) -> BareJid { + BareJid::from_parts(Some(node), self) + } +} + +impl From for BareJid { + fn from(other: DomainPart) -> Self { + BareJid { + inner: InnerJid { + normalized: other.0, + at: None, + slash: None, + }, + } + } +} + +impl From for Jid { + fn from(other: DomainPart) -> Self { + Jid::Bare(other.into()) + } +} + +impl<'x> From<&'x DomainRef> for BareJid { + fn from(other: &'x DomainRef) -> Self { + Self::from_parts(None, other) + } +} + +impl NodeRef { + /// Construct a bare JID (a JID without a resource) from this node (the + /// local part) and the given domain. + pub fn with_domain(&self, domain: &DomainRef) -> BareJid { + BareJid::from_parts(Some(self), domain) + } +} + #[cfg(test)] mod tests { use super::*;