webpage_quality_analyzer 1.0.2

High-performance webpage quality analyzer with 115 comprehensive metrics - Rust library with WASM, C++, and Python bindings
Documentation
//! Type conversion utilities for FFI boundary

use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::ptr;

/// Convert C string to Rust string slice (borrow, no ownership transfer)
///
/// # Safety
/// - `ptr` must be a valid null-terminated C string
/// - The string must outlive the returned reference
pub unsafe fn c_str_to_rust<'a>(ptr: *const c_char) -> Option<&'a str> {
    if ptr.is_null() {
        return None;
    }
    CStr::from_ptr(ptr).to_str().ok()
}

/// Convert Rust string to C string (transfer ownership to caller)
/// Caller must free with `wqa_free_string()`
pub fn rust_str_to_c(s: &str) -> *mut c_char {
    match CString::new(s) {
        Ok(c_str) => c_str.into_raw(),
        Err(_) => ptr::null_mut(),
    }
}

/// Convert Option<T> to nullable pointer
pub fn option_to_ptr<T>(opt: Option<T>) -> *mut T {
    match opt {
        Some(val) => Box::into_raw(Box::new(val)),
        None => ptr::null_mut(),
    }
}

/// Convert nullable pointer to Option
///
/// # Safety
/// - `ptr` must be null or a valid pointer to T
pub unsafe fn ptr_to_option<'a, T>(ptr: *const T) -> Option<&'a T> {
    if ptr.is_null() {
        None
    } else {
        Some(&*ptr)
    }
}

/// Convert nullable mutable pointer to Option
///
/// # Safety
/// - `ptr` must be null or a valid mutable pointer to T
pub unsafe fn ptr_to_option_mut<'a, T>(ptr: *mut T) -> Option<&'a mut T> {
    if ptr.is_null() {
        None
    } else {
        Some(&mut *ptr)
    }
}

// ============================================================================
// Safe Pointer Casting (for opaque types)
// ============================================================================

use super::types::{
    AnalyzerInternal, BuilderInternal, CAnalyzer, CAnalyzerBuilder, CReport, CReportInternal,
};

/// Safely cast CAnalyzer pointer to internal type
///
/// # Safety
/// - `ptr` must be a valid CAnalyzer pointer created by this FFI
pub unsafe fn cast_analyzer(ptr: *const CAnalyzer) -> Option<&'static AnalyzerInternal> {
    if ptr.is_null() {
        None
    } else {
        Some(&*(ptr as *const AnalyzerInternal))
    }
}

/// Safely cast mutable CAnalyzer pointer to internal type
///
/// # Safety
/// - `ptr` must be a valid CAnalyzer pointer created by this FFI
pub unsafe fn cast_analyzer_mut(ptr: *mut CAnalyzer) -> Option<&'static mut AnalyzerInternal> {
    if ptr.is_null() {
        None
    } else {
        Some(&mut *(ptr as *mut AnalyzerInternal))
    }
}

/// Safely cast CReport pointer to internal type
///
/// # Safety
/// - `ptr` must be a valid CReport pointer created by this FFI
pub unsafe fn cast_report(ptr: *const CReport) -> Option<&'static CReportInternal> {
    if ptr.is_null() {
        None
    } else {
        Some(&*(ptr as *const CReportInternal))
    }
}

/// Safely cast CAnalyzerBuilder pointer to internal type
///
/// # Safety
/// - `ptr` must be a valid CAnalyzerBuilder pointer created by this FFI
pub unsafe fn cast_builder(ptr: *const CAnalyzerBuilder) -> Option<&'static BuilderInternal> {
    if ptr.is_null() {
        None
    } else {
        Some(&*(ptr as *const BuilderInternal))
    }
}

/// Safely cast mutable CAnalyzerBuilder pointer to internal type
///
/// # Safety
/// - `ptr` must be a valid CAnalyzerBuilder pointer created by this FFI
pub unsafe fn cast_builder_mut(ptr: *mut CAnalyzerBuilder) -> Option<&'static mut BuilderInternal> {
    if ptr.is_null() {
        None
    } else {
        Some(&mut *(ptr as *mut BuilderInternal))
    }
}

// ============================================================================
// Null Check Macros
// ============================================================================

/// Macro for null pointer checks with error code return
#[macro_export]
macro_rules! null_check {
    ($ptr:expr) => {
        if $ptr.is_null() {
            $crate::ffi::errors::set_last_error("Null pointer provided".to_string());
            return $crate::ffi::types::CErrorCode::InvalidPointer;
        }
    };
    ($ptr:expr, $ret:expr) => {
        if $ptr.is_null() {
            $crate::ffi::errors::set_last_error("Null pointer provided".to_string());
            return $ret;
        }
    };
}

/// Macro for null pointer checks with Option return
#[macro_export]
macro_rules! null_check_opt {
    ($ptr:expr) => {
        if $ptr.is_null() {
            $crate::ffi::errors::set_last_error("Null pointer provided".to_string());
            return None;
        }
    };
}