icsneoc2 0.1002002.0-rc.4

High-level Rust interface for Intrepid Control Systems vehicle network adapters
Documentation
//! Internal FFI helpers shared across the crate.
//!
//! These are not part of the public API.

use crate::error::{Error, Result};
use crate::sys;

/// Call an FFI function that fills a `(buf: *mut i8, len: *mut usize)` pair
/// and return the result as a Rust `String`.
///
/// `max_len` is the initial buffer capacity.
/// `f` receives `(buf_ptr, len_ptr)` and must return `icsneoc2_error_t`.
pub(crate) fn ffi_string(
    max_len: usize,
    f: impl FnOnce(*mut i8, *mut usize) -> sys::icsneoc2_error_t,
) -> Result<String> {
    let mut buf: Vec<u8> = vec![0; max_len];
    let mut len: usize = max_len;
    check(f(buf.as_mut_ptr() as *mut i8, &mut len))?;
    // Truncate at the first NUL byte (or `len`), so we handle C APIs that
    // include or exclude the terminator in the returned length.
    let nul_pos = buf[..len].iter().position(|&b| b == 0).unwrap_or(len);
    buf.truncate(nul_pos);
    String::from_utf8(buf)
        .map_err(|e| Error::StringConversionError(format!("UTF-8 conversion error: {e}")))
}

/// Look up the human-readable description string for a C API error code.
/// Falls back to [`Error::ErrorCodeGetError`] if the lookup itself fails.
pub(crate) fn api_error(code: sys::Error) -> Error {
    let raw = sys::icsneoc2_error_t::from(code);
    match ffi_string(255, |buf, len| unsafe {
        sys::icsneoc2_error_code_get(raw, buf, len)
    }) {
        Ok(s) => Error::APIError(code, s),
        Err(_) => Error::ErrorCodeGetError(code),
    }
}

/// Convert a raw `icsneoc2_error_t` to `Result<()>`.
/// Returns `Ok(())` on success, or the looked-up API error otherwise.
pub(crate) fn check(raw: sys::icsneoc2_error_t) -> Result<()> {
    match sys::Error::try_from(raw) {
        Ok(sys::Error::Success) => Ok(()),
        Ok(code) => Err(api_error(code)),
        Err(e) => Err(Error::from(e)),
    }
}