tank-duckdb 0.1.0

DuckDB driver implementation for Tank
Documentation
use libduckdb_sys::duckdb_result;
use std::{
    mem::ManuallyDrop,
    ops::{Deref, DerefMut},
    sync::atomic::AtomicPtr,
};

pub(crate) trait NullCheck {
    fn is_null(&self) -> bool;
}

impl<T> NullCheck for *const T {
    fn is_null(&self) -> bool {
        (*self as *const T).is_null()
    }
}

impl<T> NullCheck for *mut T {
    fn is_null(&self) -> bool {
        (*self as *const T).is_null()
    }
}

impl<T> NullCheck for AtomicPtr<T> {
    fn is_null(&self) -> bool {
        self.load(std::sync::atomic::Ordering::Relaxed).is_null()
    }
}

impl<T: NullCheck> NullCheck for &T {
    fn is_null(&self) -> bool {
        (*self).is_null()
    }
}

impl<T: NullCheck> NullCheck for &mut T {
    fn is_null(&self) -> bool {
        false
    }
}

impl NullCheck for duckdb_result {
    fn is_null(&self) -> bool {
        false
    }
}

#[derive(Debug)]
pub(crate) struct CBox<T: NullCheck> {
    ptr: T,
    dealloc: fn(T),
}

impl<T: NullCheck> CBox<T> {
    pub fn new(ptr: T, dealloc: fn(T)) -> Self {
        Self { ptr, dealloc }
    }

    pub fn as_ref(&self) -> CBox<&T> {
        CBox::new(&self.ptr, |_| {})
    }

    pub fn as_mut(&mut self) -> CBox<&mut T> {
        CBox::new(&mut self.ptr, |_| {})
    }

    pub fn into_raw(self) -> T {
        let this = ManuallyDrop::new(self);
        unsafe { std::ptr::read(&this.ptr) }
    }
}

impl<T: NullCheck> Drop for CBox<T> {
    fn drop(&mut self) {
        if !self.is_null() {
            unsafe {
                (self.dealloc)(std::ptr::read(&self.ptr as *const T));
            }
        }
    }
}

impl<T: NullCheck> Deref for CBox<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.ptr
    }
}

impl<T: NullCheck> DerefMut for CBox<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.ptr
    }
}

unsafe impl<T: NullCheck> Send for CBox<T> {}
unsafe impl<T: NullCheck> Sync for CBox<T> {}

#[cfg(test)]
mod tests {
    use std::ptr;

    use crate::cbox::CBox;

    #[tokio::test]
    async fn cbox_raw_pointer() {
        static mut DESTROYED: bool = false;
        let v = 123;
        let ptr: *const i32 = &v;
        unsafe {
            {
                let ptr = CBox::new(ptr::null::<*const i32>(), |_| DESTROYED = true);
                assert_eq!(*ptr, ptr::null());
            }
            assert!(!DESTROYED);
            {
                let ptr = CBox::new(ptr, |_| DESTROYED = true);
                assert_eq!(**ptr, 123);
                assert!(!DESTROYED);
            }
            assert!(DESTROYED)
        }
    }
}