Skip to content

Commit

Permalink
Add JsString <-> char conversions
Browse files Browse the repository at this point in the history
These are pretty common and already supported via ABI conversions, yet pretty easy to get wrong when converting them manually.

Fixes #1363.
  • Loading branch information
RReverser committed Apr 23, 2019
1 parent ac7230b commit 91f112d
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 0 deletions.
43 changes: 43 additions & 0 deletions crates/js-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3641,6 +3641,36 @@ impl JsString {
) -> impl ExactSizeIterator<Item = u16> + DoubleEndedIterator<Item = u16> + '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<char> {
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<str> for JsString {
Expand All @@ -3649,6 +3679,12 @@ impl PartialEq<str> for JsString {
}
}

impl PartialEq<char> 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 {
<JsString as PartialEq<str>>::eq(self, other)
Expand Down Expand Up @@ -3679,6 +3715,13 @@ impl From<String> for JsString {
}
}

impl From<char> 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()
Expand Down
11 changes: 11 additions & 0 deletions crates/js-sys/tests/wasm/JsString.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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("abc").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);
}

0 comments on commit 91f112d

Please sign in to comment.