webpage_quality_analyzer 1.0.2

High-performance webpage quality analyzer with 115 comprehensive metrics - Rust library with WASM, C++, and Python bindings
Documentation
/**
 * FFI bindings for output customization (field selectors)
 */
use std::ffi::CString;
use std::os::raw::c_char;
use std::ptr;

use super::conversions::{c_str_to_rust, cast_report};
use super::errors::CErrorCode;
use super::types::{CFieldSelector, CReport};
use crate::utils::json_optimizer::{FieldSelector, OptimizedSerializer, SerializationOptions};

// Internal wrapper
pub(crate) struct FieldSelectorInternal {
    pub inc_fields: Vec<String>,
    pub exc_fields: Vec<String>,
    pub inc_sections: Vec<String>,
    pub exc_sections: Vec<String>,
}

impl FieldSelectorInternal {
    fn new() -> Self {
        Self {
            inc_fields: Vec::new(),
            exc_fields: Vec::new(),
            inc_sections: Vec::new(),
            exc_sections: Vec::new(),
        }
    }

    fn to_selector(&self) -> FieldSelector {
        let mut builder = FieldSelector::builder();
        for f in &self.inc_fields {
            builder = builder.include_field(f.clone());
        }
        for f in &self.exc_fields {
            builder = builder.exclude_field(f.clone());
        }
        for s in &self.inc_sections {
            builder = builder.include_section(s.clone());
        }
        for s in &self.exc_sections {
            builder = builder.exclude_section(s.clone());
        }
        builder.build()
    }
}

#[no_mangle]
pub extern "C" fn wqa_field_selector_new() -> *mut CFieldSelector {
    Box::into_raw(Box::new(FieldSelectorInternal::new())) as *mut CFieldSelector
}

#[no_mangle]
pub extern "C" fn wqa_field_selector_free(selector: *mut CFieldSelector) {
    if !selector.is_null() {
        unsafe {
            let _ = Box::from_raw(selector as *mut FieldSelectorInternal);
        }
    }
}

#[no_mangle]
pub extern "C" fn wqa_field_selector_include_field(
    sel: *mut CFieldSelector,
    field: *const c_char,
) -> CErrorCode {
    if sel.is_null() || field.is_null() {
        return CErrorCode::InvalidPointer;
    }
    let s = match unsafe { c_str_to_rust(field) } {
        Some(s) => s.to_string(),
        None => return CErrorCode::Utf8Error,
    };
    unsafe {
        (&mut *(sel as *mut FieldSelectorInternal))
            .inc_fields
            .push(s);
    }
    CErrorCode::Success
}

#[no_mangle]
pub extern "C" fn wqa_field_selector_exclude_field(
    sel: *mut CFieldSelector,
    field: *const c_char,
) -> CErrorCode {
    if sel.is_null() || field.is_null() {
        return CErrorCode::InvalidPointer;
    }
    let s = match unsafe { c_str_to_rust(field) } {
        Some(s) => s.to_string(),
        None => return CErrorCode::Utf8Error,
    };
    unsafe {
        (&mut *(sel as *mut FieldSelectorInternal))
            .exc_fields
            .push(s);
    }
    CErrorCode::Success
}

#[no_mangle]
pub extern "C" fn wqa_field_selector_include_section(
    sel: *mut CFieldSelector,
    sec: *const c_char,
) -> CErrorCode {
    if sel.is_null() || sec.is_null() {
        return CErrorCode::InvalidPointer;
    }
    let s = match unsafe { c_str_to_rust(sec) } {
        Some(s) => s.to_string(),
        None => return CErrorCode::Utf8Error,
    };
    unsafe {
        (&mut *(sel as *mut FieldSelectorInternal))
            .inc_sections
            .push(s);
    }
    CErrorCode::Success
}

#[no_mangle]
pub extern "C" fn wqa_field_selector_exclude_section(
    sel: *mut CFieldSelector,
    sec: *const c_char,
) -> CErrorCode {
    if sel.is_null() || sec.is_null() {
        return CErrorCode::InvalidPointer;
    }
    let s = match unsafe { c_str_to_rust(sec) } {
        Some(s) => s.to_string(),
        None => return CErrorCode::Utf8Error,
    };
    unsafe {
        (&mut *(sel as *mut FieldSelectorInternal))
            .exc_sections
            .push(s);
    }
    CErrorCode::Success
}

#[no_mangle]
pub extern "C" fn wqa_report_to_json_with_selector(
    report: *const CReport,
    selector: *const CFieldSelector,
    compact: bool,
) -> *mut c_char {
    if report.is_null() {
        return ptr::null_mut();
    }
    let r = match unsafe { cast_report(report) } {
        Some(r) => r,
        None => return ptr::null_mut(),
    };
    let json = if selector.is_null() {
        if compact {
            serde_json::to_string(&r.report)
        } else {
            serde_json::to_string_pretty(&r.report)
        }
    } else {
        let si = unsafe { &*(selector as *const FieldSelectorInternal) };
        let fs = si.to_selector();
        let opts = SerializationOptions {
            compact,
            skip_empty_fields: true,
            minimal_output: false,
            streaming: false,
            buffer_size: 8192,
        };
        OptimizedSerializer::serialize_with_selector(&r.report, &fs, Some(&opts))
    };
    match json {
        Ok(j) => match CString::new(j) {
            Ok(cs) => cs.into_raw(),
            Err(_) => ptr::null_mut(),
        },
        Err(_) => ptr::null_mut(),
    }
}

#[no_mangle]
pub extern "C" fn wqa_report_to_ultra_compact_json(report: *const CReport) -> *mut c_char {
    if report.is_null() {
        return ptr::null_mut();
    }
    let r = match unsafe { cast_report(report) } {
        Some(r) => r,
        None => return ptr::null_mut(),
    };
    let sel = FieldSelector::builder()
        .include_fields(["url", "score", "verdict", "version"])
        .include_field("metrics.html_analysis.content.word_count")
        .build();
    let opts = SerializationOptions {
        compact: true,
        skip_empty_fields: true,
        minimal_output: true,
        streaming: false,
        buffer_size: 4096,
    };
    match OptimizedSerializer::serialize_with_selector(&r.report, &sel, Some(&opts)) {
        Ok(j) => match CString::new(j) {
            Ok(cs) => cs.into_raw(),
            Err(_) => ptr::null_mut(),
        },
        Err(_) => ptr::null_mut(),
    }
}