webpage_quality_analyzer 1.0.2

High-performance webpage quality analyzer with 115 comprehensive metrics - Rust library with WASM, C++, and Python bindings
Documentation
//! Error handling for FFI boundary
//!
//! Uses thread-local storage for error messages since C doesn't have exceptions.

use std::cell::RefCell;
use std::ffi::CString;
use std::os::raw::c_char;
use std::ptr;

pub use super::types::CErrorCode;

thread_local! {
    static LAST_ERROR: RefCell<Option<String>> = RefCell::new(None);
}

/// Set the last error message (thread-local)
pub(crate) fn set_last_error(msg: String) {
    LAST_ERROR.with(|e| *e.borrow_mut() = Some(msg));
}

/// Get the last error message as a C string
///
/// # Returns
/// Pointer to null-terminated error string, or NULL if no error.
/// Caller must free with `wqa_free_string()`.
///
/// # Thread Safety
/// Each thread has its own error storage.
#[no_mangle]
pub extern "C" fn wqa_get_last_error() -> *mut c_char {
    LAST_ERROR.with(|e| {
        match &*e.borrow() {
            Some(msg) => {
                match CString::new(msg.clone()) {
                    Ok(c_str) => c_str.into_raw(),
                    Err(_) => {
                        // Error message contains null byte, return generic message
                        CString::new("Error message contains invalid characters")
                            .unwrap()
                            .into_raw()
                    }
                }
            }
            None => ptr::null_mut(),
        }
    })
}

/// Clear the last error message
#[no_mangle]
pub extern "C" fn wqa_clear_error() {
    LAST_ERROR.with(|e| *e.borrow_mut() = None);
}

/// Free a string allocated by Rust
///
/// # Safety
/// - `s` must be a valid pointer returned by a Rust FFI function
/// - `s` must not be used after calling this function
#[no_mangle]
pub extern "C" fn wqa_free_string(s: *mut c_char) {
    if !s.is_null() {
        unsafe {
            let _ = CString::from_raw(s);
        }
    }
}

/// Helper to set error from AnalyzeError
pub(crate) fn set_error_from_result<T>(result: crate::models::models::Result<T>) -> Option<T> {
    match result {
        Ok(val) => Some(val),
        Err(e) => {
            set_last_error(format!("{}", e));
            None
        }
    }
}