@@ -340,6 +340,30 @@ pub unsafe extern "C" fn get_from_string_map(
340340 . and_then ( |v| allocate_fn ( kernel_string_slice ! ( v) ) )
341341}
342342
343+ /// Visit all values in a CStringMap. The callback will be called once for each element of the map
344+ ///
345+ /// # Safety
346+ ///
347+ /// The engine is responsible for providing a valid [`CStringMap`] pointer and callback
348+ #[ no_mangle]
349+ pub unsafe extern "C" fn visit_string_map (
350+ map : & CStringMap ,
351+ engine_context : NullableCvoid ,
352+ visitor : extern "C" fn (
353+ engine_context : NullableCvoid ,
354+ key : KernelStringSlice ,
355+ value : KernelStringSlice ,
356+ ) ,
357+ ) {
358+ for ( key, val) in map. values . iter ( ) {
359+ visitor (
360+ engine_context,
361+ kernel_string_slice ! ( key) ,
362+ kernel_string_slice ! ( val) ,
363+ ) ;
364+ }
365+ }
366+
343367/// Transformation expressions that need to be applied to each row `i` in ScanMetadata. You can use
344368/// [`get_transform_for_row`] to get the transform for a particular row. If that returns an
345369/// associated expression, it _must_ be applied to the data read from the file specified by the
@@ -487,3 +511,42 @@ pub unsafe extern "C" fn visit_scan_metadata(
487511 . visit_scan_files ( context_wrapper, rust_callback)
488512 . unwrap ( ) ;
489513}
514+
515+ #[ cfg( test) ]
516+ mod tests {
517+ use std:: { collections:: HashMap , ptr:: NonNull } ;
518+
519+ use crate :: { KernelStringSlice , NullableCvoid , TryFromStringSlice } ;
520+
521+ extern "C" fn visit_entry (
522+ engine_context : NullableCvoid ,
523+ key : KernelStringSlice ,
524+ value : KernelStringSlice ,
525+ ) {
526+ let map_ptr: * mut HashMap < String , String > = engine_context. unwrap ( ) . as_ptr ( ) . cast ( ) ;
527+ let key = unsafe { String :: try_from_slice ( & key) . unwrap ( ) } ;
528+ let value = unsafe { String :: try_from_slice ( & value) . unwrap ( ) } ;
529+ unsafe {
530+ ( * map_ptr) . insert ( key, value) ;
531+ }
532+ }
533+
534+ #[ test]
535+ fn visit_string_map ( ) {
536+ let test_map: HashMap < String , String > = HashMap :: from ( [
537+ ( "A" . into ( ) , "B" . into ( ) ) ,
538+ ( "C" . into ( ) , "D" . into ( ) ) ,
539+ ( "E" . into ( ) , "F" . into ( ) ) ,
540+ ( "G" . into ( ) , "H" . into ( ) ) ,
541+ ] ) ;
542+ let cmap: super :: CStringMap = test_map. clone ( ) . into ( ) ;
543+ let context_map: Box < HashMap < String , String > > = Box :: default ( ) ;
544+ let map_ptr: * mut HashMap < String , String > = Box :: into_raw ( context_map) ;
545+ unsafe {
546+ let ptr = NonNull :: new_unchecked ( map_ptr. cast ( ) ) ;
547+ super :: visit_string_map ( & cmap, Some ( ptr) , visit_entry) ;
548+ }
549+ let final_map: HashMap < String , String > = * unsafe { Box :: from_raw ( map_ptr) } ;
550+ assert_eq ! ( test_map, final_map) ;
551+ }
552+ }
0 commit comments