use std::os::raw::c_char;
use std::ptr;
use super::conversions::c_str_to_rust;
use super::errors::set_last_error;
use super::types::{CReportArray, CStringArray};
#[no_mangle]
pub extern "C" fn wqa_analyze_batch_parallel(
urls: *const CStringArray,
max_concurrent: usize,
) -> *mut CReportArray {
if urls.is_null() {
set_last_error("Null URLs array provided".to_string());
return ptr::null_mut();
}
let url_array = unsafe { &*urls };
let url_vec: Vec<String> = unsafe {
if url_array.data.is_null() || url_array.length == 0 {
set_last_error("Empty URLs array".to_string());
return ptr::null_mut();
}
let url_ptrs = std::slice::from_raw_parts(url_array.data, url_array.length);
url_ptrs
.iter()
.filter_map(|&ptr| c_str_to_rust(ptr))
.map(|s| s.to_string())
.collect()
};
let concurrent_limit = if max_concurrent == 0 {
10
} else {
max_concurrent
};
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();
}
};
use futures::stream::{self, StreamExt};
use std::sync::Arc;
use tokio::sync::Semaphore;
let result = runtime.block_on(async {
let semaphore = Arc::new(Semaphore::new(concurrent_limit));
let tasks: Vec<_> = url_vec
.iter()
.map(|url| {
let url = url.clone();
let semaphore = Arc::clone(&semaphore);
async move {
let _permit = semaphore.acquire().await.ok()?;
crate::analyze(&url, None).await.ok()
}
})
.collect();
stream::iter(tasks)
.buffer_unordered(concurrent_limit)
.collect::<Vec<_>>()
.await
});
let reports: Vec<_> = result.into_iter().filter_map(|r| r).collect();
if reports.is_empty() {
set_last_error("All analyses failed".to_string());
return ptr::null_mut();
}
CReportArray::from_vec(reports)
}
#[no_mangle]
pub extern "C" fn wqa_free_report_array(arr: *mut CReportArray) {
unsafe {
CReportArray::free(arr);
}
}
#[no_mangle]
pub extern "C" fn wqa_free_string_array(arr: *mut CStringArray) {
unsafe {
CStringArray::free(arr);
}
}