storage-bindings 0.2.0

Rust bindings for Storage, the Decentralized Durability Engine
//! FFI bindings for the libstorage C library
//!
//! This module contains the low-level bindings generated by bindgen
//! and provides safe wrappers around the C functions.

// Include the generated bindings in a module to suppress warnings
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
mod generated {
    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}

// Re-export all the generated bindings
pub use generated::*;

use libc::c_char;
use std::ffi::CStr;
use std::str::Utf8Error;

/// Convert a C string to a Rust string
///
/// # Safety
///
/// The `ptr` must be either:
/// - A valid pointer to a null-terminated C string
/// - A null pointer (which will return an empty string)
///
/// The memory pointed to by `ptr` must remain valid for the duration of this call.
pub unsafe fn c_str_to_string(ptr: *const c_char) -> Result<String, Utf8Error> {
    if ptr.is_null() {
        return Ok(String::new());
    }

    let c_str = CStr::from_ptr(ptr);
    c_str.to_str().map(|s| s.to_string())
}

/// Convert a Rust string to a C string
pub fn string_to_c_string(s: &str) -> *mut c_char {
    std::ffi::CString::new(s).unwrap().into_raw()
}

/// Free a C string created with string_to_c_string
///
/// # Safety
///
/// The `ptr` must be either:
/// - A valid pointer to a C string allocated by `string_to_c_string`
/// - A null pointer (which will be safely ignored)
///
/// After calling this function, the pointer becomes invalid and must not be used.
pub unsafe fn free_c_string(ptr: *mut c_char) {
    if !ptr.is_null() {
        let _ = std::ffi::CString::from_raw(ptr);
    }
}

/// Callback return codes
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CallbackReturn {
    Ok = 0,
    Error = 1,
    Progress = 3,
}

impl From<i32> for CallbackReturn {
    fn from(value: i32) -> Self {
        match value {
            0 => CallbackReturn::Ok,
            1 => CallbackReturn::Error,
            3 => CallbackReturn::Progress,
            _ => CallbackReturn::Error,
        }
    }
}

/// Callback function type - available from generated bindings
// The StorageCallback type alias is already available from the generated bindings
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_c_str_conversion() {
        let rust_str = "Hello, World!";
        let c_str = string_to_c_string(rust_str);

        unsafe {
            let converted = c_str_to_string(c_str).unwrap();
            assert_eq!(rust_str, converted);
            free_c_string(c_str);
        }
    }
}