modelio-rs 0.2.1

Safe Rust bindings for Apple's ModelIO framework — assets, meshes, materials, lights, cameras, voxels, textures, and animation on macOS
Documentation
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_void};
use std::path::Path;

use serde::de::DeserializeOwned;

use crate::error::{ModelIoError, Result};
use crate::ffi;

pub(crate) fn c_string(value: &str) -> Result<CString> {
    CString::new(value).map_err(|_| {
        ModelIoError::new(
            ffi::status::INVALID_ARGUMENT,
            "string contained interior NUL",
        )
    })
}

pub(crate) fn path_to_c_string(path: &Path) -> Result<CString> {
    c_string(path.to_string_lossy().as_ref())
}

pub(crate) fn take_string(ptr: *mut c_char) -> Option<String> {
    if ptr.is_null() {
        return None;
    }

    let string = unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() };
    unsafe { libc::free(ptr.cast::<c_void>()) };
    Some(string)
}

pub(crate) fn status_result(status: i32, error_ptr: *mut c_char) -> Result<()> {
    if status == ffi::status::OK {
        return Ok(());
    }

    let message =
        take_string(error_ptr).unwrap_or_else(|| format!("ModelIO bridge error {status}"));
    Err(ModelIoError::new(status, message))
}

pub(crate) fn required_handle(
    ptr: *mut c_void,
    context: &'static str,
) -> Result<crate::handle::ObjectHandle> {
    unsafe { crate::handle::ObjectHandle::from_retained_ptr(ptr) }.ok_or_else(|| {
        ModelIoError::new(ffi::status::NULL_RESULT, format!("{context} returned null"))
    })
}

pub(crate) fn parse_json<T: DeserializeOwned>(
    ptr: *mut c_char,
    context: &'static str,
) -> Result<T> {
    let json = take_string(ptr).ok_or_else(|| {
        ModelIoError::new(ffi::status::NULL_RESULT, format!("{context} returned null"))
    })?;
    serde_json::from_str(&json).map_err(|error| {
        ModelIoError::new(
            ffi::status::FRAMEWORK,
            format!("failed to parse {context} JSON: {error}"),
        )
    })
}