use std::ffi::{CStr, c_char};
pub(crate) unsafe fn decode_non_null_ffi_c_string(ptr: *const c_char) -> Result<String, String> {
unsafe { CStr::from_ptr(ptr) }
.to_str()
.map(str::to_string)
.map_err(|error| format!("provider FFI string is not valid UTF-8: {error}"))
}
pub(crate) unsafe fn decode_provider_last_error_message(
ptr: *const c_char,
provider_name: &str,
unknown_message: &str,
) -> String {
if ptr.is_null() {
return unknown_message.to_string();
}
unsafe { decode_non_null_ffi_c_string(ptr) }.unwrap_or_else(|error| {
format!("{provider_name} provider error message is not valid UTF-8: {error}")
})
}
pub(crate) mod lancedb;
pub(crate) mod sqlite;
#[cfg(test)]
mod tests {
use super::{decode_non_null_ffi_c_string, decode_provider_last_error_message};
use std::ffi::CString;
#[test]
fn provider_ffi_c_string_decodes_valid_utf8() {
let value = CString::new("provider-ok").expect("build valid provider string");
let decoded = unsafe { decode_non_null_ffi_c_string(value.as_ptr()) }
.expect("valid provider string should decode");
assert_eq!(decoded, "provider-ok");
}
#[test]
fn provider_ffi_c_string_rejects_invalid_utf8() {
let value = CString::new(vec![0xff]).expect("build invalid provider string");
let error = unsafe { decode_non_null_ffi_c_string(value.as_ptr()) }
.expect_err("invalid provider string should fail");
assert!(error.contains("not valid UTF-8"));
}
#[test]
fn provider_last_error_message_marks_invalid_utf8() {
let value = CString::new(vec![0xff]).expect("build invalid provider error string");
let decoded = unsafe {
decode_provider_last_error_message(value.as_ptr(), "SQLite", "unknown provider error")
};
assert!(decoded.contains("SQLite provider error message is not valid UTF-8"));
}
#[test]
fn provider_last_error_message_uses_unknown_message_for_null() {
let decoded = unsafe {
decode_provider_last_error_message(
std::ptr::null(),
"SQLite",
"unknown SQLite host error",
)
};
assert_eq!(decoded, "unknown SQLite host error");
}
}