value-box 4.1.0

Allows developers to pass Rust-allocated structures over ffi.
Documentation
use std::error::Error;
use std::ffi::c_void;
use std::mem::size_of;
use std::rc::Rc;

use value_box::{BorrowedPtr, ErasedBorrowedPtr, OwnedPtr, Result, from_raw};

#[test]
fn value_box_size_in_memory() -> Result<()> {
    assert_eq!(size_of::<BorrowedPtr<c_void>>(), size_of::<*mut c_void>());
    assert_eq!(size_of::<OwnedPtr<c_void>>(), size_of::<*mut c_void>());
    assert_eq!(
        size_of::<BorrowedPtr<(u64, u64)>>(),
        size_of::<*mut (u64, u64)>()
    );
    assert_eq!(
        size_of::<OwnedPtr<Box<dyn Error>>>(),
        size_of::<*mut Box<dyn Error>>()
    );
    Ok(())
}

#[test]
fn value_box_as_ref_mut() -> Result<()> {
    let raw = Box::into_raw(Box::new(5_i32));
    let value_box_ptr = unsafe { BorrowedPtr::from_raw(raw) };
    let value = value_box_ptr.with_ref_ok(|value| *value)?;
    assert_eq!(value, 5);
    unsafe { drop(Box::from_raw(raw)) };
    Ok(())
}

#[test]
fn borrowed_ptr_from_ref() -> Result<()> {
    let value = 5_i32;
    let value_box_ptr = BorrowedPtr::from_ref(&value);
    let borrowed = value_box_ptr.with_ref_ok(|value| *value)?;
    assert_eq!(borrowed, 5);
    Ok(())
}

#[test]
fn borrowed_ptr_from_mut() -> Result<()> {
    let mut value = 5_i32;
    let mut value_box_ptr = BorrowedPtr::from_mut(&mut value);
    value_box_ptr.with_mut_ok(|value| *value = 7)?;
    assert_eq!(value, 7);
    Ok(())
}

#[test]
fn value_box_as_non_null() -> Result<()> {
    let raw = Box::into_raw(Box::new(5_i32));
    let value_box_ptr = unsafe { BorrowedPtr::from_raw(raw) };
    let value = value_box_ptr.with_ptr_ok(|pointer| unsafe { *pointer.as_ptr().cast::<i32>() })?;
    assert_eq!(value, 5);
    unsafe { drop(Box::from_raw(raw)) };
    Ok(())
}

#[test]
fn erased_value_box_as_non_null() -> Result<()> {
    let raw = Box::into_raw(Box::new(7_i32));
    let erased_value_box_ptr = unsafe { ErasedBorrowedPtr::from_raw(raw.cast()) };
    let value =
        erased_value_box_ptr.with_ptr_ok(|pointer| unsafe { *pointer.as_ptr().cast::<i32>() })?;
    assert_eq!(value, 7);
    unsafe { drop(Box::from_raw(raw)) };
    Ok(())
}

#[test]
fn value_box_into_erased_raw() -> Result<()> {
    let erased_value_box_ptr =
        unsafe { ErasedBorrowedPtr::from_raw(Box::into_raw(Box::new(9_i32)).cast()) };
    let value =
        erased_value_box_ptr.with_ptr_ok(|pointer| unsafe { *pointer.as_ptr().cast::<i32>() })?;
    assert_eq!(value, 9);
    unsafe { from_raw::<i32>(erased_value_box_ptr.as_raw().cast()) };
    Ok(())
}

#[test]
fn const_erased_value_box_as_non_null() -> Result<()> {
    let raw = Box::into_raw(Box::new(11_i32));
    let erased_value_box_ptr = unsafe { ErasedBorrowedPtr::from_raw(raw.cast()) };
    let value =
        erased_value_box_ptr.with_ptr_ok(|pointer| unsafe { *pointer.as_ptr().cast::<i32>() })?;
    assert_eq!(value, 11);
    unsafe { drop(Box::from_raw(raw)) };
    Ok(())
}

#[test]
fn borrowed_ptr_with_clone() -> Result<()> {
    let raw = Box::into_raw(Box::new(5_i32));
    let value_box_ptr = unsafe { BorrowedPtr::from_raw(raw) };
    assert!(!value_box_ptr.is_null());

    let mut result = 0;
    value_box_ptr.with_clone_ok(|value| result = value * 2)?;
    assert!(!value_box_ptr.is_null());
    assert_eq!(result, 10);
    unsafe { drop(Box::from_raw(raw)) };
    Ok(())
}

#[test]
fn borrowed_ptr_with_clone_return() -> Result<()> {
    let raw = Box::into_raw(Box::new(5_i32));
    let value_box_ptr = unsafe { BorrowedPtr::from_raw(raw) };
    assert!(!value_box_ptr.is_null());

    let result = value_box_ptr.with_clone_ok(|value| value * 2)?;
    assert!(!value_box_ptr.is_null());
    assert_eq!(result, 10);
    unsafe { drop(Box::from_raw(raw)) };
    Ok(())
}

#[test]
fn value_box_drop() {
    let value = Rc::new(42);

    let ptr = OwnedPtr::new(value.clone());
    assert_eq!(Rc::strong_count(&value), 2);
    drop(ptr);

    assert_eq!(Rc::strong_count(&value), 1);
}

#[test]
fn owned_ptr_with_value_ok() -> Result<()> {
    let value = Rc::new(42);
    let owned = OwnedPtr::new(value.clone());

    let strong_count = owned.with_value_ok(|extracted| {
        let count = Rc::strong_count(&extracted);
        drop(extracted);
        count
    })?;

    assert_eq!(strong_count, 2);
    assert_eq!(Rc::strong_count(&value), 1);

    Ok(())
}

#[test]
fn owned_ptr_with_value() -> Result<()> {
    let value = Rc::new(42);
    let owned = OwnedPtr::new(value.clone());

    let strong_count = owned.with_value_ok(|extracted| {
        let count = Rc::strong_count(&extracted);
        drop(extracted);
        count
    })?;

    assert_eq!(strong_count, 2);
    assert_eq!(Rc::strong_count(&value), 1);

    Ok(())
}