From d867d8d7a1e4bcfe1aba4f600832230df448ccd9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 19 Jun 2023 23:39:21 +0200 Subject: [PATCH] jid: Replace icu with stringprep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- jid/Cargo.toml | 5 +---- jid/src/lib.rs | 42 +++++++++++++++++++++++++----------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/jid/Cargo.toml b/jid/Cargo.toml index c53e0eb..b55541a 100644 --- a/jid/Cargo.toml +++ b/jid/Cargo.toml @@ -19,9 +19,6 @@ edition = "2018" gitlab = { repository = "xmpp-rs/xmpp-rs" } [dependencies] -icu = { version = "0.1", optional = true } minidom = { version = "0.15", optional = true } serde = { version = "1.0", features = ["derive"], optional = true } - -[features] -stringprep = ["icu"] +stringprep = "0.1.2" diff --git a/jid/src/lib.rs b/jid/src/lib.rs index 9d3a9e2..f8ac2e6 100644 --- a/jid/src/lib.rs +++ b/jid/src/lib.rs @@ -18,15 +18,13 @@ use std::convert::{Into, TryFrom}; use std::error::Error as StdError; use std::fmt; use std::str::FromStr; +use stringprep::{nameprep, nodeprep, resourceprep}; #[cfg(feature = "serde")] 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. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug)] pub enum JidParseError { /// Happens when there is no domain, that is either the string is empty, /// 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 /. EmptyResource, - #[cfg(feature = "icu")] /// Happens when the JID is invalid according to stringprep. TODO: make errors /// meaningful. - Stringprep(icu::Error), + Stringprep(stringprep::Error), } -#[cfg(feature = "icu")] -impl From for JidParseError { - fn from(e: icu::Error) -> JidParseError { +impl From for JidParseError { + fn from(e: stringprep::Error) -> JidParseError { 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 fmt::Display for JidParseError { @@ -66,7 +78,6 @@ impl fmt::Display for JidParseError { JidParseError::NoResource => "no resource found in this full JID", JidParseError::EmptyNode => "nodepart empty despite the presence of a @", JidParseError::EmptyResource => "resource empty despite the presence of a /", - #[cfg(feature = "icu")] JidParseError::Stringprep(_err) => "TODO", } ) @@ -416,17 +427,15 @@ fn _from_str(s: &str) -> Result { return Err(JidParseError::EmptyResource); } let domain = domain.ok_or(JidParseError::NoDomain)?; - #[cfg(feature = "icu")] let (node, domain, resource) = { - let icu = Icu::new()?; let node = if let Some(node) = node { - Some(icu.nodeprep(&node, Strict::AllowUnassigned)?) + Some(nodeprep(&node)?.into_owned()) } else { None }; - let domain = icu.idna2008.to_unicode(&domain)?; + let domain = nameprep(&domain)?.into_owned(); let resource = if let Some(resource) = resource { - Some(icu.resourceprep(&resource, Strict::AllowUnassigned)?) + Some(resourceprep(&resource)?.into_owned()) } else { None }; @@ -973,9 +982,8 @@ mod tests { assert_eq!(elem.attr("from"), Some(String::from(bare).as_ref())); } - #[cfg(feature = "icu")] #[test] - fn icu_jid() { + fn stringprep() { let full = FullJid::from_str("Test@☃.coM/Test™").unwrap(); let equiv = FullJid::new("test", "☃.com", "TestTM"); assert_eq!(full, equiv);