use rong::{function::Optional, *};
#[derive(Default)]
struct TextDecoderOptions {
fatal: bool,
ignore_bom: bool,
}
#[js_export]
pub struct TextDecoder {
encoding: &'static str,
fatal: bool,
ignore_bom: bool,
}
#[js_class]
impl TextDecoder {
#[js_method(constructor)]
fn new(label: Optional<String>, options: Optional<JSObject>) -> JSResult<Self> {
if let Some(label) = label.0 {
let label = label.to_lowercase();
match label.as_str() {
"utf-8" | "utf8" | "" => {}
_ => {
return Err(HostError::new(
rong::error::E_INVALID_ARG,
format!("Unsupported encoding: {}", label),
)
.with_name("TypeError")
.into());
}
}
}
let mut opts = TextDecoderOptions::default();
if let Some(options) = options.0 {
if let Ok(fatal) = options.get::<_, bool>("fatal") {
opts.fatal = fatal;
}
if let Ok(ignore_bom) = options.get::<_, bool>("ignoreBOM") {
opts.ignore_bom = ignore_bom;
}
}
Ok(Self {
encoding: "utf-8",
fatal: opts.fatal,
ignore_bom: opts.ignore_bom,
})
}
#[js_method(getter, enumerable)]
fn encoding(&self) -> String {
self.encoding.to_string()
}
#[js_method(getter, enumerable)]
fn fatal(&self) -> bool {
self.fatal
}
#[js_method(getter, enumerable, rename = "ignoreBOM")]
fn ignore_bom(&self) -> bool {
self.ignore_bom
}
#[js_method]
fn decode(&self, input: Optional<JSObject>, options: Optional<JSObject>) -> JSResult<String> {
let mut _stream = false;
if let Some(options) = options.0
&& let Ok(stream) = options.get::<_, bool>("stream")
{
_stream = stream;
}
let bytes = if let Some(input) = input.0 {
if let Some(typed_array) = AnyJSTypedArray::from_object(input.clone()) {
if let Some(bytes) = typed_array.as_bytes() {
bytes.to_vec()
} else {
return Err(
HostError::new(rong::error::E_INVALID_ARG, "Invalid TypedArray")
.with_name("TypeError")
.into(),
);
}
} else if let Some(buffer) = JSArrayBuffer::from_object(input) {
buffer.as_bytes().to_vec()
} else {
return Err(HostError::new(
rong::error::E_INVALID_ARG,
"Input must be an ArrayBuffer or TypedArray",
)
.with_name("TypeError")
.into());
}
} else {
Vec::new() };
let start =
if !self.ignore_bom && bytes.len() >= 3 && bytes.starts_with(&[0xEF, 0xBB, 0xBF]) {
3
} else {
0
};
match String::from_utf8(bytes[start..].to_vec()) {
Ok(text) => Ok(text),
Err(e) => {
if self.fatal {
Err(HostError::new(
rong::error::E_INVALID_ARG,
format!("Invalid UTF-8 sequence: {}", e),
)
.with_name("TypeError")
.into())
} else {
Ok(String::from_utf8_lossy(&bytes[start..]).into_owned())
}
}
}
}
#[js_method(gc_mark)]
fn gc_mark_with<F>(&self, _mark_fn: F)
where
F: FnMut(&JSValue),
{
}
}
pub(crate) fn init(ctx: &JSContext) -> JSResult<()> {
ctx.register_class::<TextDecoder>()?;
Ok(())
}