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 Builder Pattern (Level 2 API)
//!
//! Builder pattern implementation for C/C++ with method chaining support.

use std::os::raw::c_char;
use std::ptr;

use super::conversions::{c_str_to_rust, cast_builder, cast_builder_mut};
use super::errors::set_last_error;
use super::types::{AnalyzerInternal, BuilderInternal, CAnalyzer, CAnalyzerBuilder, CErrorCode};
use crate::async_runtime::DefaultRuntime;
use crate::AnalyzerBuilder;

/// Create a new analyzer builder
///
/// # Returns
/// Pointer to CAnalyzerBuilder on success, NULL on failure.
/// Caller must either call `wqa_builder_build()` or `wqa_builder_free()`.
///
/// # Example (C)
/// ```c
/// CAnalyzerBuilder* builder = wqa_builder_new();
/// wqa_builder_with_profile(builder, "news");
/// wqa_builder_disable_metric(builder, "grammar_score");
/// CAnalyzer* analyzer = wqa_builder_build(builder);
/// // builder is consumed by build(), don't free it
/// ```
#[no_mangle]
pub extern "C" fn wqa_builder_new() -> *mut CAnalyzerBuilder {
    let builder = AnalyzerBuilder::<DefaultRuntime>::default();
    let internal = Box::new(BuilderInternal { builder });
    Box::into_raw(internal) as *mut CAnalyzerBuilder
}

/// Set the profile by name
///
/// # Arguments
/// * `builder` - Builder pointer
/// * `profile` - Profile name (e.g., "news", "blog", "ecommerce")
///
/// # Returns
/// CErrorCode::Success on success, error code on failure
#[no_mangle]
pub extern "C" fn wqa_builder_with_profile(
    builder: *mut CAnalyzerBuilder,
    profile: *const c_char,
) -> CErrorCode {
    if builder.is_null() || profile.is_null() {
        set_last_error("Null pointer provided".to_string());
        return CErrorCode::InvalidPointer;
    }

    let profile_str = unsafe {
        match c_str_to_rust(profile) {
            Some(s) => s,
            None => {
                set_last_error("Invalid UTF-8 in profile name".to_string());
                return CErrorCode::Utf8Error;
            }
        }
    };

    let internal = unsafe {
        match cast_builder_mut(builder) {
            Some(b) => b,
            None => return CErrorCode::InvalidPointer,
        }
    };

    // Replace the builder with a temporary one, call method, then replace back
    let temp_builder = std::mem::replace(
        &mut internal.builder,
        AnalyzerBuilder::<DefaultRuntime>::default(),
    );
    match temp_builder.with_profile_name(profile_str) {
        Ok(new_builder) => {
            internal.builder = new_builder;
            CErrorCode::Success
        }
        Err(e) => {
            set_last_error(format!("{}", e));
            // Restore original builder on error
            internal.builder = AnalyzerBuilder::<DefaultRuntime>::default();
            CErrorCode::InvalidProfile
        }
    }
}

/// Disable a specific metric
///
/// # Arguments
/// * `builder` - Builder pointer
/// * `metric` - Metric name (e.g., "word_count", "grammar_score")
#[no_mangle]
pub extern "C" fn wqa_builder_disable_metric(
    builder: *mut CAnalyzerBuilder,
    metric: *const c_char,
) -> CErrorCode {
    if builder.is_null() || metric.is_null() {
        set_last_error("Null pointer provided".to_string());
        return CErrorCode::InvalidPointer;
    }

    let metric_str = unsafe {
        match c_str_to_rust(metric) {
            Some(s) => s,
            None => {
                set_last_error("Invalid UTF-8 in metric name".to_string());
                return CErrorCode::Utf8Error;
            }
        }
    };

    let internal = unsafe {
        match cast_builder_mut(builder) {
            Some(b) => b,
            None => return CErrorCode::InvalidPointer,
        }
    };

    let temp_builder = std::mem::replace(
        &mut internal.builder,
        AnalyzerBuilder::<DefaultRuntime>::default(),
    );
    match temp_builder.disable_metric(metric_str) {
        Ok(new_builder) => {
            internal.builder = new_builder;
            CErrorCode::Success
        }
        Err(e) => {
            set_last_error(format!("{}", e));
            internal.builder = AnalyzerBuilder::<DefaultRuntime>::default();
            CErrorCode::MetricNotFound
        }
    }
}

/// Enable a specific metric
#[no_mangle]
pub extern "C" fn wqa_builder_enable_metric(
    builder: *mut CAnalyzerBuilder,
    metric: *const c_char,
) -> CErrorCode {
    if builder.is_null() || metric.is_null() {
        set_last_error("Null pointer provided".to_string());
        return CErrorCode::InvalidPointer;
    }

    let metric_str = unsafe {
        match c_str_to_rust(metric) {
            Some(s) => s,
            None => {
                set_last_error("Invalid UTF-8 in metric name".to_string());
                return CErrorCode::Utf8Error;
            }
        }
    };

    let internal = unsafe {
        match cast_builder_mut(builder) {
            Some(b) => b,
            None => return CErrorCode::InvalidPointer,
        }
    };

    let temp_builder = std::mem::replace(
        &mut internal.builder,
        AnalyzerBuilder::<DefaultRuntime>::default(),
    );
    match temp_builder.enable_metric(metric_str) {
        Ok(new_builder) => {
            internal.builder = new_builder;
            CErrorCode::Success
        }
        Err(e) => {
            set_last_error(format!("{}", e));
            internal.builder = AnalyzerBuilder::<DefaultRuntime>::default();
            CErrorCode::MetricNotFound
        }
    }
}

/// Set metric weight (Phase 4 customization)
#[no_mangle]
pub extern "C" fn wqa_builder_set_metric_weight(
    builder: *mut CAnalyzerBuilder,
    metric: *const c_char,
    weight: f32,
) -> CErrorCode {
    if builder.is_null() || metric.is_null() {
        set_last_error("Null pointer provided".to_string());
        return CErrorCode::InvalidPointer;
    }

    let metric_str = unsafe {
        match c_str_to_rust(metric) {
            Some(s) => s,
            None => {
                set_last_error("Invalid UTF-8 in metric name".to_string());
                return CErrorCode::Utf8Error;
            }
        }
    };

    let internal = unsafe {
        match cast_builder_mut(builder) {
            Some(b) => b,
            None => return CErrorCode::InvalidPointer,
        }
    };

    let temp_builder = std::mem::replace(
        &mut internal.builder,
        AnalyzerBuilder::<DefaultRuntime>::default(),
    );
    match temp_builder.set_metric_weight(metric_str, weight) {
        Ok(new_builder) => {
            internal.builder = new_builder;
            CErrorCode::Success
        }
        Err(e) => {
            set_last_error(format!("{}", e));
            internal.builder = AnalyzerBuilder::<DefaultRuntime>::default();
            CErrorCode::MetricNotFound
        }
    }
}

/// Build the analyzer (consumes the builder)
///
/// # Arguments
/// * `builder` - Builder pointer (will be freed by this function)
///
/// # Returns
/// Pointer to CAnalyzer on success, NULL on failure.
/// Caller must free with `wqa_free_analyzer()`.
///
/// # Note
/// The builder is consumed and freed by this function, whether it succeeds or fails.
#[no_mangle]
pub extern "C" fn wqa_builder_build(builder: *mut CAnalyzerBuilder) -> *mut CAnalyzer {
    if builder.is_null() {
        set_last_error("Null builder provided".to_string());
        return ptr::null_mut();
    }

    let internal = unsafe { Box::from_raw(builder as *mut BuilderInternal) };

    match internal.builder.build() {
        Ok(analyzer) => {
            // Create runtime for analyzer
            let runtime = match tokio::runtime::Runtime::new() {
                Ok(rt) => rt,
                Err(e) => {
                    set_last_error(format!("Failed to create runtime: {}", e));
                    return ptr::null_mut();
                }
            };

            let analyzer_internal = Box::new(AnalyzerInternal { analyzer, runtime });
            Box::into_raw(analyzer_internal) as *mut CAnalyzer
        }
        Err(e) => {
            set_last_error(format!("{}", e));
            ptr::null_mut()
        }
    }
}

/// Free analyzer
#[no_mangle]
pub extern "C" fn wqa_free_analyzer(analyzer: *mut CAnalyzer) {
    if !analyzer.is_null() {
        unsafe {
            let _ = Box::from_raw(analyzer as *mut AnalyzerInternal);
        }
    }
}

/// Free builder (if not consumed by build)
#[no_mangle]
pub extern "C" fn wqa_builder_free(builder: *mut CAnalyzerBuilder) {
    if !builder.is_null() {
        unsafe {
            let _ = Box::from_raw(builder as *mut BuilderInternal);
        }
    }
}