use alloc::string::String;
use core::str;
use rquickjs::{Ctx, Error as JSError, String as JSString, Value, qjs};
use crate::err::{Error, Result};
pub fn as_key(v: &Value) -> Result<String> {
if v.is_string() {
let js_str = v.as_string().unwrap();
let v = js_str
.to_string()
.unwrap_or_else(|e| to_string_lossy(js_str.ctx(), js_str, e));
Ok(v)
} else {
Err(Error::new("map keys must be a string"))
}
}
pub fn to_string_lossy<'js>(cx: &Ctx<'js>, string: &JSString<'js>, error: JSError) -> String {
let mut len: qjs::size_t = 0;
let ptr = unsafe {
#[allow(clippy::useless_conversion)]
qjs::JS_ToCStringLen2(
cx.as_raw().as_ptr(),
&mut len,
string.as_raw(),
(false).into(),
)
};
let buffer = unsafe { core::slice::from_raw_parts(ptr as *const u8, len as usize) };
let mut utf8_error = match error {
JSError::Utf8(e) => e,
_ => unreachable!("expected Utf8 error"),
};
let mut res = String::new();
let mut buffer = buffer;
loop {
let (valid, after_valid) = buffer.split_at(utf8_error.valid_up_to());
res.push_str(unsafe { str::from_utf8_unchecked(valid) });
res.push(char::REPLACEMENT_CHARACTER);
let lone_surrogate = matches!(after_valid, [0xED, 0xA0..=0xBF, 0x80..=0xBF, ..]);
let error_len = if lone_surrogate {
3
} else {
utf8_error
.error_len()
.expect("Error length should always be available on underlying buffer")
};
buffer = &after_valid[error_len..];
match str::from_utf8(buffer) {
Ok(valid) => {
res.push_str(valid);
break;
}
Err(e) => utf8_error = e,
}
}
res
}