1use std::collections::{BTreeMap, HashMap};
4
5use rquickjs::{
6 atom::PredefinedAtom, Array, ArrayBuffer, Coerced, Ctx, Exception, FromJs, Function, IntoAtom,
7 IntoJs, Object, Result, Symbol, TypedArray, Value,
8};
9
10use super::result::ResultExt;
11
12#[allow(dead_code)]
13pub fn array_to_hash_map<'js>(
14 ctx: &Ctx<'js>,
15 array: Array<'js>,
16) -> Result<HashMap<String, String>> {
17 let value = object_from_entries(ctx, array)?;
18 let value = value.into_value();
19 HashMap::from_js(ctx, value)
20}
21
22pub fn array_to_btree_map<'js>(
23 ctx: &Ctx<'js>,
24 array: Array<'js>,
25) -> Result<BTreeMap<String, Coerced<String>>> {
26 let value = object_from_entries(ctx, array)?;
27 let value = value.into_value();
28 BTreeMap::from_js(ctx, value)
29}
30
31pub fn object_from_entries<'js>(ctx: &Ctx<'js>, array: Array<'js>) -> Result<Object<'js>> {
32 let obj = Object::new(ctx.clone())?;
33 for value in array.into_iter().flatten() {
34 if let Some(entry) = value.as_array() {
35 if let Ok(key) = entry.get::<Value>(0) {
36 if let Ok(value) = entry.get::<Value>(1) {
37 let _ = obj.set(key, value); }
39 }
40 }
41 }
42 Ok(obj)
43}
44
45pub fn map_to_entries<'js, K, V, M>(ctx: &Ctx<'js>, map: M) -> Result<Array<'js>>
46where
47 M: IntoIterator<Item = (K, V)>,
48 K: IntoJs<'js>,
49 V: IntoJs<'js>,
50{
51 let array = Array::new(ctx.clone())?;
52 for (idx, (key, value)) in map.into_iter().enumerate() {
53 let entry = Array::new(ctx.clone())?;
54 entry.set(0, key)?;
55 entry.set(1, value)?;
56 array.set(idx, entry)?;
57 }
58
59 Ok(array)
60}
61
62pub fn get_start_end_indexes(
63 source_len: usize,
64 target_len: Option<usize>,
65 offset: usize,
66) -> (usize, usize) {
67 if offset > source_len {
68 return (0, 0);
69 }
70
71 let target_len = target_len.unwrap_or(source_len - offset);
72
73 if offset + target_len > source_len {
74 return (offset, source_len);
75 }
76
77 (offset, target_len + offset)
78}
79
80pub fn get_bytes_offset_length<'js>(
81 ctx: &Ctx<'js>,
82 value: Value<'js>,
83 offset: usize,
84 length: Option<usize>,
85) -> Result<Vec<u8>> {
86 if let Some(bytes) = get_string_bytes(&value, offset, length)? {
87 return Ok(bytes);
88 }
89 if let Some(bytes) = get_array_bytes(ctx, &value, offset, length)? {
90 return Ok(bytes);
91 }
92
93 if let Some(obj) = value.as_object() {
94 if let Some((array_buffer, source_length, source_offset)) = obj_to_array_buffer(obj)? {
95 let (start, end) = get_start_end_indexes(source_length, length, offset);
96 let bytes: &[u8] = array_buffer.as_ref();
97 return Ok(bytes[start + source_offset..end - source_offset].to_vec());
98 }
99 }
100
101 if let Some(bytes) = get_coerced_string_bytes(&value, offset, length) {
102 return Ok(bytes);
103 }
104
105 Err(Exception::throw_message(
106 ctx,
107 "value must be typed DataView, Buffer, ArrayBuffer, Uint8Array or interpretable as string",
108 ))
109}
110
111pub fn get_array_bytes<'js>(
112 ctx: &Ctx<'js>,
113 value: &Value<'js>,
114 offset: usize,
115 length: Option<usize>,
116) -> Result<Option<Vec<u8>>> {
117 if value.is_array() {
118 let array = value.as_array().unwrap();
119 let (start, end) = get_start_end_indexes(array.len(), length, offset);
120 let size = end - start;
121 let mut bytes: Vec<u8> = Vec::with_capacity(size);
122
123 for val in array.iter::<u8>().skip(start).take(size) {
124 let val: u8 = val.or_throw_msg(ctx, "array value is not u8")?;
125 bytes.push(val);
126 }
127
128 return Ok(Some(bytes));
129 }
130 Ok(None)
131}
132
133pub fn get_coerced_string_bytes(
134 value: &Value<'_>,
135 offset: usize,
136 length: Option<usize>,
137) -> Option<Vec<u8>> {
138 if let Ok(val) = value.get::<Coerced<String>>() {
139 let string = val.to_string();
140 return Some(bytes_from_js_string(string, offset, length));
141 };
142 None
143}
144
145#[inline]
146pub fn get_string_bytes(
147 value: &Value<'_>,
148 offset: usize,
149 length: Option<usize>,
150) -> Result<Option<Vec<u8>>> {
151 if let Some(val) = value.as_string() {
152 let string = val.to_string()?;
153 return Ok(Some(bytes_from_js_string(string, offset, length)));
154 }
155 Ok(None)
156}
157
158fn bytes_from_js_string(string: String, offset: usize, length: Option<usize>) -> Vec<u8> {
159 let (start, end) = get_start_end_indexes(string.len(), length, offset);
160 string.as_bytes()[start..end].to_vec()
161}
162
163pub fn obj_to_array_buffer<'js>(
164 obj: &Object<'js>,
165) -> Result<Option<(ArrayBuffer<'js>, usize, usize)>> {
166 if let Ok(typed_array) = TypedArray::<u8>::from_object(obj.clone()) {
168 let byte_length = typed_array.len();
169 let offset: usize = typed_array.get("byteOffset")?;
170 return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
171 }
172 if let Some(array_buffer) = ArrayBuffer::from_object(obj.clone()) {
174 let byte_length = array_buffer.len();
175 return Ok(Some((array_buffer, byte_length, 0)));
176 }
177
178 if let Ok(typed_array) = TypedArray::<i8>::from_object(obj.clone()) {
179 let byte_length = typed_array.len();
180 let offset: usize = typed_array.get("byteOffset")?;
181 return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
182 }
183
184 if let Ok(typed_array) = TypedArray::<u16>::from_object(obj.clone()) {
185 let byte_length = typed_array.len() * 2;
186 let offset: usize = typed_array.get("byteOffset")?;
187 return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
188 }
189
190 if let Ok(typed_array) = TypedArray::<i16>::from_object(obj.clone()) {
191 let byte_length = typed_array.len() * 2;
192 let offset: usize = typed_array.get("byteOffset")?;
193 return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
194 }
195
196 if let Ok(typed_array) = TypedArray::<u32>::from_object(obj.clone()) {
197 let byte_length = typed_array.len() * 4;
198 let offset: usize = typed_array.get("byteOffset")?;
199 return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
200 }
201
202 if let Ok(typed_array) = TypedArray::<i32>::from_object(obj.clone()) {
203 let byte_length = typed_array.len() * 4;
204 let offset: usize = typed_array.get("byteOffset")?;
205 return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
206 }
207
208 if let Ok(typed_array) = TypedArray::<u64>::from_object(obj.clone()) {
209 let byte_length = typed_array.len() * 8;
210 let offset: usize = typed_array.get("byteOffset")?;
211 return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
212 }
213
214 if let Ok(typed_array) = TypedArray::<i64>::from_object(obj.clone()) {
215 let byte_length = typed_array.len() * 8;
216 let offset: usize = typed_array.get("byteOffset")?;
217 return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
218 }
219
220 if let Ok(typed_array) = TypedArray::<f32>::from_object(obj.clone()) {
221 let byte_length = typed_array.len() * 4;
222 let offset: usize = typed_array.get("byteOffset")?;
223 return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
224 }
225
226 if let Ok(typed_array) = TypedArray::<f64>::from_object(obj.clone()) {
227 let byte_length = typed_array.len() * 8;
228 let offset: usize = typed_array.get("byteOffset")?;
229 return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
230 }
231
232 if let Ok(array_buffer) = obj.get::<_, ArrayBuffer>("buffer") {
233 let length = array_buffer.len();
234 return Ok(Some((array_buffer, length, 0)));
235 }
236
237 Ok(None)
238}
239
240pub fn get_array_buffer_bytes(
241 array_buffer: ArrayBuffer<'_>,
242 start: usize,
243 end_end: usize,
244) -> Vec<u8> {
245 let bytes: &[u8] = array_buffer.as_ref();
246 bytes[start..end_end].to_vec()
247}
248
249pub fn get_bytes<'js>(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Vec<u8>> {
250 get_bytes_offset_length(ctx, value, 0, None)
251}
252
253pub fn bytes_to_typed_array<'js>(ctx: Ctx<'js>, bytes: &[u8]) -> Result<Value<'js>> {
254 TypedArray::<u8>::new(ctx.clone(), bytes).into_js(&ctx)
255}
256
257pub trait ObjectExt<'js> {
258 fn get_optional<K: IntoAtom<'js> + Clone, V: FromJs<'js>>(&self, k: K) -> Result<Option<V>>;
259}
260
261impl<'js> ObjectExt<'js> for Object<'js> {
262 fn get_optional<K: IntoAtom<'js> + Clone, V: FromJs<'js> + Sized>(
263 &self,
264 k: K,
265 ) -> Result<Option<V>> {
266 self.get::<K, Option<V>>(k)
267 }
268}
269
270impl<'js> ObjectExt<'js> for Value<'js> {
271 fn get_optional<K: IntoAtom<'js> + Clone, V: FromJs<'js>>(&self, k: K) -> Result<Option<V>> {
272 if let Some(obj) = self.as_object() {
273 return obj.get_optional(k);
274 }
275 Ok(None)
276 }
277}
278
279pub trait CreateSymbol<'js> {
280 fn for_description(globals: &Object<'js>, description: &'static str) -> Result<Symbol<'js>>;
281}
282
283impl<'js> CreateSymbol<'js> for Symbol<'js> {
284 fn for_description(globals: &Object<'js>, description: &'static str) -> Result<Symbol<'js>> {
285 let symbol_function: Function = globals.get(PredefinedAtom::Symbol)?;
286 let for_function: Function = symbol_function.get(PredefinedAtom::For)?;
287 for_function.call((description,))
288 }
289}