use crate::status::KgliteStatusCode;
use crate::strings::alloc_c_string;
use kglite::api::datasets::sodir::{fetch_all_blocking, FetchAllReport, Workdir};
use std::ffi::{c_char, CStr};
#[no_mangle]
pub unsafe extern "C" fn kglite_datasets_sodir_fetch_all(
workdir_path: *const c_char,
datasets_json: *const c_char,
index_cooldown_days: i64,
dataset_cooldown_days: i64,
concurrency: usize,
out_report_json: *mut *const c_char,
out_error_msg: *mut *const c_char,
) -> KgliteStatusCode {
if workdir_path.is_null() || datasets_json.is_null() || out_report_json.is_null() {
return KgliteStatusCode::NullPointer;
}
let workdir_str = match unsafe { CStr::from_ptr(workdir_path) }.to_str() {
Ok(s) => s,
Err(_) => return KgliteStatusCode::InvalidUtf8,
};
let datasets_str = match unsafe { CStr::from_ptr(datasets_json) }.to_str() {
Ok(s) => s,
Err(_) => return KgliteStatusCode::InvalidUtf8,
};
let datasets: Vec<String> = match serde_json::from_str(datasets_str) {
Ok(v) => v,
Err(_) => return KgliteStatusCode::InvalidArgument,
};
let workdir = Workdir::new(workdir_str);
match fetch_all_blocking(
&workdir,
&datasets,
index_cooldown_days,
dataset_cooldown_days,
concurrency,
) {
Ok(report) => {
let json = serialize_fetch_all_report(&report);
unsafe {
*out_report_json = alloc_c_string(&json);
}
if !out_error_msg.is_null() {
unsafe {
*out_error_msg = std::ptr::null();
}
}
KgliteStatusCode::Ok
}
Err(err) => {
unsafe {
*out_report_json = std::ptr::null();
}
if !out_error_msg.is_null() {
unsafe {
*out_error_msg = alloc_c_string(&err.to_string());
}
}
KgliteStatusCode::Internal
}
}
}
fn serialize_fetch_all_report(report: &FetchAllReport) -> String {
let refresh = &report.refresh;
let preprocess = &report.preprocess;
let errors_json = serde_json::Value::Array(
refresh
.errors
.iter()
.map(|(stem, msg)| {
serde_json::Value::Array(vec![
serde_json::Value::String(stem.clone()),
serde_json::Value::String(msg.clone()),
])
})
.collect(),
);
let value = serde_json::json!({
"refresh": {
"fetched": refresh.fetched,
"unchanged": refresh.unchanged,
"user_supplied": refresh.user_supplied,
"cached": refresh.cached,
"unfetchable": refresh.unfetchable,
"errors": errors_json,
},
"preprocess": {
"petreg_licence_pk": preprocess.petreg_licence_pk,
"seismic_progress_fk": preprocess.seismic_progress_fk,
"chrono_parent_fk": preprocess.chrono_parent_fk,
"announced_block_fk": preprocess.announced_block_fk,
}
});
serde_json::to_string(&value).unwrap_or_else(|_| "{}".to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serialize_default_report() {
let report = FetchAllReport::default();
let json = serialize_fetch_all_report(&report);
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert!(parsed["refresh"].is_object());
assert!(parsed["preprocess"].is_object());
assert_eq!(parsed["refresh"]["fetched"], serde_json::json!([]));
assert_eq!(
parsed["preprocess"]["petreg_licence_pk"],
serde_json::Value::Null
);
}
#[test]
fn null_pointer_returns_null_pointer_status() {
let mut out_report: *const c_char = std::ptr::null();
let mut out_err: *const c_char = std::ptr::null();
let rc = unsafe {
kglite_datasets_sodir_fetch_all(
std::ptr::null(),
std::ptr::null(),
7,
30,
10,
&mut out_report as *mut _,
&mut out_err as *mut _,
)
};
assert_eq!(rc, KgliteStatusCode::NullPointer);
}
#[test]
fn bad_datasets_json_returns_invalid_argument() {
use std::ffi::CString;
let workdir = CString::new("/tmp/kglite_c_sodir_bad_json").unwrap();
let bad_json = CString::new("not-json").unwrap();
let mut out_report: *const c_char = std::ptr::null();
let mut out_err: *const c_char = std::ptr::null();
let rc = unsafe {
kglite_datasets_sodir_fetch_all(
workdir.as_ptr(),
bad_json.as_ptr(),
7,
30,
10,
&mut out_report as *mut _,
&mut out_err as *mut _,
)
};
assert_eq!(rc, KgliteStatusCode::InvalidArgument);
}
}