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}