mirror of
https://gitlab.com/xmpp-rs/xmpp-rs.git
synced 2024-07-12 22:21:53 +00:00
Introduce typed Parts for the JID to enable unfallible JID construction
This commit is contained in:
parent
85bdcdb131
commit
199b3ae7ae
2 changed files with 128 additions and 0 deletions
|
@ -45,6 +45,9 @@ pub use crate::error::Error;
|
||||||
mod inner;
|
mod inner;
|
||||||
use inner::InnerJid;
|
use inner::InnerJid;
|
||||||
|
|
||||||
|
mod parts;
|
||||||
|
pub use parts::{DomainPart, NodePart, ResourcePart};
|
||||||
|
|
||||||
/// An enum representing a Jabber ID. It can be either a `FullJid` or a `BareJid`.
|
/// 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", derive(Serialize, Deserialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(untagged))]
|
#[cfg_attr(feature = "serde", serde(untagged))]
|
||||||
|
@ -89,6 +92,7 @@ impl fmt::Display for Jid {
|
||||||
impl Jid {
|
impl Jid {
|
||||||
/// Constructs a Jabber ID from a string. This is of the form
|
/// Constructs a Jabber ID from a string. This is of the form
|
||||||
/// `node`@`domain`/`resource`, where node and resource parts are optional.
|
/// `node`@`domain`/`resource`, where node and resource parts are optional.
|
||||||
|
/// If you want a non-fallible version, use [`Jid::from_parts`] instead.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
@ -114,6 +118,21 @@ 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.
|
||||||
|
pub fn from_parts(
|
||||||
|
node: Option<&NodePart>,
|
||||||
|
domain: &DomainPart,
|
||||||
|
resource: Option<&ResourcePart>,
|
||||||
|
) -> Jid {
|
||||||
|
if let Some(resource) = resource {
|
||||||
|
Jid::Full(FullJid::from_parts(node, domain, resource))
|
||||||
|
} else {
|
||||||
|
Jid::Bare(BareJid::from_parts(node, domain))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The optional node part of the JID.
|
/// The optional node part of the JID.
|
||||||
pub fn node(&self) -> Option<&str> {
|
pub fn node(&self) -> Option<&str> {
|
||||||
match self {
|
match self {
|
||||||
|
@ -305,6 +324,7 @@ impl<'de> Deserialize<'de> for BareJid {
|
||||||
impl FullJid {
|
impl FullJid {
|
||||||
/// Constructs a full Jabber ID containing all three components. This is of the form
|
/// Constructs a full Jabber ID containing all three components. This is of the form
|
||||||
/// `node@domain/resource`, where node part is optional.
|
/// `node@domain/resource`, where node part is optional.
|
||||||
|
/// If you want a non-fallible version, use [`FullJid::from_parts`] instead.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
@ -330,6 +350,38 @@ impl FullJid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build a [`FullJid`] 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.
|
||||||
|
pub fn from_parts(
|
||||||
|
node: Option<&NodePart>,
|
||||||
|
domain: &DomainPart,
|
||||||
|
resource: &ResourcePart,
|
||||||
|
) -> FullJid {
|
||||||
|
let (at, slash, normalized) = if let Some(node) = node {
|
||||||
|
// Parts are never empty so len > 0 for NonZeroU16::new is always Some
|
||||||
|
(
|
||||||
|
NonZeroU16::new(node.0.len() as u16),
|
||||||
|
NonZeroU16::new((node.0.len() + 1 + domain.0.len()) as u16),
|
||||||
|
format!("{}@{}/{}", node.0, domain.0, resource.0),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
NonZeroU16::new(domain.0.len() as u16),
|
||||||
|
format!("{}/{}", domain.0, resource.0),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let inner = InnerJid {
|
||||||
|
normalized,
|
||||||
|
at,
|
||||||
|
slash,
|
||||||
|
};
|
||||||
|
|
||||||
|
FullJid { inner }
|
||||||
|
}
|
||||||
|
|
||||||
/// The optional node part of the JID.
|
/// The optional node part of the JID.
|
||||||
pub fn node(&self) -> Option<&str> {
|
pub fn node(&self) -> Option<&str> {
|
||||||
self.inner.node()
|
self.inner.node()
|
||||||
|
@ -378,6 +430,7 @@ impl FromStr for BareJid {
|
||||||
impl BareJid {
|
impl BareJid {
|
||||||
/// Constructs a bare Jabber ID, containing two components. This is of the form
|
/// Constructs a bare Jabber ID, containing two components. This is of the form
|
||||||
/// `node`@`domain`, where node part is optional.
|
/// `node`@`domain`, where node part is optional.
|
||||||
|
/// If you want a non-fallible version, use [`BareJid::from_parts`] instead.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
@ -402,6 +455,29 @@ 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.
|
||||||
|
pub fn from_parts(node: Option<&NodePart>, domain: &DomainPart) -> BareJid {
|
||||||
|
let (at, normalized) = if let Some(node) = node {
|
||||||
|
// Parts are never empty so len > 0 for NonZeroU16::new is always Some
|
||||||
|
(
|
||||||
|
NonZeroU16::new(node.0.len() as u16),
|
||||||
|
format!("{}@{}", node.0, domain.0),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(None, domain.0.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
let inner = InnerJid {
|
||||||
|
normalized,
|
||||||
|
at,
|
||||||
|
slash: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
BareJid { inner }
|
||||||
|
}
|
||||||
|
|
||||||
/// The optional node part of the JID.
|
/// The optional node part of the JID.
|
||||||
pub fn node(&self) -> Option<&str> {
|
pub fn node(&self) -> Option<&str> {
|
||||||
self.inner.node()
|
self.inner.node()
|
||||||
|
|
52
jid/src/parts.rs
Normal file
52
jid/src/parts.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use stringprep::{nameprep, nodeprep, resourceprep};
|
||||||
|
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
|
/// The [`NodePart`] is the optional part before the (optional) `@` in any [`Jid`], whether [`BareJid`] or [`FullJid`].
|
||||||
|
#[derive(Clone, Debug, PartialEq, Hash, PartialOrd)]
|
||||||
|
pub struct NodePart(pub(crate) String);
|
||||||
|
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodePart {
|
||||||
|
/// Build a new [`NodePart`] from a string slice. Will fail in case of stringprep validation error.
|
||||||
|
pub fn new(s: &str) -> Result<NodePart, Error> {
|
||||||
|
let node = nodeprep(s).map_err(|_| Error::NodePrep)?;
|
||||||
|
length_check(node.len(), Error::NodeEmpty, Error::NodeTooLong)?;
|
||||||
|
Ok(NodePart(node.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [`DomainPart`] is the part between the (optional) `@` and the (optional) `/` in any [`Jid`], whether [`BareJid`] or [`FullJid`].
|
||||||
|
#[derive(Clone, Debug, PartialEq, Hash, PartialOrd)]
|
||||||
|
pub struct DomainPart(pub(crate) String);
|
||||||
|
|
||||||
|
impl DomainPart {
|
||||||
|
/// Build a new [`DomainPart`] from a string slice. Will fail in case of stringprep validation error.
|
||||||
|
pub fn new(s: &str) -> Result<DomainPart, Error> {
|
||||||
|
let domain = nameprep(s).map_err(|_| Error::NamePrep)?;
|
||||||
|
length_check(domain.len(), Error::DomainEmpty, Error::DomainTooLong)?;
|
||||||
|
Ok(DomainPart(domain.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [`ResourcePart`] is the optional part after the `/` in a [`Jid`]. It is mandatory in [`FullJid`].
|
||||||
|
#[derive(Clone, Debug, PartialEq, Hash, PartialOrd)]
|
||||||
|
pub struct ResourcePart(pub(crate) String);
|
||||||
|
|
||||||
|
impl ResourcePart {
|
||||||
|
/// Build a new [`ResourcePart`] from a string slice. Will fail in case of stringprep validation error.
|
||||||
|
pub fn new(s: &str) -> Result<ResourcePart, Error> {
|
||||||
|
let resource = resourceprep(s).map_err(|_| Error::ResourcePrep)?;
|
||||||
|
length_check(resource.len(), Error::ResourceEmpty, Error::ResourceTooLong)?;
|
||||||
|
Ok(ResourcePart(resource.to_string()))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue