From bc4a98d14690ea8668cc09f8e39bbab5b9b5947d Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Thu, 18 Apr 2019 14:01:13 +0100 Subject: [PATCH 1/2] Add JsString <-> char conversions These are pretty common and already supported via ABI conversions, yet pretty easy to get wrong when converting them manually. Fixes #1363. --- crates/js-sys/src/lib.rs | 43 ++++++++++++++++++++++++++++ crates/js-sys/tests/wasm/JsString.rs | 11 +++++++ 2 files changed, 54 insertions(+) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 7593aa0f822..3f64633fbf2 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -3641,6 +3641,36 @@ impl JsString { ) -> impl ExactSizeIterator + DoubleEndedIterator + 'a { (0..self.length()).map(move |i| self.char_code_at(i) as u16) } + + /// If this string consists of a single Unicode code point, then this method + /// converts it into a Rust `char` without doing any allocations. + /// + /// If this JS value is not a valid UTF-8 or consists of more than a single + /// codepoint, then this returns `None`. + /// + /// Note that a single Unicode code point might be represented as more than + /// one code unit on the JavaScript side. For example, a JavaScript string + /// `"\uD801\uDC37"` is actually a single Unicode code point U+10437 which + /// corresponds to a character '𐐷'. + pub fn as_char(&self) -> Option { + let len = self.length(); + + if len == 0 || len > 2 { + return None; + } + + // This will be simplified when definitions are fixed: + // https://github.com/rustwasm/wasm-bindgen/issues/1362 + let cp = self.code_point_at(0).as_f64().unwrap_throw() as u32; + + let c = std::char::from_u32(cp)?; + + if c.len_utf16() as u32 == len { + Some(c) + } else { + None + } + } } impl PartialEq for JsString { @@ -3649,6 +3679,12 @@ impl PartialEq for JsString { } } +impl PartialEq for JsString { + fn eq(&self, other: &char) -> bool { + self.as_char() == Some(*other) + } +} + impl<'a> PartialEq<&'a str> for JsString { fn eq(&self, other: &&'a str) -> bool { >::eq(self, other) @@ -3679,6 +3715,13 @@ impl From for JsString { } } +impl From for JsString { + #[inline] + fn from(c: char) -> Self { + JsString::from_code_point1(c as u32).unwrap_throw() + } +} + impl<'a> From<&'a JsString> for String { fn from(s: &'a JsString) -> Self { s.obj.as_string().unwrap_throw() diff --git a/crates/js-sys/tests/wasm/JsString.rs b/crates/js-sys/tests/wasm/JsString.rs index c7f229f1613..d2b8505cc4e 100644 --- a/crates/js-sys/tests/wasm/JsString.rs +++ b/crates/js-sys/tests/wasm/JsString.rs @@ -553,3 +553,14 @@ fn is_valid_utf16() { assert!(!JsString::from_char_code1(0xd800).is_valid_utf16()); assert!(!JsString::from_char_code1(0xdc00).is_valid_utf16()); } + +#[wasm_bindgen_test] +fn as_char() { + assert_eq!(JsString::from('a').as_char(), Some('a')); + assert_eq!(JsString::from('🥑').as_char(), Some('🥑')); + assert_eq!(JsString::from("").as_char(), None); + assert_eq!(JsString::from("ab").as_char(), None); + assert_eq!(JsString::from_char_code1(0xd800).as_char(), None); + assert_eq!(JsString::from_char_code1(0xdc00).as_char(), None); + assert_eq!(JsString::from_char_code1(0xdfff).as_char(), None); +} From 680a6bbb0cbca198f2ea74513742f8e65d54bc6f Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Wed, 24 Apr 2019 13:40:18 +0100 Subject: [PATCH 2/2] Remove PartialEq for JsString for now This seems to spark controversy, so removing for now but should be easy enough to still add in the future. --- crates/js-sys/src/lib.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 3f64633fbf2..1ffadaa9500 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -3679,12 +3679,6 @@ impl PartialEq for JsString { } } -impl PartialEq for JsString { - fn eq(&self, other: &char) -> bool { - self.as_char() == Some(*other) - } -} - impl<'a> PartialEq<&'a str> for JsString { fn eq(&self, other: &&'a str) -> bool { >::eq(self, other)