use std::{ffi::c_void, ptr::{NonNull, self}};

/// Wraps a type-erased pointer (which it does not own) and marks it as `Send`.
///
/// The `Send`-ability of the wrapped pointer must be guaranteed by code that
/// instantiates a `Sendable`.
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
pub struct Sendable {
    pub(crate) ptr: NonNull<c_void>,
}

impl Sendable {
    /// Creates a `Sendable` wrapping `ptr`.
    ///
    /// ## Safety
    ///
    /// `ptr` must be safe to send across threads.
    pub const unsafe fn new(ptr: NonNull<c_void>) -> Self {
        Sendable { ptr }
    }

    /// Creates a `Sendable` if `ptr` is not null, returning `None` if it is.
    /// ## Safety
    ///
    /// `ptr` must be safe to send across threads.
    pub unsafe fn from_nullable(ptr: *mut c_void) -> Option<Self> {
        unsafe {
            NonNull::new(ptr)
                .map(|p| Sendable::new(p))
        }
    }

    /// Retrieves the pointer wrapped by `s`, or a null pointer if `s` is
    /// `None`.
    pub fn as_ptr(s: Option<Sendable>) -> *mut c_void {
        s.map(|s| s.ptr.as_ptr()).unwrap_or(ptr::null_mut())
    }
}

// Guaranteed by `Sendable::new`.
unsafe impl Send for Sendable {}
