1+ use mlua:: { ffi:: lua, lua_State, prelude:: * } ;
2+ use std:: cmp;
3+
4+ enum EncoderEncoding {
5+ UTF8 ,
6+ UTF16 ,
7+ UTF16LE ,
8+ UTF16BE ,
9+ }
10+
11+ enum EncoderBom {
12+ No ,
13+ Yes ,
14+ Auto ,
15+ }
16+
17+ fn get_encoder_encoding ( encoding : & str ) -> Option < EncoderEncoding > {
18+ match encoding {
19+ "utf8" => Some ( EncoderEncoding :: UTF8 ) ,
20+ "utf16" => Some ( EncoderEncoding :: UTF16 ) ,
21+ "utf16le" => Some ( EncoderEncoding :: UTF16LE ) ,
22+ "utf16be" => Some ( EncoderEncoding :: UTF16BE ) ,
23+ _ => None ,
24+ }
25+ }
26+
27+ fn get_encoder_bom ( bom : & str ) -> Option < EncoderBom > {
28+ match bom {
29+ "no" => Some ( EncoderBom :: No ) ,
30+ "yes" => Some ( EncoderBom :: Yes ) ,
31+ "auto" => Some ( EncoderBom :: Auto ) ,
32+ _ => None ,
33+ }
34+ }
35+
36+ fn encoder_len ( lua : & Lua , ( encoding, s, i, j) : ( String , String , Option < i32 > , Option < i32 > ) ) -> LuaResult < i32 > {
37+ let len = s. len ( ) as i32 ;
38+
39+ let start = match i {
40+ Some ( idx) if idx > 0 => cmp:: min ( idx - 1 , len) ,
41+ Some ( idx) if idx < 0 => cmp:: max ( len + idx, 0 ) ,
42+ _ => 0 ,
43+ } ;
44+
45+ let end = match j {
46+ Some ( idx) if idx > 0 => cmp:: min ( idx, len) ,
47+ Some ( idx) if idx < 0 => cmp:: max ( len + idx + 1 , 0 ) ,
48+ _ => len,
49+ } ;
50+
51+ let substr = & s[ start as usize ..end as usize ] ;
52+
53+ match get_encoder_encoding ( & encoding) {
54+ Some ( EncoderEncoding :: UTF8 ) => Ok ( substr. len ( ) as i32 ) ,
55+ Some ( EncoderEncoding :: UTF16 ) | Some ( EncoderEncoding :: UTF16LE ) | Some ( EncoderEncoding :: UTF16BE ) => {
56+ Ok ( substr. encode_utf16 ( ) . count ( ) as i32 )
57+ }
58+ None => Err ( mlua:: Error :: RuntimeError ( "Unsupported encoding" . to_string ( ) ) ) ,
59+ }
60+ }
61+
62+ fn encoder_offset ( lua : & Lua , ( encoding, s, n, i) : ( String , String , i32 , Option < i32 > ) ) -> LuaResult < i32 > {
63+ let len = s. len ( ) as i32 ;
64+
65+ let start = match i {
66+ Some ( idx) if idx > 0 => cmp:: min ( idx - 1 , len) ,
67+ Some ( idx) if idx < 0 => cmp:: max ( len + idx, 0 ) ,
68+ _ => 0 ,
69+ } ;
70+
71+ let substr = & s[ start as usize ..] ;
72+
73+ let mut char_count = 0 ;
74+ for ( byte_idx, _) in substr. char_indices ( ) {
75+ if char_count == n {
76+ return Ok ( ( start + byte_idx as i32 ) as i32 ) ;
77+ }
78+ char_count += 1 ;
79+ }
80+
81+ Ok ( -1 )
82+ }
83+
84+ fn encoder_encode ( lua : & Lua , ( encoding, s, bom) : ( String , String , String ) ) -> LuaResult < String > {
85+ let bom = get_encoder_bom ( & bom) . ok_or_else ( || mlua:: Error :: RuntimeError ( "Invalid BOM option" . to_string ( ) ) ) ?;
86+ let encoding = get_encoder_encoding ( & encoding) . ok_or_else ( || mlua:: Error :: RuntimeError ( "Unsupported encoding" . to_string ( ) ) ) ?;
87+
88+ let mut encoded: Vec < u8 > = match encoding {
89+ EncoderEncoding :: UTF8 => s. into_bytes ( ) ,
90+ EncoderEncoding :: UTF16 | EncoderEncoding :: UTF16LE => s. encode_utf16 ( ) . flat_map ( |u| u. to_le_bytes ( ) ) . collect ( ) ,
91+ EncoderEncoding :: UTF16BE => s. encode_utf16 ( ) . flat_map ( |u| u. to_be_bytes ( ) ) . collect ( ) ,
92+ } ;
93+
94+ if let EncoderBom :: Yes = bom {
95+ match encoding {
96+ EncoderEncoding :: UTF8 => encoded. splice ( 0 ..0 , [ 0xEF , 0xBB , 0xBF ] . iter ( ) . cloned ( ) ) . for_each ( drop) ,
97+ EncoderEncoding :: UTF16 | EncoderEncoding :: UTF16LE => encoded. splice ( 0 ..0 , [ 0xFF , 0xFE ] . iter ( ) . cloned ( ) ) . for_each ( drop) ,
98+ EncoderEncoding :: UTF16BE => encoded. splice ( 0 ..0 , [ 0xFE , 0xFF ] . iter ( ) . cloned ( ) ) . for_each ( drop) ,
99+ }
100+ }
101+
102+ Ok ( encoded. iter ( ) . map ( |b| * b as char ) . collect ( ) )
103+ }
104+
105+ fn encoder_decode ( lua : & Lua , ( encoding, s) : ( String , String ) ) -> LuaResult < String > {
106+ let encoding = get_encoder_encoding ( & encoding) . ok_or_else ( || mlua:: Error :: RuntimeError ( "Unsupported encoding" . to_string ( ) ) ) ?;
107+
108+ let decoded = match encoding {
109+ EncoderEncoding :: UTF8 => s,
110+ EncoderEncoding :: UTF16 | EncoderEncoding :: UTF16LE => {
111+ let bytes: Vec < u16 > = s. as_bytes ( ) . chunks ( 2 ) . map ( |chunk| u16:: from_le_bytes ( [ chunk[ 0 ] , chunk[ 1 ] ] ) ) . collect ( ) ;
112+ String :: from_utf16 ( & bytes) . map_err ( |e| mlua:: Error :: RuntimeError ( e. to_string ( ) ) ) ?
113+ }
114+ EncoderEncoding :: UTF16BE => {
115+ let bytes: Vec < u16 > = s. as_bytes ( ) . chunks ( 2 ) . map ( |chunk| u16:: from_be_bytes ( [ chunk[ 0 ] , chunk[ 1 ] ] ) ) . collect ( ) ;
116+ String :: from_utf16 ( & bytes) . map_err ( |e| mlua:: Error :: RuntimeError ( e. to_string ( ) ) ) ?
117+ }
118+ } ;
119+
120+ Ok ( decoded)
121+ }
122+
123+ pub fn lua_encoder_loader ( lua : & Lua ) -> LuaResult < LuaTable > {
124+ let encoder = lua. create_table ( ) ?;
125+ encoder. set ( "len" , lua. create_function ( encoder_len) ?) ;
126+ encoder. set ( "offset" , lua. create_function ( encoder_offset) ?) ;
127+ encoder. set ( "encode" , lua. create_function ( encoder_encode) ?) ;
128+ encoder. set ( "decode" , lua. create_function ( encoder_decode) ?) ;
129+ Ok ( encoder)
130+ }
0 commit comments