rqjs_ext/modules/encoding/
text_decoder.rs1use rquickjs::{function::Opt, Ctx, Exception, Object, Result, Value};
4
5use std::borrow::Cow;
6
7use crate::utils::{object::get_bytes, object::ObjectExt, result::ResultExt};
8use encoding_rs::Encoding;
9
10#[rquickjs::class]
11#[derive(rquickjs::class::Trace)]
12pub struct TextDecoder {
13 #[qjs(skip_trace)]
14 encoding: String,
15 fatal: bool,
16 ignore_bom: bool,
17}
18
19#[rquickjs::methods]
20impl<'js> TextDecoder {
21 #[qjs(constructor)]
22 pub fn new(ctx: Ctx<'js>, label: Opt<String>, options: Opt<Object<'js>>) -> Result<Self> {
23 let label = label
24 .0
25 .filter(|lbl| !lbl.is_empty())
26 .unwrap_or_else(|| String::from("utf-8"));
27 let mut fatal = false;
28 let mut ignore_bom = false;
29
30 let encoding = Encoding::for_label(label.as_bytes())
31 .map(|enc| enc.name().to_string())
32 .or_throw_msg(&ctx, "Unsupported encoding label")?;
33
34 if let Some(options) = options.0 {
35 if let Some(opt) = options.get_optional("fatal")? {
36 fatal = opt;
37 }
38 if let Some(opt) = options.get_optional("ignoreBOM")? {
39 ignore_bom = opt;
40 }
41 }
42
43 Ok(TextDecoder {
44 encoding,
45 fatal,
46 ignore_bom,
47 })
48 }
49
50 #[qjs(get)]
51 fn encoding(&self) -> String {
52 let s = self.encoding.clone();
53 s.replace('_', "-").to_ascii_lowercase()
54 }
55
56 #[qjs(get)]
57 fn fatal(&self) -> bool {
58 self.fatal
59 }
60
61 #[qjs(get, rename = "ignoreBOM")]
62 fn ignore_bom(&self) -> bool {
63 self.ignore_bom
64 }
65
66 pub fn decode(&self, ctx: Ctx<'js>, buffer: Value<'js>) -> Result<String> {
67 let bytes = get_bytes(&ctx, buffer)?;
68
69 let decoder = Encoding::for_label(self.encoding.as_bytes()).unwrap();
70
71 let str: Cow<str>;
72 let has_error: bool;
73
74 if decoder == encoding_rs::UTF_8 {
75 (str, has_error) = match self.ignore_bom {
76 false => decoder.decode_with_bom_removal(&bytes),
77 true => decoder.decode_without_bom_handling(&bytes),
78 }
79 } else {
80 (str, _, has_error) = decoder.decode(&bytes);
81 }
82
83 if self.fatal && has_error {
84 return Err(Exception::throw_message(&ctx, "Fatal error"));
85 }
86
87 Ok(str.into_owned())
88 }
89}