quickjs_runtime/quickjs_utils/maps.rs
1//! Map utils, these methods can be used to manage Map objects from rust
2//! see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) for more on Maps
3
4use crate::jsutils::JsError;
5#[cfg(feature = "bellard")]
6use crate::quickjs_utils::class_ids::JS_CLASS_MAP;
7use crate::quickjs_utils::objects::construct_object;
8use crate::quickjs_utils::{arrays, functions, get_constructor, iterators, objects, primitives};
9use crate::quickjsrealmadapter::QuickJsRealmAdapter;
10use crate::quickjsvalueadapter::QuickJsValueAdapter;
11use libquickjs_sys as q;
12#[cfg(feature = "bellard")]
13use libquickjs_sys::JS_GetClassID;
14#[cfg(feature = "quickjs-ng")]
15use libquickjs_sys::JS_IsMap;
16
17/// create new instance of Map
18/// # Example
19/// ```rust
20/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
21/// use quickjs_runtime::quickjs_utils::maps::new_map_q;
22/// use quickjs_runtime::quickjsvalueadapter::QuickJsValueAdapter;
23///
24/// let rt = QuickJsRuntimeBuilder::new().build();
25/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
26/// let q_ctx = q_js_rt.get_main_realm();
27/// let my_map: QuickJsValueAdapter = new_map_q(q_ctx).ok().unwrap();
28/// });
29/// ```
30pub fn new_map_q(q_ctx: &QuickJsRealmAdapter) -> Result<QuickJsValueAdapter, JsError> {
31 unsafe { new_map(q_ctx.context) }
32}
33
34/// create new instance of Map
35/// # Safety
36/// please ensure the passed JSContext is still valid
37pub unsafe fn new_map(ctx: *mut q::JSContext) -> Result<QuickJsValueAdapter, JsError> {
38 let map_constructor = get_constructor(ctx, "Map")?;
39 construct_object(ctx, &map_constructor, &[])
40}
41
42/// see if a JSValueRef is an instance of Map
43pub fn is_map_q(q_ctx: &QuickJsRealmAdapter, obj: &QuickJsValueAdapter) -> bool {
44 unsafe { is_map(q_ctx.context, obj) }
45}
46
47/// see if a JSValueRef is an instance of Map
48/// # Safety
49/// please ensure the passed JSContext is still valid
50#[allow(unused_variables)]
51pub unsafe fn is_map(ctx: *mut q::JSContext, obj: &QuickJsValueAdapter) -> bool {
52 #[cfg(feature = "bellard")]
53 {
54 JS_GetClassID(*obj.borrow_value()) == JS_CLASS_MAP
55 }
56 #[cfg(feature = "quickjs-ng")]
57 {
58 JS_IsMap(*obj.borrow_value())
59 }
60}
61
62/// set a key/value pair in a Map
63/// # Example
64/// ```rust
65/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
66/// use quickjs_runtime::quickjs_utils::maps::{new_map_q, set_q};
67/// use quickjs_runtime::quickjsvalueadapter::QuickJsValueAdapter;
68/// use quickjs_runtime::quickjs_utils::primitives;
69///
70/// let rt = QuickJsRuntimeBuilder::new().build();
71/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
72/// let q_ctx = q_js_rt.get_main_realm();
73/// let my_map: QuickJsValueAdapter = new_map_q(q_ctx).ok().unwrap();
74/// let key = primitives::from_i32(12);
75/// let value = primitives::from_i32(23);
76/// set_q(q_ctx, &my_map, key, value).ok().unwrap();
77/// });
78/// ```
79pub fn set_q(
80 q_ctx: &QuickJsRealmAdapter,
81 map: &QuickJsValueAdapter,
82 key: QuickJsValueAdapter,
83 val: QuickJsValueAdapter,
84) -> Result<QuickJsValueAdapter, JsError> {
85 unsafe { set(q_ctx.context, map, key, val) }
86}
87
88/// set a key/value pair in a Map
89/// # Safety
90/// please ensure the passed JSContext is still valid
91pub unsafe fn set(
92 ctx: *mut q::JSContext,
93 map: &QuickJsValueAdapter,
94 key: QuickJsValueAdapter,
95 val: QuickJsValueAdapter,
96) -> Result<QuickJsValueAdapter, JsError> {
97 functions::invoke_member_function(ctx, map, "set", &[key, val])
98}
99
100/// get a value from a map by key
101/// # Example
102/// ```rust
103/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
104/// use quickjs_runtime::quickjs_utils::maps::{new_map_q, get_q, set_q};
105/// use quickjs_runtime::quickjsvalueadapter::QuickJsValueAdapter;
106/// use quickjs_runtime::quickjs_utils::primitives;
107///
108/// let rt = QuickJsRuntimeBuilder::new().build();
109/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
110/// let q_ctx = q_js_rt.get_main_realm();
111/// let my_map: QuickJsValueAdapter = new_map_q(q_ctx).ok().unwrap();
112/// let key = primitives::from_i32(12);
113/// let value = primitives::from_i32(23);
114/// set_q(q_ctx, &my_map, key.clone(), value).ok().unwrap();
115/// let val_res = get_q(q_ctx, &my_map, key).ok().unwrap();
116/// assert_eq!(primitives::to_i32(&val_res).ok().unwrap(), 23);
117/// });
118/// ```
119pub fn get_q(
120 q_ctx: &QuickJsRealmAdapter,
121 map: &QuickJsValueAdapter,
122 key: QuickJsValueAdapter,
123) -> Result<QuickJsValueAdapter, JsError> {
124 unsafe { get(q_ctx.context, map, key) }
125}
126
127/// get a value from a map by key
128/// # Safety
129/// please ensure the passed JSContext is still valid
130pub unsafe fn get(
131 ctx: *mut q::JSContext,
132 map: &QuickJsValueAdapter,
133 key: QuickJsValueAdapter,
134) -> Result<QuickJsValueAdapter, JsError> {
135 functions::invoke_member_function(ctx, map, "get", &[key])
136}
137
138/// delete a value from a map by key
139/// # Example
140/// ```rust
141/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
142/// use quickjs_runtime::quickjs_utils::maps::{new_map_q, set_q, delete_q};
143/// use quickjs_runtime::quickjsvalueadapter::QuickJsValueAdapter;
144/// use quickjs_runtime::quickjs_utils::primitives;
145///
146/// let rt = QuickJsRuntimeBuilder::new().build();
147/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
148/// let q_ctx = q_js_rt.get_main_realm();
149/// let my_map: QuickJsValueAdapter = new_map_q(q_ctx).ok().unwrap();
150/// let key = primitives::from_i32(12);
151/// let value = primitives::from_i32(23);
152/// set_q(q_ctx, &my_map, key.clone(), value).ok().unwrap();
153/// delete_q(q_ctx, &my_map, key).ok().unwrap();
154/// });
155/// ```
156pub fn delete_q(
157 q_ctx: &QuickJsRealmAdapter,
158 map: &QuickJsValueAdapter,
159 key: QuickJsValueAdapter,
160) -> Result<bool, JsError> {
161 unsafe { delete(q_ctx.context, map, key) }
162}
163
164/// delete a value from a map by key
165/// # Safety
166/// please ensure the passed JSContext is still valid
167pub unsafe fn delete(
168 ctx: *mut q::JSContext,
169 map: &QuickJsValueAdapter,
170 key: QuickJsValueAdapter,
171) -> Result<bool, JsError> {
172 let res = functions::invoke_member_function(ctx, map, "delete", &[key])?;
173 primitives::to_bool(&res)
174}
175
176/// check whether a Map has a value for a key
177/// # Example
178/// ```rust
179/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
180/// use quickjs_runtime::quickjs_utils::maps::{new_map_q, set_q, has_q};
181/// use quickjs_runtime::quickjsvalueadapter::QuickJsValueAdapter;
182/// use quickjs_runtime::quickjs_utils::primitives;
183///
184/// let rt = QuickJsRuntimeBuilder::new().build();
185/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
186/// let q_ctx = q_js_rt.get_main_realm();
187/// let my_map: QuickJsValueAdapter = new_map_q(q_ctx).ok().unwrap();
188/// let key = primitives::from_i32(12);
189/// let value = primitives::from_i32(23);
190/// set_q(q_ctx, &my_map, key.clone(), value).ok().unwrap();
191/// let bln_has = has_q(q_ctx, &my_map, key).ok().unwrap();
192/// assert!(bln_has);
193/// });
194/// ```
195pub fn has_q(
196 q_ctx: &QuickJsRealmAdapter,
197 map: &QuickJsValueAdapter,
198 key: QuickJsValueAdapter,
199) -> Result<bool, JsError> {
200 unsafe { has(q_ctx.context, map, key) }
201}
202
203/// check whether a Map has a value for a key
204/// # Safety
205/// please ensure the passed JSContext is still valid
206pub unsafe fn has(
207 ctx: *mut q::JSContext,
208 map: &QuickJsValueAdapter,
209 key: QuickJsValueAdapter,
210) -> Result<bool, JsError> {
211 let res = functions::invoke_member_function(ctx, map, "has", &[key])?;
212 primitives::to_bool(&res)
213}
214
215/// get the number of entries in a map
216/// # Example
217/// ```rust
218/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
219/// use quickjs_runtime::quickjs_utils::maps::{new_map_q, set_q, size_q};
220/// use quickjs_runtime::quickjsvalueadapter::QuickJsValueAdapter;
221/// use quickjs_runtime::quickjs_utils::primitives;
222///
223/// let rt = QuickJsRuntimeBuilder::new().build();
224/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
225/// let q_ctx = q_js_rt.get_main_realm();
226/// let my_map: QuickJsValueAdapter = new_map_q(q_ctx).ok().unwrap();
227/// let key = primitives::from_i32(12);
228/// let value = primitives::from_i32(23);
229/// set_q(q_ctx, &my_map, key.clone(), value).ok().unwrap();
230/// let i_size = size_q(q_ctx, &my_map).ok().unwrap();
231/// assert_eq!(i_size, 1);
232/// });
233/// ```
234pub fn size_q(q_ctx: &QuickJsRealmAdapter, map: &QuickJsValueAdapter) -> Result<i32, JsError> {
235 unsafe { size(q_ctx.context, map) }
236}
237
238/// get the number of entries in a map
239/// # Safety
240/// please ensure the passed JSContext is still valid
241pub unsafe fn size(ctx: *mut q::JSContext, map: &QuickJsValueAdapter) -> Result<i32, JsError> {
242 let res = objects::get_property(ctx, map, "size")?;
243 primitives::to_i32(&res)
244}
245
246/// remove all entries from a map
247/// # Example
248/// ```rust
249/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
250/// use quickjs_runtime::quickjs_utils::maps::{new_map_q, set_q, clear_q, size_q};
251/// use quickjs_runtime::quickjsvalueadapter::QuickJsValueAdapter;
252/// use quickjs_runtime::quickjs_utils::primitives;
253///
254/// let rt = QuickJsRuntimeBuilder::new().build();
255/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
256/// let q_ctx = q_js_rt.get_main_realm();
257/// let my_map: QuickJsValueAdapter = new_map_q(q_ctx).ok().unwrap();
258/// let key = primitives::from_i32(12);
259/// let value = primitives::from_i32(23);
260/// set_q(q_ctx, &my_map, key.clone(), value).ok().unwrap();
261/// clear_q(q_ctx, &my_map).ok().unwrap();
262/// let i_size = size_q(q_ctx, &my_map).ok().unwrap();
263/// assert_eq!(i_size, 0);
264/// });
265/// ```
266pub fn clear_q(q_ctx: &QuickJsRealmAdapter, map: &QuickJsValueAdapter) -> Result<(), JsError> {
267 unsafe { clear(q_ctx.context, map) }
268}
269
270/// remove all entries from a map
271/// # Safety
272/// please ensure the passed JSContext is still valid
273pub unsafe fn clear(ctx: *mut q::JSContext, map: &QuickJsValueAdapter) -> Result<(), JsError> {
274 let _ = functions::invoke_member_function(ctx, map, "clear", &[])?;
275 Ok(())
276}
277
278/// iterate over all keys of a map
279/// # Example
280/// ```rust
281/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
282/// use quickjs_runtime::quickjs_utils::maps::{new_map_q, set_q, keys_q};
283/// use quickjs_runtime::quickjsvalueadapter::QuickJsValueAdapter;
284/// use quickjs_runtime::quickjs_utils::primitives;
285///
286/// let rt = QuickJsRuntimeBuilder::new().build();
287/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
288/// let q_ctx = q_js_rt.get_main_realm();
289/// let my_map: QuickJsValueAdapter = new_map_q(q_ctx).ok().unwrap();
290/// let key = primitives::from_i32(12);
291/// let value = primitives::from_i32(23);
292/// set_q(q_ctx, &my_map, key, value).ok().unwrap();
293/// let mapped_keys = keys_q(q_ctx, &my_map, |key| {Ok(123)}).ok().unwrap();
294/// assert_eq!(mapped_keys.len(), 1);
295/// });
296/// ```
297pub fn keys_q<C: Fn(QuickJsValueAdapter) -> Result<R, JsError>, R>(
298 q_ctx: &QuickJsRealmAdapter,
299 map: &QuickJsValueAdapter,
300 consumer_producer: C,
301) -> Result<Vec<R>, JsError> {
302 unsafe { keys(q_ctx.context, map, consumer_producer) }
303}
304
305/// iterate over all keys of a map
306/// # Safety
307/// please ensure the passed JSContext is still valid
308pub unsafe fn keys<C: Fn(QuickJsValueAdapter) -> Result<R, JsError>, R>(
309 ctx: *mut q::JSContext,
310 map: &QuickJsValueAdapter,
311 consumer_producer: C,
312) -> Result<Vec<R>, JsError> {
313 let iter_ref = functions::invoke_member_function(ctx, map, "keys", &[])?;
314
315 iterators::iterate(ctx, &iter_ref, consumer_producer)
316}
317
318/// iterate over all values of a map
319/// # Example
320/// ```rust
321/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
322/// use quickjs_runtime::quickjs_utils::maps::{new_map_q, set_q, values_q};
323/// use quickjs_runtime::quickjsvalueadapter::QuickJsValueAdapter;
324/// use quickjs_runtime::quickjs_utils::primitives;
325///
326/// let rt = QuickJsRuntimeBuilder::new().build();
327/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
328/// let q_ctx = q_js_rt.get_main_realm();
329/// let my_map: QuickJsValueAdapter = new_map_q(q_ctx).ok().unwrap();
330/// let key = primitives::from_i32(12);
331/// let value = primitives::from_i32(23);
332/// set_q(q_ctx, &my_map, key, value).ok().unwrap();
333/// let mapped_values = values_q(q_ctx, &my_map, |value| {Ok(123)}).ok().unwrap();
334/// assert_eq!(mapped_values.len(), 1);
335/// });
336/// ```
337pub fn values_q<C: Fn(QuickJsValueAdapter) -> Result<R, JsError>, R>(
338 q_ctx: &QuickJsRealmAdapter,
339 map: &QuickJsValueAdapter,
340 consumer_producer: C,
341) -> Result<Vec<R>, JsError> {
342 unsafe { values(q_ctx.context, map, consumer_producer) }
343}
344
345/// iterate over all values of a map
346/// # Safety
347/// please ensure the passed JSContext is still valid
348pub unsafe fn values<C: Fn(QuickJsValueAdapter) -> Result<R, JsError>, R>(
349 ctx: *mut q::JSContext,
350 map: &QuickJsValueAdapter,
351 consumer_producer: C,
352) -> Result<Vec<R>, JsError> {
353 let iter_ref = functions::invoke_member_function(ctx, map, "values", &[])?;
354
355 iterators::iterate(ctx, &iter_ref, consumer_producer)
356}
357
358/// iterate over all entries of a map
359/// # Example
360/// ```rust
361/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
362/// use quickjs_runtime::quickjs_utils::maps::{new_map_q, set_q, entries_q};
363/// use quickjs_runtime::quickjsvalueadapter::QuickJsValueAdapter;
364/// use quickjs_runtime::quickjs_utils::primitives;
365///
366/// let rt = QuickJsRuntimeBuilder::new().build();
367/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
368/// let q_ctx = q_js_rt.get_main_realm();
369/// let my_map: QuickJsValueAdapter = new_map_q(q_ctx).ok().unwrap();
370/// let key = primitives::from_i32(12);
371/// let value = primitives::from_i32(23);
372/// set_q(q_ctx, &my_map, key, value).ok().unwrap();
373/// let mapped_values = entries_q(q_ctx, &my_map, |key, value| {Ok(123)}).ok().unwrap();
374/// assert_eq!(mapped_values.len(), 1);
375/// });
376/// ```
377pub fn entries_q<C: Fn(QuickJsValueAdapter, QuickJsValueAdapter) -> Result<R, JsError>, R>(
378 q_ctx: &QuickJsRealmAdapter,
379 map: &QuickJsValueAdapter,
380 consumer_producer: C,
381) -> Result<Vec<R>, JsError> {
382 unsafe { entries(q_ctx.context, map, consumer_producer) }
383}
384
385/// iterate over all entries of a map
386/// # Safety
387/// please ensure the passed JSContext is still valid
388pub unsafe fn entries<C: Fn(QuickJsValueAdapter, QuickJsValueAdapter) -> Result<R, JsError>, R>(
389 ctx: *mut q::JSContext,
390 map: &QuickJsValueAdapter,
391 consumer_producer: C,
392) -> Result<Vec<R>, JsError> {
393 let iter_ref = functions::invoke_member_function(ctx, map, "entries", &[])?;
394
395 iterators::iterate(ctx, &iter_ref, |arr_ref| {
396 let key = arrays::get_element(ctx, &arr_ref, 0)?;
397 let value = arrays::get_element(ctx, &arr_ref, 1)?;
398 consumer_producer(key, value)
399 })
400}