use std::ffi::CString;
use std::os::raw::c_char;
use std::ptr;
use crate::utils::json_optimizer::{FieldSelector, FieldSelectorBuilder, OptimizedSerializer, SerializationOptions};
use super::types::{CFieldSelector, CReport};
use super::conversions::{c_str_to_rust, cast_report};
use super::errors::CErrorCode;
pub(crate) struct FieldSelectorInternal {
pub include_fields: Vec<String>,
pub exclude_fields: Vec<String>,
pub include_sections: Vec<String>,
pub exclude_sections: Vec<String>,
}
impl FieldSelectorInternal {
fn new() -> Self {
Self {
include_fields: Vec::new(),
exclude_fields: Vec::new(),
include_sections: Vec::new(),
exclude_sections: Vec::new(),
}
}
fn to_field_selector(&self) -> FieldSelector {
let mut builder = FieldSelector::builder();
for field in &self.include_fields {
builder = builder.include_field(field.clone());
}
for field in &self.exclude_fields {
builder = builder.exclude_field(field.clone());
}
for section in &self.include_sections {
builder = builder.include_section(section.clone());
}
for section in &self.exclude_sections {
builder = builder.exclude_section(section.clone());
}
builder.build()
}
}
#[no_mangle]
pub extern "C" fn wqa_field_selector_new() -> *mut CFieldSelector {
let internal = Box::new(FieldSelectorInternal::new());
Box::into_raw(internal) as *mut CFieldSelector
}
#[no_mangle]
pub extern "C" fn wqa_field_selector_free(selector: *mut CFieldSelector) {
if selector.is_null() {
return;
}
unsafe {
let _ = Box::from_raw(selector as *mut FieldSelectorInternal);
}
}
#[no_mangle]
pub extern "C" fn wqa_field_selector_include_field(
selector: *mut CFieldSelector,
field: *const c_char,
) -> CErrorCode {
if selector.is_null() || field.is_null() {
return CErrorCode::InvalidPointer;
}
let field_str = match c_str_to_rust(field) {
Ok(s) => s,
Err(_) => return CErrorCode::Utf8Error,
};
unsafe {
let internal = &mut *(selector as *mut FieldSelectorInternal);
internal.include_fields.push(field_str);
}
CErrorCode::Success
}
#[no_mangle]
pub extern "C" fn wqa_field_selector_exclude_field(
selector: *mut CFieldSelector,
field: *const c_char,
) -> CErrorCode {
if selector.is_null() || field.is_null() {
return CErrorCode::InvalidPointer;
}
let field_str = match c_str_to_rust(field) {
Ok(s) => s,
Err(_) => return CErrorCode::Utf8Error,
};
unsafe {
let internal = &mut *(selector as *mut FieldSelectorInternal);
internal.exclude_fields.push(field_str);
}
CErrorCode::Success
}
#[no_mangle]
pub extern "C" fn wqa_field_selector_include_section(
selector: *mut CFieldSelector,
section: *const c_char,
) -> CErrorCode {
if selector.is_null() || section.is_null() {
return CErrorCode::InvalidPointer;
}
let section_str = match c_str_to_rust(section) {
Ok(s) => s,
Err(_) => return CErrorCode::Utf8Error,
};
unsafe {
let internal = &mut *(selector as *mut FieldSelectorInternal);
internal.include_sections.push(section_str);
}
CErrorCode::Success
}
#[no_mangle]
pub extern "C" fn wqa_field_selector_exclude_section(
selector: *mut CFieldSelector,
section: *const c_char,
) -> CErrorCode {
if selector.is_null() || section.is_null() {
return CErrorCode::InvalidPointer;
}
let section_str = match c_str_to_rust(section) {
Ok(s) => s,
Err(_) => return CErrorCode::Utf8Error,
};
unsafe {
let internal = &mut *(selector as *mut FieldSelectorInternal);
internal.exclude_sections.push(section_str);
}
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 report_internal = match unsafe { cast_report(report) } {
Some(r) => r,
None => return ptr::null_mut(),
};
let json_result = if selector.is_null() {
if compact {
serde_json::to_string(&report_internal.report)
} else {
serde_json::to_string_pretty(&report_internal.report)
}
} else {
let selector_internal = unsafe { &*(selector as *const FieldSelectorInternal) };
let field_selector = selector_internal.to_field_selector();
let opts = SerializationOptions {
compact,
skip_empty_fields: true,
minimal_output: false,
streaming: false,
buffer_size: 8192,
};
OptimizedSerializer::serialize_with_selector(
&report_internal.report,
&field_selector,
Some(&opts)
)
};
match json_result {
Ok(json) => match CString::new(json) {
Ok(c_string) => c_string.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 report_internal = match unsafe { cast_report(report) } {
Some(r) => r,
None => return ptr::null_mut(),
};
let selector = FieldSelector::builder()
.include_fields(["url", "score", "verdict", "version"])
.include_field("metrics.html_analysis.content.word_count")
.include_field("metrics.html_analysis.seo.title_length")
.include_field("metrics.html_analysis.technical.html_bytes")
.build();
let opts = SerializationOptions {
compact: true,
skip_empty_fields: true,
minimal_output: true,
streaming: false,
buffer_size: 4096,
};
match OptimizedSerializer::serialize_with_selector(&report_internal.report, &selector, Some(&opts)) {
Ok(json) => match CString::new(json) {
Ok(c_string) => c_string.into_raw(),
Err(_) => ptr::null_mut(),
},
Err(_) => ptr::null_mut(),
}
}