rqjs_ext/modules/encoding/
text_decoder.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3use 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}