mirror of https://gitlab.com/xmpp-rs/xmpp-rs.git
Compare commits
7 Commits
e7fa6460f4
...
65c91439f8
Author | SHA1 | Date |
---|---|---|
Werner Kroneman | 65c91439f8 | |
Werner Kroneman | f54776ca0a | |
Jonas Schäfer | 2c701038cd | |
Jonas Schäfer | 9608b59f60 | |
Jonas Schäfer | 7fce1146e0 | |
Jonas Schäfer | 45f1567ff2 | |
Jonas Schäfer | 8238e81c66 |
|
@ -1,7 +1,26 @@
|
|||
Version xxx, release xxx:
|
||||
* Breaking:
|
||||
- With the addition of `str`-like types for `DomainPart`, `NodePart` and
|
||||
`ResourcePart`, the functions on `Jid`, `BareJid` and `FullJid` which
|
||||
return the respective types have been changed to return references
|
||||
instead. Use `ToOwned::to_owned` to obtain a copy or `.as_str()` to
|
||||
obtain a plain `str` reference.
|
||||
- The `node_str`, `domain_str` and `resource_str` functions returning str
|
||||
references have been removed from the JID types. Use `.as_str()` or
|
||||
`.map(|x| x.as_str())` on the corresponding `node`/`domain`/`resource`
|
||||
functions instead.
|
||||
* Additions:
|
||||
- Add optional quote support. Implement quote::ToTokens for Jid, FullJid
|
||||
and BareJid.
|
||||
- `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<DomainPart> for BareJid` and
|
||||
`impl From<DomainPart> for Jid`, both of which are (unlike
|
||||
`::from_parts`) copy-free.
|
||||
- `as_str` methods have been added on all Jid types.
|
||||
|
||||
Version 0.10.0, release 2023-08-17:
|
||||
* Breaking
|
||||
|
|
|
@ -17,6 +17,8 @@ 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)
|
||||
|
@ -107,38 +109,43 @@ impl InnerJid {
|
|||
})
|
||||
}
|
||||
|
||||
pub(crate) fn node(&self) -> Option<&str> {
|
||||
pub(crate) fn node(&self) -> Option<&NodeRef> {
|
||||
self.at.map(|at| {
|
||||
let at = u16::from(at) as usize;
|
||||
&self.normalized[..at]
|
||||
NodeRef::from_str_unchecked(&self.normalized[..at])
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn domain(&self) -> &str {
|
||||
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;
|
||||
&self.normalized[at + 1..slash]
|
||||
DomainRef::from_str_unchecked(&self.normalized[at + 1..slash])
|
||||
}
|
||||
(Some(at), None) => {
|
||||
let at = u16::from(at) as usize;
|
||||
&self.normalized[at + 1..]
|
||||
DomainRef::from_str_unchecked(&self.normalized[at + 1..])
|
||||
}
|
||||
(None, Some(slash)) => {
|
||||
let slash = u16::from(slash) as usize;
|
||||
&self.normalized[..slash]
|
||||
DomainRef::from_str_unchecked(&self.normalized[..slash])
|
||||
}
|
||||
(None, None) => &self.normalized,
|
||||
(None, None) => DomainRef::from_str_unchecked(&self.normalized),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn resource(&self) -> Option<&str> {
|
||||
pub(crate) fn resource(&self) -> Option<&ResourceRef> {
|
||||
self.slash.map(|slash| {
|
||||
let slash = u16::from(slash) as usize;
|
||||
&self.normalized[slash + 1..]
|
||||
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 {
|
||||
|
|
206
jid/src/lib.rs
206
jid/src/lib.rs
|
@ -49,7 +49,7 @@ mod inner;
|
|||
use inner::InnerJid;
|
||||
|
||||
mod parts;
|
||||
pub use parts::{DomainPart, NodePart, ResourcePart};
|
||||
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))]
|
||||
|
@ -106,9 +106,9 @@ impl Jid {
|
|||
/// # fn main() -> Result<(), Error> {
|
||||
/// let jid = Jid::new("node@domain/resource")?;
|
||||
///
|
||||
/// assert_eq!(jid.node_str(), Some("node"));
|
||||
/// assert_eq!(jid.domain_str(), "domain");
|
||||
/// assert_eq!(jid.resource_str(), Some("resource"));
|
||||
/// assert_eq!(jid.node().map(|x| x.as_str()), Some("node"));
|
||||
/// assert_eq!(jid.domain().as_str(), "domain");
|
||||
/// assert_eq!(jid.resource().map(|x| x.as_str()), Some("resource"));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
|
@ -130,11 +130,14 @@ 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<&NodePart>,
|
||||
domain: &DomainPart,
|
||||
resource: Option<&ResourcePart>,
|
||||
node: Option<&NodeRef>,
|
||||
domain: &DomainRef,
|
||||
resource: Option<&ResourceRef>,
|
||||
) -> Jid {
|
||||
if let Some(resource) = resource {
|
||||
Jid::Full(FullJid::from_parts(node, domain, resource))
|
||||
|
@ -143,51 +146,23 @@ impl Jid {
|
|||
}
|
||||
}
|
||||
|
||||
/// The optional node part of the JID, as a [`NodePart`]
|
||||
pub fn node(&self) -> Option<NodePart> {
|
||||
match self {
|
||||
Jid::Bare(BareJid { inner }) | Jid::Full(FullJid { inner }) => {
|
||||
inner.node().map(NodePart::new_unchecked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The optional node part of the JID, as a stringy reference
|
||||
pub fn node_str(&self) -> Option<&str> {
|
||||
/// 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(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The domain part of the JID, as a [`DomainPart`]
|
||||
pub fn domain(&self) -> DomainPart {
|
||||
match self {
|
||||
Jid::Bare(BareJid { inner }) | Jid::Full(FullJid { inner }) => {
|
||||
DomainPart::new_unchecked(inner.domain())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The domain part of the JID, as a stringy reference
|
||||
pub fn domain_str(&self) -> &str {
|
||||
/// 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(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The optional resource part of the JID, as a [`ResourcePart`]. 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<ResourcePart> {
|
||||
match self {
|
||||
Jid::Bare(BareJid { inner }) | Jid::Full(FullJid { inner }) => {
|
||||
inner.resource().map(ResourcePart::new_unchecked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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_str(&self) -> Option<&str> {
|
||||
pub fn resource(&self) -> Option<&ResourceRef> {
|
||||
match self {
|
||||
Jid::Bare(BareJid { inner }) | Jid::Full(FullJid { inner }) => inner.resource(),
|
||||
}
|
||||
|
@ -221,6 +196,13 @@ impl Jid {
|
|||
pub fn is_bare(&self) -> bool {
|
||||
!self.is_full()
|
||||
}
|
||||
|
||||
/// 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Jid> for FullJid {
|
||||
|
@ -301,13 +283,13 @@ pub struct BareJid {
|
|||
|
||||
impl fmt::Debug for FullJid {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(fmt, "FullJID({})", self)
|
||||
fmt.debug_tuple("FullJid").field(&self.inner).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BareJid {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(fmt, "BareJID({})", self)
|
||||
fmt.debug_tuple("BareJid").field(&self.inner).finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -415,9 +397,9 @@ impl FullJid {
|
|||
/// # fn main() -> Result<(), Error> {
|
||||
/// let jid = FullJid::new("node@domain/resource")?;
|
||||
///
|
||||
/// assert_eq!(jid.node_str(), Some("node"));
|
||||
/// assert_eq!(jid.domain_str(), "domain");
|
||||
/// assert_eq!(jid.resource_str(), "resource");
|
||||
/// assert_eq!(jid.node().map(|x| x.as_str()), Some("node"));
|
||||
/// assert_eq!(jid.domain().as_str(), "domain");
|
||||
/// assert_eq!(jid.resource().as_str(), "resource");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
|
@ -439,22 +421,27 @@ impl FullJid {
|
|||
/// 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,
|
||||
node: Option<&NodeRef>,
|
||||
domain: &DomainRef,
|
||||
resource: &ResourceRef,
|
||||
) -> 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),
|
||||
NonZeroU16::new(node.len() as u16),
|
||||
NonZeroU16::new((node.len() + 1 + domain.len()) as u16),
|
||||
format!(
|
||||
"{}@{}/{}",
|
||||
node.as_str(),
|
||||
domain.as_str(),
|
||||
resource.as_str()
|
||||
),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
None,
|
||||
NonZeroU16::new(domain.0.len() as u16),
|
||||
format!("{}/{}", domain.0, resource.0),
|
||||
NonZeroU16::new(domain.len() as u16),
|
||||
format!("{}/{}", domain.as_str(), resource.as_str()),
|
||||
)
|
||||
};
|
||||
|
||||
|
@ -467,34 +454,18 @@ impl FullJid {
|
|||
FullJid { inner }
|
||||
}
|
||||
|
||||
/// The optional node part of the JID, as a [`NodePart`]
|
||||
pub fn node(&self) -> Option<NodePart> {
|
||||
self.node_str().map(NodePart::new_unchecked)
|
||||
}
|
||||
|
||||
/// The optional node part of the JID, as a stringy reference
|
||||
pub fn node_str(&self) -> Option<&str> {
|
||||
/// 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 a [`DomainPart`]
|
||||
pub fn domain(&self) -> DomainPart {
|
||||
DomainPart::new_unchecked(self.domain_str())
|
||||
}
|
||||
|
||||
/// The domain part of the JID, as a stringy reference
|
||||
pub fn domain_str(&self) -> &str {
|
||||
/// The domain part of the JID as reference
|
||||
pub fn domain(&self) -> &DomainRef {
|
||||
self.inner.domain()
|
||||
}
|
||||
|
||||
/// The optional resource part of the JID, as a [`ResourcePart`]. Since this is a full JID it
|
||||
/// is always present.
|
||||
pub fn resource(&self) -> ResourcePart {
|
||||
ResourcePart::new_unchecked(self.resource_str())
|
||||
}
|
||||
|
||||
/// The optional resource of the Jabber ID. Since this is a full JID it is always present.
|
||||
pub fn resource_str(&self) -> &str {
|
||||
pub fn resource(&self) -> &ResourceRef {
|
||||
self.inner.resource().unwrap()
|
||||
}
|
||||
|
||||
|
@ -518,6 +489,11 @@ impl FullJid {
|
|||
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 {
|
||||
|
@ -542,8 +518,8 @@ impl BareJid {
|
|||
/// # fn main() -> Result<(), Error> {
|
||||
/// let jid = BareJid::new("node@domain")?;
|
||||
///
|
||||
/// assert_eq!(jid.node_str(), Some("node"));
|
||||
/// assert_eq!(jid.domain_str(), "domain");
|
||||
/// assert_eq!(jid.node().map(|x| x.as_str()), Some("node"));
|
||||
/// assert_eq!(jid.domain().as_str(), "domain");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
|
@ -562,17 +538,20 @@ 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 {
|
||||
/// 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
|
||||
(
|
||||
NonZeroU16::new(node.0.len() as u16),
|
||||
format!("{}@{}", node.0, domain.0),
|
||||
NonZeroU16::new(node.len() as u16),
|
||||
format!("{}@{}", node.as_str(), domain.as_str()),
|
||||
)
|
||||
} else {
|
||||
(None, domain.0.clone())
|
||||
(None, domain.to_string())
|
||||
};
|
||||
|
||||
let inner = InnerJid {
|
||||
|
@ -584,23 +563,13 @@ impl BareJid {
|
|||
BareJid { inner }
|
||||
}
|
||||
|
||||
/// The optional node part of the JID, as a [`NodePart`]
|
||||
pub fn node(&self) -> Option<NodePart> {
|
||||
self.node_str().map(NodePart::new_unchecked)
|
||||
}
|
||||
|
||||
/// The optional node part of the JID, as a stringy reference
|
||||
pub fn node_str(&self) -> Option<&str> {
|
||||
/// 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 a [`DomainPart`]
|
||||
pub fn domain(&self) -> DomainPart {
|
||||
DomainPart::new_unchecked(self.domain_str())
|
||||
}
|
||||
|
||||
/// The domain part of the JID, as a stringy reference
|
||||
pub fn domain_str(&self) -> &str {
|
||||
/// The domain part of the JID as reference
|
||||
pub fn domain(&self) -> &DomainRef {
|
||||
self.inner.domain()
|
||||
}
|
||||
|
||||
|
@ -616,11 +585,11 @@ impl BareJid {
|
|||
/// let bare = BareJid::new("node@domain").unwrap();
|
||||
/// let full = bare.with_resource(&resource);
|
||||
///
|
||||
/// assert_eq!(full.node_str(), Some("node"));
|
||||
/// assert_eq!(full.domain_str(), "domain");
|
||||
/// assert_eq!(full.resource_str(), "resource");
|
||||
/// assert_eq!(full.node().map(|x| x.as_str()), Some("node"));
|
||||
/// assert_eq!(full.domain().as_str(), "domain");
|
||||
/// assert_eq!(full.resource().as_str(), "resource");
|
||||
/// ```
|
||||
pub fn with_resource(&self, resource: &ResourcePart) -> FullJid {
|
||||
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 {
|
||||
|
@ -643,14 +612,19 @@ impl BareJid {
|
|||
/// let bare = BareJid::new("node@domain").unwrap();
|
||||
/// let full = bare.with_resource_str("resource").unwrap();
|
||||
///
|
||||
/// assert_eq!(full.node_str(), Some("node"));
|
||||
/// assert_eq!(full.domain_str(), "domain");
|
||||
/// assert_eq!(full.resource_str(), "resource");
|
||||
/// assert_eq!(full.node().map(|x| x.as_str()), Some("node"));
|
||||
/// assert_eq!(full.domain().as_str(), "domain");
|
||||
/// assert_eq!(full.resource().as_str(), "resource");
|
||||
/// ```
|
||||
pub fn with_resource_str(&self, resource: &str) -> Result<FullJid, Error> {
|
||||
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")]
|
||||
|
@ -796,27 +770,21 @@ mod tests {
|
|||
fn node_from_jid() {
|
||||
let jid = Jid::new("a@b.c/d").unwrap();
|
||||
|
||||
assert_eq!(jid.node_str(), Some("a"),);
|
||||
|
||||
assert_eq!(jid.node(), Some(NodePart::new("a").unwrap()));
|
||||
assert_eq!(jid.node().map(|x| x.as_str()), Some("a"),);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn domain_from_jid() {
|
||||
let jid = Jid::new("a@b.c").unwrap();
|
||||
|
||||
assert_eq!(jid.domain_str(), "b.c");
|
||||
|
||||
assert_eq!(jid.domain(), DomainPart::new("b.c").unwrap());
|
||||
assert_eq!(jid.domain().as_str(), "b.c");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resource_from_jid() {
|
||||
let jid = Jid::new("a@b.c/d").unwrap();
|
||||
|
||||
assert_eq!(jid.resource_str(), Some("d"),);
|
||||
|
||||
assert_eq!(jid.resource(), Some(ResourcePart::new("d").unwrap()));
|
||||
assert_eq!(jid.resource().map(|x| x.as_str()), Some("d"),);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -959,4 +927,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);
|
||||
}
|
||||
}
|
||||
|
|
320
jid/src/parts.rs
320
jid/src/parts.rs
|
@ -1,8 +1,11 @@
|
|||
use std::borrow::{Borrow, Cow};
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
|
||||
use stringprep::{nameprep, nodeprep, resourceprep};
|
||||
|
||||
use std::fmt;
|
||||
|
||||
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 {
|
||||
|
@ -14,77 +17,278 @@ fn length_check(len: usize, error_empty: Error, error_too_long: Error) -> Result
|
|||
}
|
||||
}
|
||||
|
||||
/// The [`NodePart`] is the optional part before the (optional) `@` in any [`Jid`][crate::Jid],
|
||||
/// whether [`BareJid`][crate::BareJid] or [`FullJid`][crate::FullJid].
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct NodePart(pub(crate) String);
|
||||
macro_rules! def_part_parse_doc {
|
||||
($name:ident, $other:ident, $more:expr) => {
|
||||
concat!(
|
||||
"Parse a [`",
|
||||
stringify!($name),
|
||||
"`] from a `",
|
||||
stringify!($other),
|
||||
"`, copying its contents.\n",
|
||||
"\n",
|
||||
"If the given `",
|
||||
stringify!($other),
|
||||
"` does not conform to the restrictions imposed by `",
|
||||
stringify!($name),
|
||||
"`, an error is returned.\n",
|
||||
$more,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
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()))
|
||||
}
|
||||
macro_rules! def_part_into_inner_doc {
|
||||
($name:ident, $other:ident, $more:expr) => {
|
||||
concat!(
|
||||
"Consume the `",
|
||||
stringify!($name),
|
||||
"` and return the inner `",
|
||||
stringify!($other),
|
||||
"`.\n",
|
||||
$more,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) fn new_unchecked(s: &str) -> NodePart {
|
||||
NodePart(s.to_string())
|
||||
macro_rules! def_part_types {
|
||||
(
|
||||
$(#[$mainmeta:meta])*
|
||||
pub struct $name:ident(String) use $prepfn:ident(err = $preperr:path, empty = $emptyerr:path, long = $longerr:path);
|
||||
|
||||
$(#[$refmeta:meta])*
|
||||
pub struct ref $borrowed:ident(str);
|
||||
) => {
|
||||
$(#[$mainmeta])*
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
pub struct $name(pub(crate) String);
|
||||
|
||||
impl $name {
|
||||
#[doc = def_part_parse_doc!($name, str, "Depending on whether the contents are changed by normalisation operations, this function either returns a copy or a reference to the original data.")]
|
||||
pub fn new(s: &str) -> Result<Cow<'_, $borrowed>, Error> {
|
||||
let node = $prepfn(s).map_err(|_| $preperr)?;
|
||||
length_check(node.len(), $emptyerr, $longerr)?;
|
||||
match node {
|
||||
Cow::Borrowed(v) => Ok(Cow::Borrowed($borrowed::from_str_unchecked(v))),
|
||||
Cow::Owned(v) => Ok(Cow::Owned(Self(v))),
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = def_part_into_inner_doc!($name, String, "")]
|
||||
pub fn into_inner(self) -> String {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for $name {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Error> {
|
||||
Ok(Self::new(s)?.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for $name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
<$borrowed as fmt::Display>::fmt(Borrow::<$borrowed>::borrow(self), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for $name {
|
||||
type Target = $borrowed;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
Borrow::<$borrowed>::borrow(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<$borrowed> for $name {
|
||||
fn as_ref(&self) -> &$borrowed {
|
||||
Borrow::<$borrowed>::borrow(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<String> for $name {
|
||||
fn as_ref(&self) -> &String {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<$borrowed> for $name {
|
||||
fn borrow(&self) -> &$borrowed {
|
||||
$borrowed::from_str_unchecked(self.0.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
// useful for use in hashmaps
|
||||
impl Borrow<String> for $name {
|
||||
fn borrow(&self) -> &String {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
// useful for use in hashmaps
|
||||
impl Borrow<str> for $name {
|
||||
fn borrow(&self) -> &str {
|
||||
self.0.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'x> TryFrom<&'x str> for $name {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(s: &str) -> Result<Self, Error> {
|
||||
Self::from_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&$borrowed> for $name {
|
||||
fn from(other: &$borrowed) -> Self {
|
||||
other.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'x> From<Cow<'x, $borrowed>> for $name {
|
||||
fn from(other: Cow<'x, $borrowed>) -> Self {
|
||||
other.into_owned()
|
||||
}
|
||||
}
|
||||
|
||||
$(#[$refmeta])*
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct $borrowed(pub(crate) str);
|
||||
|
||||
impl $borrowed {
|
||||
pub(crate) fn from_str_unchecked(s: &str) -> &Self {
|
||||
// SAFETY: repr(transparent) thing can be transmuted to/from
|
||||
// its inner.
|
||||
unsafe { std::mem::transmute(s) }
|
||||
}
|
||||
|
||||
/// Access the contents as [`str`] slice.
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for $borrowed {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for $borrowed {
|
||||
type Owned = $name;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
$name(self.0.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for $borrowed {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for $borrowed {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", &self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NodePart {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
def_part_types! {
|
||||
/// The [`NodePart`] is the optional part before the (optional) `@` in any
|
||||
/// [`Jid`][crate::Jid], whether [`BareJid`][crate::BareJid] or
|
||||
/// [`FullJid`][crate::FullJid].
|
||||
///
|
||||
/// The corresponding slice type is [`NodeRef`].
|
||||
pub struct NodePart(String) use nodeprep(err = Error::NodePrep, empty = Error::NodeEmpty, long = Error::NodeTooLong);
|
||||
|
||||
/// `str`-like type which conforms to the requirements of [`NodePart`].
|
||||
///
|
||||
/// See [`NodePart`] for details.
|
||||
pub struct ref NodeRef(str);
|
||||
}
|
||||
|
||||
def_part_types! {
|
||||
/// The [`DomainPart`] is the part between the (optional) `@` and the
|
||||
/// (optional) `/` in any [`Jid`][crate::Jid], whether
|
||||
/// [`BareJid`][crate::BareJid] or [`FullJid`][crate::FullJid].
|
||||
pub struct DomainPart(String) use nameprep(err = Error::NamePrep, empty = Error::DomainEmpty, long = Error::DomainTooLong);
|
||||
|
||||
/// `str`-like type which conforms to the requirements of [`DomainPart`].
|
||||
///
|
||||
/// See [`DomainPart`] for details.
|
||||
pub struct ref DomainRef(str);
|
||||
}
|
||||
|
||||
def_part_types! {
|
||||
/// The [`ResourcePart`] is the optional part after the `/` in a
|
||||
/// [`Jid`][crate::Jid]. It is mandatory in [`FullJid`][crate::FullJid].
|
||||
pub struct ResourcePart(String) use resourceprep(err = Error::ResourcePrep, empty = Error::ResourceEmpty, long = Error::ResourceTooLong);
|
||||
|
||||
/// `str`-like type which conforms to the requirements of
|
||||
/// [`ResourcePart`].
|
||||
///
|
||||
/// See [`ResourcePart`] for details.
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`DomainPart`] is the part between the (optional) `@` and the (optional) `/` in any
|
||||
/// [`Jid`][crate::Jid], whether [`BareJid`][crate::BareJid] or [`FullJid`][crate::FullJid].
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
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()))
|
||||
}
|
||||
|
||||
pub(crate) fn new_unchecked(s: &str) -> DomainPart {
|
||||
DomainPart(s.to_string())
|
||||
impl From<DomainPart> for BareJid {
|
||||
fn from(other: DomainPart) -> Self {
|
||||
BareJid {
|
||||
inner: InnerJid {
|
||||
normalized: other.0,
|
||||
at: None,
|
||||
slash: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DomainPart {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
impl From<DomainPart> for Jid {
|
||||
fn from(other: DomainPart) -> Self {
|
||||
Jid::Bare(other.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`ResourcePart`] is the optional part after the `/` in a [`Jid`][crate::Jid]. It is
|
||||
/// mandatory in [`FullJid`][crate::FullJid].
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
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()))
|
||||
}
|
||||
|
||||
pub(crate) fn new_unchecked(s: &str) -> ResourcePart {
|
||||
ResourcePart(s.to_string())
|
||||
impl<'x> From<&'x DomainRef> for BareJid {
|
||||
fn from(other: &'x DomainRef) -> Self {
|
||||
Self::from_parts(None, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ResourcePart {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
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::*;
|
||||
|
||||
#[test]
|
||||
fn nodepart_comparison() {
|
||||
let n1 = NodePart::new("foo").unwrap();
|
||||
let n2 = NodePart::new("bar").unwrap();
|
||||
let n3 = NodePart::new("foo").unwrap();
|
||||
assert_eq!(n1, n3);
|
||||
assert_ne!(n1, n2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -587,6 +587,34 @@ impl Element {
|
|||
self.children.push(Node::Text(child.into()));
|
||||
}
|
||||
|
||||
/// Appends a string as plain text to an `Element`.
|
||||
///
|
||||
/// If the last child node of the element is a text node, the string will be appended to it.
|
||||
/// Otherwise, a new text node will be created.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use minidom::Element;
|
||||
///
|
||||
/// let mut elem = Element::bare("node", "ns1");
|
||||
///
|
||||
/// assert_eq!(elem.text(), "");
|
||||
///
|
||||
/// elem.append_text_node("text");
|
||||
///
|
||||
/// elem.append_text(" and more text");
|
||||
///
|
||||
/// assert_eq!(elem.nodes().count(), 1);
|
||||
/// ```
|
||||
pub fn append_text<S: Into<String>>(&mut self, text: S) {
|
||||
if let Some(Node::Text(ref mut child)) = self.children.last_mut() {
|
||||
child.push_str(&text.into());
|
||||
} else {
|
||||
self.append_text_node(text);
|
||||
}
|
||||
}
|
||||
|
||||
/// Appends a node to an `Element`.
|
||||
///
|
||||
/// # Examples
|
||||
|
|
|
@ -97,7 +97,7 @@ impl TreeBuilder {
|
|||
fn process_text(&mut self, text: String) {
|
||||
if self.depth() > 0 {
|
||||
let top = self.stack.len() - 1;
|
||||
self.stack[top].append_text_node(text);
|
||||
self.stack[top].append_text(text);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,8 +15,8 @@ pub async fn bind<S: AsyncRead + AsyncWrite + Unpin>(
|
|||
if stream.stream_features.can_bind() {
|
||||
let resource = stream
|
||||
.jid
|
||||
.resource_str()
|
||||
.and_then(|resource| Some(resource.to_owned()));
|
||||
.resource()
|
||||
.and_then(|resource| Some(resource.to_string()));
|
||||
let iq = Iq::from_set(BIND_REQ_ID, BindQuery::new(resource));
|
||||
stream.send_stanza(iq).await?;
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ pub async fn client_login<C: ServerConnector>(
|
|||
jid: Jid,
|
||||
password: String,
|
||||
) -> Result<XMPPStream<C::Stream>, Error> {
|
||||
let username = jid.node_str().unwrap();
|
||||
let username = jid.node().unwrap().as_str();
|
||||
let password = password;
|
||||
|
||||
let xmpp_stream = server.connect(&jid, ns::JABBER_CLIENT).await?;
|
||||
|
|
|
@ -64,7 +64,7 @@ impl ServerConnector for ServerConfig {
|
|||
// TCP connection
|
||||
let tcp_stream = match self {
|
||||
ServerConfig::UseSrv => {
|
||||
connect_with_srv(jid.domain_str(), "_xmpp-client._tcp", 5222).await?
|
||||
connect_with_srv(jid.domain().as_str(), "_xmpp-client._tcp", 5222).await?
|
||||
}
|
||||
ServerConfig::Manual { host, port } => connect_to_host(host.as_str(), *port).await?,
|
||||
};
|
||||
|
@ -126,7 +126,7 @@ async fn get_tls_stream<S: AsyncRead + AsyncWrite + Unpin>(
|
|||
async fn get_tls_stream<S: AsyncRead + AsyncWrite + Unpin>(
|
||||
xmpp_stream: XMPPStream<S>,
|
||||
) -> Result<TlsStream<S>, Error> {
|
||||
let domain = xmpp_stream.jid.domain_str().to_owned();
|
||||
let domain = xmpp_stream.jid.domain().to_string();
|
||||
let domain = ServerName::try_from(domain.as_str())?;
|
||||
let stream = xmpp_stream.into_inner();
|
||||
let mut root_store = RootCertStore::empty();
|
||||
|
|
|
@ -15,7 +15,7 @@ pub async fn start<S: AsyncRead + AsyncWrite + Unpin>(
|
|||
ns: String,
|
||||
) -> Result<XMPPStream<S>, Error> {
|
||||
let attrs = [
|
||||
("to".to_owned(), jid.domain_str().to_owned()),
|
||||
("to".to_owned(), jid.domain().to_string()),
|
||||
("version".to_owned(), "1.0".to_owned()),
|
||||
("xmlns".to_owned(), ns.clone()),
|
||||
("xmlns:stream".to_owned(), ns::STREAM.to_owned()),
|
||||
|
|
|
@ -39,7 +39,7 @@ pub async fn handle_message_chat<C: ServerConnector>(
|
|||
Jid::Full(full) => Event::RoomPrivateMessage(
|
||||
message.id.clone(),
|
||||
full.to_bare(),
|
||||
full.resource_str().to_owned(),
|
||||
full.resource().to_string(),
|
||||
body.clone(),
|
||||
time_info.clone(),
|
||||
),
|
||||
|
|
|
@ -21,7 +21,7 @@ pub async fn handle_message_group_chat<C: ServerConnector>(
|
|||
if let Some((_lang, subject)) = message.get_best_subject(langs.clone()) {
|
||||
events.push(Event::RoomSubject(
|
||||
from.to_bare(),
|
||||
from.resource_str().map(String::from),
|
||||
from.resource().map(|x| x.to_string()),
|
||||
subject.0.clone(),
|
||||
time_info.clone(),
|
||||
));
|
||||
|
@ -32,7 +32,7 @@ pub async fn handle_message_group_chat<C: ServerConnector>(
|
|||
Jid::Full(full) => Event::RoomMessage(
|
||||
message.id.clone(),
|
||||
from.to_bare(),
|
||||
full.resource_str().to_owned(),
|
||||
full.resource().to_string(),
|
||||
body.clone(),
|
||||
time_info,
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue