jid: Replace icu with stringprep

This dependency is unmaintained, but it is written in pure Rust unlike
ICU, and doesn’t require a roundtrip through UTF-16, improving both
performances (perhaps?) and ease of compilation.
This commit is contained in:
Emmanuel Gil Peyrot 2023-06-19 23:39:21 +02:00
parent a7dee0bef4
commit d867d8d7a1
2 changed files with 26 additions and 21 deletions

View file

@ -19,9 +19,6 @@ edition = "2018"
gitlab = { repository = "xmpp-rs/xmpp-rs" } gitlab = { repository = "xmpp-rs/xmpp-rs" }
[dependencies] [dependencies]
icu = { version = "0.1", optional = true }
minidom = { version = "0.15", optional = true } minidom = { version = "0.15", optional = true }
serde = { version = "1.0", features = ["derive"], optional = true } serde = { version = "1.0", features = ["derive"], optional = true }
stringprep = "0.1.2"
[features]
stringprep = ["icu"]

View file

@ -18,15 +18,13 @@ use std::convert::{Into, TryFrom};
use std::error::Error as StdError; use std::error::Error as StdError;
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use stringprep::{nameprep, nodeprep, resourceprep};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "icu")]
use icu::{Icu, Strict};
/// An error that signifies that a `Jid` cannot be parsed from a string. /// An error that signifies that a `Jid` cannot be parsed from a string.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug)]
pub enum JidParseError { pub enum JidParseError {
/// Happens when there is no domain, that is either the string is empty, /// Happens when there is no domain, that is either the string is empty,
/// starts with a /, or contains the @/ sequence. /// starts with a /, or contains the @/ sequence.
@ -41,19 +39,33 @@ pub enum JidParseError {
/// Happens when the resource is empty, that is the string ends with a /. /// Happens when the resource is empty, that is the string ends with a /.
EmptyResource, EmptyResource,
#[cfg(feature = "icu")]
/// Happens when the JID is invalid according to stringprep. TODO: make errors /// Happens when the JID is invalid according to stringprep. TODO: make errors
/// meaningful. /// meaningful.
Stringprep(icu::Error), Stringprep(stringprep::Error),
} }
#[cfg(feature = "icu")] impl From<stringprep::Error> for JidParseError {
impl From<icu::Error> for JidParseError { fn from(e: stringprep::Error) -> JidParseError {
fn from(e: icu::Error) -> JidParseError {
JidParseError::Stringprep(e) JidParseError::Stringprep(e)
} }
} }
impl PartialEq for JidParseError {
fn eq(&self, other: &JidParseError) -> bool {
use JidParseError as E;
match (self, other) {
(E::NoDomain, E::NoDomain) => true,
(E::NoResource, E::NoResource) => true,
(E::EmptyNode, E::EmptyNode) => true,
(E::EmptyResource, E::EmptyResource) => true,
(E::Stringprep(_), E::Stringprep(_)) => false, // TODO: fix that.
_ => false,
}
}
}
impl Eq for JidParseError {}
impl StdError for JidParseError {} impl StdError for JidParseError {}
impl fmt::Display for JidParseError { impl fmt::Display for JidParseError {
@ -66,7 +78,6 @@ impl fmt::Display for JidParseError {
JidParseError::NoResource => "no resource found in this full JID", JidParseError::NoResource => "no resource found in this full JID",
JidParseError::EmptyNode => "nodepart empty despite the presence of a @", JidParseError::EmptyNode => "nodepart empty despite the presence of a @",
JidParseError::EmptyResource => "resource empty despite the presence of a /", JidParseError::EmptyResource => "resource empty despite the presence of a /",
#[cfg(feature = "icu")]
JidParseError::Stringprep(_err) => "TODO", JidParseError::Stringprep(_err) => "TODO",
} }
) )
@ -416,17 +427,15 @@ fn _from_str(s: &str) -> Result<StringJid, JidParseError> {
return Err(JidParseError::EmptyResource); return Err(JidParseError::EmptyResource);
} }
let domain = domain.ok_or(JidParseError::NoDomain)?; let domain = domain.ok_or(JidParseError::NoDomain)?;
#[cfg(feature = "icu")]
let (node, domain, resource) = { let (node, domain, resource) = {
let icu = Icu::new()?;
let node = if let Some(node) = node { let node = if let Some(node) = node {
Some(icu.nodeprep(&node, Strict::AllowUnassigned)?) Some(nodeprep(&node)?.into_owned())
} else { } else {
None None
}; };
let domain = icu.idna2008.to_unicode(&domain)?; let domain = nameprep(&domain)?.into_owned();
let resource = if let Some(resource) = resource { let resource = if let Some(resource) = resource {
Some(icu.resourceprep(&resource, Strict::AllowUnassigned)?) Some(resourceprep(&resource)?.into_owned())
} else { } else {
None None
}; };
@ -973,9 +982,8 @@ mod tests {
assert_eq!(elem.attr("from"), Some(String::from(bare).as_ref())); assert_eq!(elem.attr("from"), Some(String::from(bare).as_ref()));
} }
#[cfg(feature = "icu")]
#[test] #[test]
fn icu_jid() { fn stringprep() {
let full = FullJid::from_str("Test@☃.coM/Test™").unwrap(); let full = FullJid::from_str("Test@☃.coM/Test™").unwrap();
let equiv = FullJid::new("test", "☃.com", "TestTM"); let equiv = FullJid::new("test", "☃.com", "TestTM");
assert_eq!(full, equiv); assert_eq!(full, equiv);