@@ -11,6 +11,7 @@ use glide_core::{
1111} ;
1212use std:: {
1313 ffi:: { CStr , CString , c_char, c_void} ,
14+ slice,
1415 sync:: Arc ,
1516} ;
1617use tokio:: runtime:: { Builder , Runtime } ;
@@ -439,29 +440,160 @@ pub unsafe extern "C" fn init(level: Option<Level>, file_name: *const c_char) ->
439440 logger_level. into ( )
440441}
441442
442- /// Stub implementation for cluster scan request.
443+ /// Execute a cluster scan request.
443444///
444445/// # Safety
445- /// * All pointer parameters must be valid for the duration of the call.
446+ /// * `client_ptr` must be a valid Client pointer from create_client
447+ /// * `cursor` must be a valid C string
448+ /// * `args` and `arg_lengths` must be valid arrays of length `arg_count`
449+ /// * `args` array format: alternating parameter names and values (e.g., [b"MATCH", pattern, b"COUNT", count_str])
446450#[ unsafe( no_mangle) ]
447451pub unsafe extern "C-unwind" fn request_cluster_scan (
448- _client : * const c_void ,
449- _index : usize ,
450- _cursor : * const c_char ,
451- _arg_count : u64 ,
452- _args : * const usize ,
453- _arg_lengths : * const u64 ,
452+ client_ptr : * const c_void ,
453+ callback_index : usize ,
454+ cursor : * const c_char ,
455+ arg_count : u64 ,
456+ args : * const usize ,
457+ arg_lengths : * const u64 ,
454458) {
455- // Stub implementation - always fails
456- panic ! ( "request_cluster_scan not implemented" ) ;
459+ let client = unsafe {
460+ Arc :: increment_strong_count ( client_ptr) ;
461+ Arc :: from_raw ( client_ptr as * mut Client )
462+ } ;
463+ let core = client. core . clone ( ) ;
464+
465+ let mut panic_guard = PanicGuard {
466+ panicked : true ,
467+ failure_callback : core. failure_callback ,
468+ callback_index,
469+ } ;
470+
471+ let cursor_id = unsafe { CStr :: from_ptr ( cursor) }
472+ . to_str ( )
473+ . unwrap_or ( "0" )
474+ . to_owned ( ) ;
475+
476+ let cluster_scan_args = unsafe { parse_cluster_scan_args ( args, arg_lengths, arg_count) } ;
477+
478+ let scan_state_cursor =
479+ match glide_core:: cluster_scan_container:: get_cluster_scan_cursor ( cursor_id) {
480+ Ok ( existing_cursor) => existing_cursor,
481+ Err ( _error) => redis:: ScanStateRC :: new ( ) ,
482+ } ;
483+
484+ client. runtime . spawn ( async move {
485+ let mut panic_guard = PanicGuard {
486+ panicked : true ,
487+ failure_callback : core. failure_callback ,
488+ callback_index,
489+ } ;
490+
491+ let result = core
492+ . client
493+ . clone ( )
494+ . cluster_scan ( & scan_state_cursor, cluster_scan_args)
495+ . await ;
496+ match result {
497+ Ok ( value) => {
498+ let ptr = Box :: into_raw ( Box :: new ( ResponseValue :: from_value ( value) ) ) ;
499+ unsafe { ( core. success_callback ) ( callback_index, ptr) } ;
500+ }
501+ Err ( err) => unsafe {
502+ report_error (
503+ core. failure_callback ,
504+ callback_index,
505+ error_message ( & err) ,
506+ error_type ( & err) ,
507+ ) ;
508+ } ,
509+ } ;
510+ panic_guard. panicked = false ;
511+ } ) ;
512+
513+ panic_guard. panicked = false ;
457514}
458515
459- /// Stub implementation for removing cluster scan cursor.
516+ /// Remove a cluster scan cursor from the Rust core container.
517+ ///
518+ /// This should be called when the C# ClusterScanCursor is disposed or finalized
519+ /// to clean up resources allocated by the Rust core for cluster scan operations.
460520///
461521/// # Safety
462- /// * `cursor_id` must point to a valid C string.
522+ /// * `cursor_id` must be a valid C string or null
463523#[ unsafe( no_mangle) ]
464- pub unsafe extern "C" fn remove_cluster_scan_cursor ( _cursor_id : * const c_char ) {
465- // Stub implementation - always fails
466- panic ! ( "remove_cluster_scan_cursor not implemented" ) ;
524+ pub unsafe extern "C" fn remove_cluster_scan_cursor ( cursor_id : * const c_char ) {
525+ if cursor_id. is_null ( ) {
526+ return ;
527+ }
528+
529+ if let Ok ( cursor_str) = unsafe { CStr :: from_ptr ( cursor_id) . to_str ( ) } {
530+ glide_core:: cluster_scan_container:: remove_scan_state_cursor ( cursor_str. to_string ( ) ) ;
531+ }
532+ }
533+
534+ /// Parse cluster scan arguments from C-style arrays.
535+ ///
536+ /// # Safety
537+ /// * `args` and `arg_lengths` must be valid arrays of length `arg_count`
538+ /// * Each pointer in `args` must point to valid memory of the corresponding length
539+ unsafe fn parse_cluster_scan_args (
540+ args : * const usize ,
541+ arg_lengths : * const u64 ,
542+ arg_count : u64 ,
543+ ) -> redis:: ClusterScanArgs {
544+ if arg_count == 0 {
545+ return redis:: ClusterScanArgs :: builder ( ) . build ( ) ;
546+ }
547+
548+ let mut pattern: Option < & [ u8 ] > = None ;
549+ let mut object_type: Option < & [ u8 ] > = None ;
550+ let mut count: Option < & [ u8 ] > = None ;
551+
552+ let mut i = 0 ;
553+ while i < arg_count as usize {
554+ let arg_ptr = unsafe { * args. add ( i) as * const u8 } ;
555+ let arg_len = unsafe { * arg_lengths. add ( i) as usize } ;
556+ let arg = unsafe { slice:: from_raw_parts ( arg_ptr, arg_len) } ;
557+
558+ match arg {
559+ b"MATCH" if i + 1 < arg_count as usize => {
560+ i += 1 ;
561+ let pattern_ptr = unsafe { * args. add ( i) as * const u8 } ;
562+ let pattern_len = unsafe { * arg_lengths. add ( i) as usize } ;
563+ pattern = Some ( unsafe { slice:: from_raw_parts ( pattern_ptr, pattern_len) } ) ;
564+ }
565+ b"TYPE" if i + 1 < arg_count as usize => {
566+ i += 1 ;
567+ let type_ptr = unsafe { * args. add ( i) as * const u8 } ;
568+ let type_len = unsafe { * arg_lengths. add ( i) as usize } ;
569+ object_type = Some ( unsafe { slice:: from_raw_parts ( type_ptr, type_len) } ) ;
570+ }
571+ b"COUNT" if i + 1 < arg_count as usize => {
572+ i += 1 ;
573+ let count_ptr = unsafe { * args. add ( i) as * const u8 } ;
574+ let count_len = unsafe { * arg_lengths. add ( i) as usize } ;
575+ count = Some ( unsafe { slice:: from_raw_parts ( count_ptr, count_len) } ) ;
576+ }
577+ _ => { }
578+ }
579+ i += 1 ;
580+ }
581+
582+ let mut builder = redis:: ClusterScanArgs :: builder ( ) ;
583+ if let Some ( pattern) = pattern {
584+ builder = builder. with_match_pattern ( pattern) ;
585+ }
586+ if let Some ( count_bytes) = count {
587+ if let Ok ( count_str) = std:: str:: from_utf8 ( count_bytes) {
588+ if let Ok ( count_val) = count_str. parse :: < u32 > ( ) {
589+ builder = builder. with_count ( count_val) ;
590+ }
591+ }
592+ }
593+ if let Some ( type_bytes) = object_type {
594+ if let Ok ( type_str) = std:: str:: from_utf8 ( type_bytes) {
595+ builder = builder. with_object_type ( redis:: ObjectType :: from ( type_str. to_string ( ) ) ) ;
596+ }
597+ }
598+ builder. build ( )
467599}
0 commit comments