#![allow(unused_unsafe)] use std::{collections::HashMap, env::current_dir, path::PathBuf}; use crate::chunker::{ context::{ButckContext as RustButckContext, ButckMethod}, rw::storage::hash::ChunkWriteHash, }; /// Helper macro to convert C string pointer to Option /// Returns None if pointer is null or conversion fails macro_rules! cstr_to_opt_string { ($ptr:expr) => { if $ptr.is_null() { None } else { unsafe { std::ffi::CStr::from_ptr($ptr) .to_str() .ok() .map(|s| s.to_string()) } } }; } /// Helper macro to convert C string pointer to PathBuf /// Returns empty PathBuf if pointer is null or conversion fails macro_rules! cstr_to_path { ($ptr:expr) => { if $ptr.is_null() { PathBuf::new() } else { unsafe { std::ffi::CStr::from_ptr($ptr) .to_str() .map(PathBuf::from) .unwrap_or_default() } } }; } /// Helper macro to convert C string pointer to PathBuf with fallback to current directory /// Returns current directory if pointer is null or conversion fails macro_rules! cstr_to_path_or_current_dir { ($ptr:expr) => { if $ptr.is_null() { current_dir().unwrap() } else { unsafe { std::ffi::CStr::from_ptr($ptr) .to_str() .map(PathBuf::from) .unwrap_or_else(|_| current_dir().unwrap()) } } }; } /// FFI representation of ButckContext for C interoperability /// This struct mirrors ButckContext but uses C-compatible types #[repr(C)] pub struct ButckContext { /// Operation mode (0=None, 1=Write, 2=Build) method: u8, /// Array of input file paths (C strings) file_paths: *mut *mut std::os::raw::c_char, /// Number of file paths in the array file_paths_len: usize, /// Storage repository path (C string) storage_path: *mut std::os::raw::c_char, /// Display chunk boundaries without executing chunking/building logic display_boundaries: bool, /// Stream reading size (if stream_read_set is true) stream_read: u32, /// Whether stream_read is set stream_read_set: bool, /// Use memory mapping for file reading memmap_read: bool, /// Registration name for bidx files (C string) register_name: *mut std::os::raw::c_char, /// Chunking policy name (C string) policy_name: *mut std::os::raw::c_char, /// Chunk hash algorithm (0=Blake3, 1=SHA256) chunk_hash: u8, /// Output directory path (C string) output_dir: *mut std::os::raw::c_char, /// Output file name (C string) output_file: *mut std::os::raw::c_char, /// Parameter keys array (C strings) params_keys: *mut *mut std::os::raw::c_char, /// Parameter values array (C strings) params_values: *mut *mut std::os::raw::c_char, /// Number of parameters in the arrays params_len: usize, } /// Create a new ButckContext for write operation /// /// # Arguments /// * `files` - Array of C string pointers to input files /// * `files_len` - Number of files in the array /// * `storage` - C string pointer to storage repository path /// /// # Returns /// * Pointer to newly created ButckContext #[unsafe(no_mangle)] #[allow(nonstandard_style)] pub extern "C" fn Butck_Write( files: *mut *mut std::os::raw::c_char, files_len: usize, storage: *mut std::os::raw::c_char, ) -> *mut ButckContext { let mut file_paths = Vec::new(); if !files.is_null() && files_len > 0 { for i in 0..files_len { unsafe { let ptr = *files.add(i); if !ptr.is_null() { let c_str = std::ffi::CStr::from_ptr(ptr); if let Ok(path_str) = c_str.to_str() { file_paths.push(PathBuf::from(path_str)); } } } } } let storage_path = if !storage.is_null() { unsafe { let c_str = std::ffi::CStr::from_ptr(storage); if let Ok(path_str) = c_str.to_str() { PathBuf::from(path_str) } else { PathBuf::new() } } } else { PathBuf::new() }; let ctx = RustButckContext::default() .with_file_paths(file_paths) .with_storage_path(storage_path) .with_write_mode(); Box::into_raw(Box::new(ButckContext::from(ctx))) } /// Create a new ButckContext for build operation /// /// # Arguments /// * `index_files` - Array of C string pointers to *.bidx files /// * `index_files_len` - Number of index files in the array /// * `storage` - C string pointer to storage repository path /// /// # Returns /// * Pointer to newly created ButckContext #[unsafe(no_mangle)] #[allow(nonstandard_style)] pub extern "C" fn Butck_Build( index_files: *mut *mut std::os::raw::c_char, index_files_len: usize, storage: *mut std::os::raw::c_char, ) -> *mut ButckContext { let mut file_paths = Vec::new(); if !index_files.is_null() && index_files_len > 0 { for i in 0..index_files_len { unsafe { let ptr = *index_files.add(i); if !ptr.is_null() { let c_str = std::ffi::CStr::from_ptr(ptr); if let Ok(path_str) = c_str.to_str() { file_paths.push(PathBuf::from(path_str)); } } } } } let storage_path = if !storage.is_null() { unsafe { let c_str = std::ffi::CStr::from_ptr(storage); if let Ok(path_str) = c_str.to_str() { PathBuf::from(path_str) } else { PathBuf::new() } } } else { PathBuf::new() }; let ctx = RustButckContext::default() .with_file_paths(file_paths) .with_storage_path(storage_path) .with_build_mode(); Box::into_raw(Box::new(ButckContext::from(ctx))) } /// Execute the operation specified in the context /// /// # Arguments /// * `ctx` - Pointer to ButckContext /// /// # Returns /// * true if execution succeeded, false otherwise /// /// # Note /// The context pointer is consumed and freed during execution. /// Callers must not free it afterward. #[unsafe(no_mangle)] #[allow(nonstandard_style)] pub extern "C" fn Butck_Exec(ctx: *mut ButckContext) -> bool { if ctx.is_null() { return false; } unsafe { let ffi_ctx = Box::from_raw(ctx); let butck_ctx = RustButckContext::from(*ffi_ctx); // ffi_ctx is consumed by the dereference above, Box will be dropped automatically let runtime = tokio::runtime::Runtime::new().unwrap(); match runtime.block_on(butck_ctx.exec()) { Ok(_) => true, Err(_) => false, } } } /// Set storage repository path /// /// # Arguments /// * `ctx` - Pointer to ButckContext /// * `storage` - C string pointer to storage repository path /// /// # Returns /// * Pointer to updated ButckContext #[unsafe(no_mangle)] #[allow(nonstandard_style)] pub extern "C" fn Butck_WithStoragePath( ctx: *mut ButckContext, storage: *mut std::os::raw::c_char, ) -> *mut ButckContext { if ctx.is_null() { return std::ptr::null_mut(); } unsafe { let ffi_ctx = Box::from_raw(ctx); let mut butck_ctx = RustButckContext::from(*ffi_ctx); let storage_path = cstr_to_path!(storage); butck_ctx = butck_ctx.with_storage_path(storage_path); Box::into_raw(Box::new(ButckContext::from(butck_ctx))) } } /// Set display boundaries flag /// /// # Arguments /// * `ctx` - Pointer to ButckContext /// * `display` - Whether to display chunk boundaries /// /// # Returns /// * Pointer to updated ButckContext #[unsafe(no_mangle)] #[allow(nonstandard_style)] pub extern "C" fn Butck_WithDisplayBoundaries( ctx: *mut ButckContext, display: bool, ) -> *mut ButckContext { if ctx.is_null() { return std::ptr::null_mut(); } unsafe { let ffi_ctx = Box::from_raw(ctx); let mut butck_ctx = RustButckContext::from(*ffi_ctx); butck_ctx = butck_ctx.with_display_boundaries(display); Box::into_raw(Box::new(ButckContext::from(butck_ctx))) } } /// Set stream reading configuration /// /// # Arguments /// * `ctx` - Pointer to ButckContext /// * `size` - Stream reading size in bytes /// * `set` - Whether to enable stream reading /// /// # Returns /// * Pointer to updated ButckContext #[unsafe(no_mangle)] #[allow(nonstandard_style)] pub extern "C" fn Butck_WithStreamRead( ctx: *mut ButckContext, size: u32, set: bool, ) -> *mut ButckContext { if ctx.is_null() { return std::ptr::null_mut(); } unsafe { let ffi_ctx = Box::from_raw(ctx); let mut butck_ctx = RustButckContext::from(*ffi_ctx); let stream_read = if set { Some(size) } else { None }; butck_ctx = butck_ctx.with_stream_read(stream_read); Box::into_raw(Box::new(ButckContext::from(butck_ctx))) } } /// Set memory map reading flag /// /// # Arguments /// * `ctx` - Pointer to ButckContext /// * `use_memmap` - Whether to use memory mapping for file reading /// /// # Returns /// * Pointer to updated ButckContext #[unsafe(no_mangle)] #[allow(nonstandard_style)] pub extern "C" fn Butck_WithMemmapRead( ctx: *mut ButckContext, use_memmap: bool, ) -> *mut ButckContext { if ctx.is_null() { return std::ptr::null_mut(); } unsafe { let ffi_ctx = Box::from_raw(ctx); let mut butck_ctx = RustButckContext::from(*ffi_ctx); butck_ctx = butck_ctx.with_memmap_read(use_memmap); Box::into_raw(Box::new(ButckContext::from(butck_ctx))) } } /// Set registration name for bidx files /// /// # Arguments /// * `ctx` - Pointer to ButckContext /// * `name` - C string pointer to registration name /// /// # Returns /// * Pointer to updated ButckContext #[unsafe(no_mangle)] #[allow(nonstandard_style)] pub extern "C" fn Butck_WithRegisterName( ctx: *mut ButckContext, name: *mut std::os::raw::c_char, ) -> *mut ButckContext { if ctx.is_null() { return std::ptr::null_mut(); } unsafe { let ffi_ctx = Box::from_raw(ctx); let mut butck_ctx = RustButckContext::from(*ffi_ctx); let register_name = cstr_to_opt_string!(name); butck_ctx = butck_ctx.with_register_name(register_name); Box::into_raw(Box::new(ButckContext::from(butck_ctx))) } } /// Set chunking policy name /// /// # Arguments /// * `ctx` - Pointer to ButckContext /// * `name` - C string pointer to policy name /// /// # Returns /// * Pointer to updated ButckContext #[unsafe(no_mangle)] #[allow(nonstandard_style)] pub extern "C" fn Butck_WithPolicyName( ctx: *mut ButckContext, name: *mut std::os::raw::c_char, ) -> *mut ButckContext { if ctx.is_null() { return std::ptr::null_mut(); } unsafe { let ffi_ctx = Box::from_raw(ctx); let mut butck_ctx = RustButckContext::from(*ffi_ctx); let policy_name = cstr_to_opt_string!(name); butck_ctx = butck_ctx.with_policy_name(policy_name); Box::into_raw(Box::new(ButckContext::from(butck_ctx))) } } /// Set chunk hash algorithm /// /// # Arguments /// * `ctx` - Pointer to ButckContext /// * `hash` - Hash algorithm (0=Blake3, 1=SHA256) /// /// # Returns /// * Pointer to updated ButckContext #[unsafe(no_mangle)] #[allow(nonstandard_style)] pub extern "C" fn Butck_WithChunkHash(ctx: *mut ButckContext, hash: u8) -> *mut ButckContext { if ctx.is_null() { return std::ptr::null_mut(); } unsafe { let ffi_ctx = Box::from_raw(ctx); let mut butck_ctx = RustButckContext::from(*ffi_ctx); let chunk_hash = match hash { 0 => ChunkWriteHash::Blake3, 1 => ChunkWriteHash::Sha256, _ => ChunkWriteHash::default(), }; butck_ctx = butck_ctx.with_chunk_hash(chunk_hash); Box::into_raw(Box::new(ButckContext::from(butck_ctx))) } } /// Set output directory path /// /// # Arguments /// * `ctx` - Pointer to ButckContext /// * `dir` - C string pointer to output directory path /// /// # Returns /// * Pointer to updated ButckContext #[unsafe(no_mangle)] #[allow(nonstandard_style)] pub extern "C" fn Butck_WithOutputDir( ctx: *mut ButckContext, dir: *mut std::os::raw::c_char, ) -> *mut ButckContext { if ctx.is_null() { return std::ptr::null_mut(); } unsafe { let ffi_ctx = Box::from_raw(ctx); let mut butck_ctx = RustButckContext::from(*ffi_ctx); let output_dir = cstr_to_path_or_current_dir!(dir); butck_ctx = butck_ctx.with_output_dir(output_dir); Box::into_raw(Box::new(ButckContext::from(butck_ctx))) } } /// Set output file name /// /// # Arguments /// * `ctx` - Pointer to ButckContext /// * `file` - C string pointer to output file name /// /// # Returns /// * Pointer to updated ButckContext #[unsafe(no_mangle)] #[allow(nonstandard_style)] pub extern "C" fn Butck_WithOutputFile( ctx: *mut ButckContext, file: *mut std::os::raw::c_char, ) -> *mut ButckContext { if ctx.is_null() { return std::ptr::null_mut(); } unsafe { let ffi_ctx = Box::from_raw(ctx); let mut butck_ctx = RustButckContext::from(*ffi_ctx); let output_file = cstr_to_opt_string!(file).map(PathBuf::from); butck_ctx = butck_ctx.with_output_file(output_file); Box::into_raw(Box::new(ButckContext::from(butck_ctx))) } } /// Set input file paths /// /// # Arguments /// * `ctx` - Pointer to ButckContext /// * `files` - Array of C string pointers to input files /// * `files_len` - Number of files in the array /// /// # Returns /// * Pointer to updated ButckContext #[unsafe(no_mangle)] #[allow(nonstandard_style)] pub extern "C" fn Butck_WithFilePaths( ctx: *mut ButckContext, files: *mut *mut std::os::raw::c_char, files_len: usize, ) -> *mut ButckContext { if ctx.is_null() { return std::ptr::null_mut(); } unsafe { let ffi_ctx = Box::from_raw(ctx); let mut butck_ctx = RustButckContext::from(*ffi_ctx); let mut file_paths = Vec::new(); if !files.is_null() && files_len > 0 { for i in 0..files_len { let ptr = *files.add(i); if !ptr.is_null() { let c_str = std::ffi::CStr::from_ptr(ptr); if let Ok(path_str) = c_str.to_str() { file_paths.push(PathBuf::from(path_str)); } } } } butck_ctx = butck_ctx.with_file_paths(file_paths); Box::into_raw(Box::new(ButckContext::from(butck_ctx))) } } /// Add a single input file path /// /// # Arguments /// * `ctx` - Pointer to ButckContext /// * `file` - C string pointer to input file path /// /// # Returns /// * Pointer to updated ButckContext #[unsafe(no_mangle)] #[allow(nonstandard_style)] pub extern "C" fn Butck_AddFile( ctx: *mut ButckContext, file: *mut std::os::raw::c_char, ) -> *mut ButckContext { if ctx.is_null() { return std::ptr::null_mut(); } unsafe { let ffi_ctx = Box::from_raw(ctx); let mut butck_ctx = RustButckContext::from(*ffi_ctx); if !file.is_null() { let c_str = std::ffi::CStr::from_ptr(file); if let Ok(path_str) = c_str.to_str() { butck_ctx = butck_ctx.add_file(PathBuf::from(path_str)); } } Box::into_raw(Box::new(ButckContext::from(butck_ctx))) } } /// Add a parameter to the context /// /// # Arguments /// * `ctx` - Pointer to ButckContext /// * `key` - C string pointer to parameter key /// * `value` - C string pointer to parameter value /// /// # Returns /// * Pointer to updated ButckContext #[unsafe(no_mangle)] #[allow(nonstandard_style)] pub extern "C" fn Butck_Param( ctx: *mut ButckContext, key: *mut std::os::raw::c_char, value: *mut std::os::raw::c_char, ) -> *mut ButckContext { if ctx.is_null() { return std::ptr::null_mut(); } unsafe { let ffi_ctx = Box::from_raw(ctx); let mut butck_ctx = RustButckContext::from(*ffi_ctx); if !key.is_null() && !value.is_null() { let key_cstr = std::ffi::CStr::from_ptr(key); let value_cstr = std::ffi::CStr::from_ptr(value); if let (Ok(key_str), Ok(value_str)) = (key_cstr.to_str(), value_cstr.to_str()) { butck_ctx = butck_ctx.param(key_str.to_string(), value_str.to_string()); } } Box::into_raw(Box::new(ButckContext::from(butck_ctx))) } } /// Free the ButckContext and all associated C strings /// /// # Arguments /// * `ctx` - Pointer to ButckContext #[unsafe(no_mangle)] #[allow(nonstandard_style)] pub extern "C" fn Butck_FreeButckContext(ctx: *mut ButckContext) { if ctx.is_null() { return; } unsafe { let ffi_ctx = &mut *ctx; // Free file_paths if !ffi_ctx.file_paths.is_null() && ffi_ctx.file_paths_len > 0 { for i in 0..ffi_ctx.file_paths_len { let ptr = *ffi_ctx.file_paths.add(i); if !ptr.is_null() { let _ = std::ffi::CString::from_raw(ptr); } } let _ = Box::from_raw(std::slice::from_raw_parts_mut( ffi_ctx.file_paths, ffi_ctx.file_paths_len, )); } // Free storage_path if !ffi_ctx.storage_path.is_null() { let _ = std::ffi::CString::from_raw(ffi_ctx.storage_path); } // Free register_name if !ffi_ctx.register_name.is_null() { let _ = std::ffi::CString::from_raw(ffi_ctx.register_name); } // Free policy_name if !ffi_ctx.policy_name.is_null() { let _ = std::ffi::CString::from_raw(ffi_ctx.policy_name); } // Free output_dir if !ffi_ctx.output_dir.is_null() { let _ = std::ffi::CString::from_raw(ffi_ctx.output_dir); } // Free output_file if !ffi_ctx.output_file.is_null() { let _ = std::ffi::CString::from_raw(ffi_ctx.output_file); } // Free params if !ffi_ctx.params_keys.is_null() && !ffi_ctx.params_values.is_null() && ffi_ctx.params_len > 0 { for i in 0..ffi_ctx.params_len { let key_ptr = *ffi_ctx.params_keys.add(i); let value_ptr = *ffi_ctx.params_values.add(i); if !key_ptr.is_null() { let _ = std::ffi::CString::from_raw(key_ptr); } if !value_ptr.is_null() { let _ = std::ffi::CString::from_raw(value_ptr); } } let _ = Box::from_raw(std::slice::from_raw_parts_mut( ffi_ctx.params_keys, ffi_ctx.params_len, )); let _ = Box::from_raw(std::slice::from_raw_parts_mut( ffi_ctx.params_values, ffi_ctx.params_len, )); } // Free the context itself let _ = Box::from_raw(ctx); } } impl From for ButckContext { fn from(ctx: RustButckContext) -> Self { // Convert method enum let method = match ctx.method { ButckMethod::None => 0, ButckMethod::Write => 1, ButckMethod::Build => 2, }; // Convert file paths let mut file_paths_vec: Vec<*mut std::os::raw::c_char> = Vec::new(); for path in &ctx.file_paths { if let Some(c_str) = path.to_str() { let c_string = std::ffi::CString::new(c_str).unwrap(); file_paths_vec.push(c_string.into_raw()); } else { file_paths_vec.push(std::ptr::null_mut()); } } let file_paths_len = file_paths_vec.len(); let file_paths = if file_paths_len > 0 { let mut boxed_slice = file_paths_vec.into_boxed_slice(); let ptr = boxed_slice.as_mut_ptr(); std::mem::forget(boxed_slice); ptr } else { std::ptr::null_mut() }; // Convert storage path let storage_path = if let Some(ref path) = ctx.storage_path { if let Some(c_str) = path.to_str() { let c_string = std::ffi::CString::new(c_str).unwrap(); c_string.into_raw() } else { std::ptr::null_mut() } } else { std::ptr::null_mut() }; // Convert stream read let (stream_read, stream_read_set) = match ctx.stream_read { Some(size) => (size, true), None => (0, false), }; // Convert register name let register_name = if let Some(ref name) = ctx.register_name { let c_string = std::ffi::CString::new(name.as_str()).unwrap(); c_string.into_raw() } else { std::ptr::null_mut() }; // Convert policy name let policy_name = if let Some(ref name) = ctx.policy_name { let c_string = std::ffi::CString::new(name.as_str()).unwrap(); c_string.into_raw() } else { std::ptr::null_mut() }; // Convert chunk hash let chunk_hash = match ctx.chunk_hash { ChunkWriteHash::Blake3 => 0, ChunkWriteHash::Sha256 => 1, }; // Convert output dir let output_dir = if let Some(c_str) = ctx.output_dir.to_str() { let c_string = std::ffi::CString::new(c_str).unwrap(); c_string.into_raw() } else { std::ptr::null_mut() }; // Convert output file let output_file = if let Some(ref path) = ctx.output_file { if let Some(c_str) = path.to_str() { let c_string = std::ffi::CString::new(c_str).unwrap(); c_string.into_raw() } else { std::ptr::null_mut() } } else { std::ptr::null_mut() }; // Convert params let mut params_keys_vec: Vec<*mut std::os::raw::c_char> = Vec::new(); let mut params_values_vec: Vec<*mut std::os::raw::c_char> = Vec::new(); for (key, value) in &ctx.params { let key_cstring = std::ffi::CString::new(key.as_str()).unwrap(); let value_cstring = std::ffi::CString::new(value.as_str()).unwrap(); params_keys_vec.push(key_cstring.into_raw()); params_values_vec.push(value_cstring.into_raw()); } let params_len = params_keys_vec.len(); let params_keys = if params_len > 0 { let mut boxed_slice = params_keys_vec.into_boxed_slice(); let ptr = boxed_slice.as_mut_ptr(); std::mem::forget(boxed_slice); ptr } else { std::ptr::null_mut() }; let params_values = if params_len > 0 { let mut boxed_slice = params_values_vec.into_boxed_slice(); let ptr = boxed_slice.as_mut_ptr(); std::mem::forget(boxed_slice); ptr } else { std::ptr::null_mut() }; ButckContext { method, file_paths, file_paths_len, storage_path, display_boundaries: ctx.display_boundaries, stream_read, stream_read_set, memmap_read: ctx.memmap_read, register_name, policy_name, chunk_hash, output_dir, output_file, params_keys, params_values, params_len, } } } impl From for RustButckContext { fn from(ffi_ctx: ButckContext) -> Self { // Convert method enum let method = match ffi_ctx.method { 0 => ButckMethod::None, 1 => ButckMethod::Write, 2 => ButckMethod::Build, _ => ButckMethod::None, }; // Convert file paths let mut file_paths = Vec::new(); if !ffi_ctx.file_paths.is_null() && ffi_ctx.file_paths_len > 0 { for i in 0..ffi_ctx.file_paths_len { unsafe { let ptr = *ffi_ctx.file_paths.add(i); if !ptr.is_null() { let c_str = std::ffi::CStr::from_ptr(ptr); if let Ok(path_str) = c_str.to_str() { file_paths.push(PathBuf::from(path_str)); } } } } } // Convert storage path let storage_path = if !ffi_ctx.storage_path.is_null() { unsafe { cstr_to_opt_string!(ffi_ctx.storage_path).map(PathBuf::from) } } else { None }; // Convert stream read let stream_read = if ffi_ctx.stream_read_set { Some(ffi_ctx.stream_read) } else { None }; // Convert register name let register_name = if !ffi_ctx.register_name.is_null() { unsafe { cstr_to_opt_string!(ffi_ctx.register_name) } } else { None }; // Convert policy name let policy_name = if !ffi_ctx.policy_name.is_null() { unsafe { cstr_to_opt_string!(ffi_ctx.policy_name) } } else { None }; // Convert chunk hash let chunk_hash = match ffi_ctx.chunk_hash { 0 => ChunkWriteHash::Blake3, 1 => ChunkWriteHash::Sha256, _ => ChunkWriteHash::default(), }; // Convert output dir let output_dir = if !ffi_ctx.output_dir.is_null() { unsafe { cstr_to_path_or_current_dir!(ffi_ctx.output_dir) } } else { current_dir().unwrap() }; // Convert output file let output_file = if !ffi_ctx.output_file.is_null() { unsafe { cstr_to_opt_string!(ffi_ctx.output_file).map(PathBuf::from) } } else { None }; // Convert params let mut params = HashMap::new(); if !ffi_ctx.params_keys.is_null() && !ffi_ctx.params_values.is_null() && ffi_ctx.params_len > 0 { for i in 0..ffi_ctx.params_len { unsafe { let key_ptr = *ffi_ctx.params_keys.add(i); let value_ptr = *ffi_ctx.params_values.add(i); if !key_ptr.is_null() && !value_ptr.is_null() { let key_cstr = std::ffi::CStr::from_ptr(key_ptr); let value_cstr = std::ffi::CStr::from_ptr(value_ptr); if let (Ok(key_str), Ok(value_str)) = (key_cstr.to_str(), value_cstr.to_str()) { params.insert(key_str.to_string(), value_str.to_string()); } } } } } RustButckContext { method, file_paths, storage_path, display_boundaries: ffi_ctx.display_boundaries, stream_read, memmap_read: ffi_ctx.memmap_read, register_name, policy_name, chunk_hash, output_dir, output_file, params, ..Default::default() } } }