Skip to content

Commit d1a226b

Browse files
committed
Initial implementation of request_cluster_scan and remove_cluster_scan_cursor.
Signed-off-by: currantw <[email protected]>
1 parent 85e3c3a commit d1a226b

File tree

1 file changed

+147
-15
lines changed

1 file changed

+147
-15
lines changed

rust/src/lib.rs

Lines changed: 147 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use glide_core::{
1111
};
1212
use std::{
1313
ffi::{CStr, CString, c_char, c_void},
14+
slice,
1415
sync::Arc,
1516
};
1617
use 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)]
447451
pub 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

Comments
 (0)