11use rquickjs:: class:: Trace ;
2+ use rquickjs:: prelude:: Opt ;
23use rquickjs:: Class ;
34use rquickjs:: Ctx ;
45use rquickjs:: Exception ;
56use rquickjs:: JsLifetime ;
7+ use rquickjs:: Object ;
68use rquickjs:: Result ;
79use rquickjs:: TypedArray ;
10+ use std:: borrow:: Cow ;
811
9- #[ derive( Clone , Trace , JsLifetime ) ]
12+ #[ derive( Clone , Default , Trace , JsLifetime ) ]
1013#[ rquickjs:: class( frozen) ]
11- struct TextDecoder { }
14+ struct TextDecoder {
15+ fatal : bool ,
16+ ignore_bom : bool ,
17+ }
1218
1319pub fn init ( ctx : & Ctx < ' _ > ) {
1420 let globals = ctx. globals ( ) ;
@@ -18,22 +24,59 @@ pub fn init(ctx: &Ctx<'_>) {
1824#[ rquickjs:: methods]
1925impl < ' js > TextDecoder {
2026 #[ qjs( constructor) ]
21- fn new ( ) -> TextDecoder {
22- TextDecoder { }
27+ fn new ( ctx : Ctx < ' js > , label : Opt < String > , options : Opt < Object < ' js > > ) -> Result < TextDecoder > {
28+ if let Some ( label) = label. into_inner ( ) {
29+ if label != "utf-8" && label != "utf8" {
30+ return Err ( Exception :: throw_message (
31+ & ctx,
32+ "TextDecoder only supports utf-8" ,
33+ ) ) ;
34+ }
35+ }
36+ let decoder = options. into_inner ( ) . map ( |options| {
37+ let fatal = options. get ( "fatal" ) . ok ( ) . flatten ( ) . unwrap_or ( false ) ;
38+ let ignore_bom = options. get ( "ignoreBOM" ) . ok ( ) . flatten ( ) . unwrap_or ( false ) ;
39+ TextDecoder { fatal, ignore_bom }
40+ } ) ;
41+
42+ Ok ( decoder. unwrap_or_default ( ) )
2343 }
2444
2545 #[ qjs( get) ]
2646 fn encoding ( & self ) -> & str {
2747 "utf-8"
2848 }
2949
50+ #[ qjs( get) ]
51+ fn fatal ( & self ) -> bool {
52+ self . fatal
53+ }
54+
55+ #[ qjs( get, rename = "ignoreBOM" ) ]
56+ fn ignore_bom ( & self ) -> bool {
57+ self . ignore_bom
58+ }
59+
3060 pub fn decode ( & self , ctx : Ctx < ' js > , bytes : TypedArray < ' js , u8 > ) -> Result < String > {
31- let bytes = bytes
61+ let mut bytes = bytes
3262 . as_bytes ( )
3363 . ok_or ( Exception :: throw_message ( & ctx, "ArrayBuffer is detached" ) ) ?;
34- let text = std:: str:: from_utf8 ( bytes)
35- . map_err ( |err| Exception :: throw_message ( & ctx, & err. to_string ( ) ) ) ?;
3664
37- Ok ( text. to_owned ( ) )
65+ if !self . ignore_bom && bytes. get ( ..3 ) == Some ( & [ 0xEF , 0xBB , 0xBF ] ) {
66+ bytes = & bytes[ 3 ..] ;
67+ }
68+
69+ let text = if self . fatal {
70+ std:: str:: from_utf8 ( bytes)
71+ . map_err ( |err| Exception :: throw_message ( & ctx, & err. to_string ( ) ) ) ?
72+ . to_string ( )
73+ } else {
74+ match String :: from_utf8_lossy ( bytes) {
75+ Cow :: Owned ( text) => text,
76+ Cow :: Borrowed ( text) => text. to_owned ( ) ,
77+ }
78+ } ;
79+
80+ Ok ( text)
3881 }
3982}
0 commit comments