use std::os::raw::c_void;
use libduckdb_sys::{
duckdb_bind_info, duckdb_bind_set_bind_data, duckdb_function_get_bind_data,
duckdb_function_info, duckdb_init_get_bind_data, duckdb_init_info,
};
pub struct FfiBindData<T: 'static> {
_marker: std::marker::PhantomData<T>,
}
impl<T: 'static> FfiBindData<T> {
pub unsafe fn set(info: duckdb_bind_info, data: T) {
let raw = Box::into_raw(Box::new(data)).cast::<c_void>();
unsafe {
duckdb_bind_set_bind_data(info, raw, Some(Self::destroy));
}
}
pub const fn get_from_bind<'a>(info: duckdb_bind_info) -> Option<&'a T> {
let _ = info; None
}
pub unsafe fn get_from_init<'a>(info: duckdb_init_info) -> Option<&'a T> {
let raw = unsafe { duckdb_init_get_bind_data(info) };
if raw.is_null() {
return None;
}
Some(unsafe { &*raw.cast::<T>() })
}
pub unsafe fn get_from_function<'a>(info: duckdb_function_info) -> Option<&'a T> {
let raw = unsafe { duckdb_function_get_bind_data(info) };
if raw.is_null() {
return None;
}
Some(unsafe { &*raw.cast::<T>() })
}
pub unsafe extern "C" fn destroy(ptr: *mut c_void) {
if !ptr.is_null() {
unsafe { drop(Box::from_raw(ptr.cast::<T>())) };
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(dead_code)]
struct Config {
value: i32,
}
#[test]
fn destroy_null_is_noop() {
unsafe { FfiBindData::<Config>::destroy(std::ptr::null_mut()) };
}
#[test]
fn destroy_allocated_box() {
let boxed = Box::new(Config { value: 42 });
let raw = Box::into_raw(boxed).cast::<c_void>();
unsafe { FfiBindData::<Config>::destroy(raw) };
}
#[test]
fn get_from_bind_returns_none() {
let result = FfiBindData::<Config>::get_from_bind(std::ptr::null_mut());
assert!(result.is_none());
}
}