use std::ffi::{c_char, c_void, CStr};
use std::marker::PhantomData;
use std::ptr::NonNull;
use ringo_fm_sys as sys;
use crate::error::{Error, Result};
pub(crate) struct ManagedRef<T> {
ptr: NonNull<c_void>,
_t: PhantomData<T>,
}
impl<T> ManagedRef<T> {
pub fn from_owned(ptr: *const c_void) -> Result<Self> {
NonNull::new(ptr as *mut c_void)
.map(|p| Self { ptr: p, _t: PhantomData })
.ok_or_else(|| Error::Native("null pointer from FFI".into()))
}
#[allow(dead_code)]
pub fn retain(ptr: *const c_void) -> Result<Self> {
let p = NonNull::new(ptr as *mut c_void)
.ok_or_else(|| Error::Native("null pointer from FFI".into()))?;
unsafe { sys::FMRetain(p.as_ptr()) };
Ok(Self { ptr: p, _t: PhantomData })
}
pub fn as_ptr(&self) -> *const c_void {
self.ptr.as_ptr()
}
}
impl<T> Drop for ManagedRef<T> {
fn drop(&mut self) {
unsafe { sys::FMRelease(self.ptr.as_ptr()) };
}
}
unsafe impl<T: Send> Send for ManagedRef<T> {}
unsafe impl<T: Sync> Sync for ManagedRef<T> {}
pub(crate) struct FmString {
ptr: *mut c_char,
}
impl FmString {
pub fn from_raw(ptr: *mut c_char) -> Option<Self> {
if ptr.is_null() { None } else { Some(Self { ptr }) }
}
pub fn to_string(&self) -> Result<String> {
let cstr = unsafe { CStr::from_ptr(self.ptr) };
cstr.to_str()
.map(|s| s.to_owned())
.map_err(|e| Error::Native(format!("invalid utf8 in C string: {e}")))
}
}
impl Drop for FmString {
fn drop(&mut self) {
unsafe { sys::FMFreeString(self.ptr) };
}
}
pub(crate) fn check_error(code: i32, desc_ptr: *mut c_char) -> Result<()> {
if code == crate::error::status::SUCCESS {
if !desc_ptr.is_null() {
drop(FmString::from_raw(desc_ptr));
}
return Ok(());
}
let message = FmString::from_raw(desc_ptr)
.and_then(|s| s.to_string().ok())
.unwrap_or_default();
Err(Error::from_status(code, message))
}