use crate::{CodePoint, JsStr, JsStrVariant};
use std::fmt;
use std::fmt::Write;
#[derive(Debug)]
pub struct JsStrDisplayEscaped<'a> {
inner: JsStr<'a>,
}
impl fmt::Display for JsStrDisplayEscaped<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.inner.variant() {
JsStrVariant::Latin1(v) => v
.iter()
.copied()
.map(char::from)
.try_for_each(|c| f.write_char(c)),
JsStrVariant::Utf16(_) => self.inner.code_points().try_for_each(|r| match r {
CodePoint::Unicode(c) => f.write_char(c),
CodePoint::UnpairedSurrogate(c) => {
write!(f, "\\u{c:04X}")
}
}),
}
}
}
impl<'a> From<JsStr<'a>> for JsStrDisplayEscaped<'a> {
fn from(inner: JsStr<'a>) -> Self {
Self { inner }
}
}
#[derive(Debug)]
pub struct JsStrDisplayLossy<'a> {
inner: JsStr<'a>,
}
impl fmt::Display for JsStrDisplayLossy<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner
.code_points_lossy()
.try_for_each(|c| f.write_char(c))
}
}
impl<'a> From<JsStr<'a>> for JsStrDisplayLossy<'a> {
fn from(inner: JsStr<'a>) -> Self {
Self { inner }
}
}
#[test]
fn latin1() {
let s = JsStr::latin1(b"Hello \xE9 world!");
let rust_str = format!("{}", JsStrDisplayEscaped { inner: s });
assert_eq!(rust_str, "Hello é world!");
let rust_str = format!("{}", JsStrDisplayLossy { inner: s });
assert_eq!(rust_str, "Hello é world!");
}
#[test]
fn emoji() {
let s = JsStr::utf16(&[0xD83D, 0xDE00]);
let rust_str = format!("{}", JsStrDisplayEscaped { inner: s });
assert_eq!(rust_str, "😀");
let rust_str = format!("{}", JsStrDisplayLossy { inner: s });
assert_eq!(rust_str, "😀");
}
#[test]
fn unpaired_surrogates() {
let s = JsStr::utf16(&[0xD800]);
let rust_str = format!("{}", JsStrDisplayEscaped { inner: s });
assert_eq!(rust_str, "\\uD800");
let rust_str = format!("{}", JsStrDisplayLossy { inner: s });
assert_eq!(rust_str, "�");
}