use std::ffi::{CStr, CString, c_char};
fn json(r: anyhow::Result<crate::checks::Report>) -> *mut c_char {
let text = match r.and_then(|r| Ok(serde_json::to_string(&r)?)) {
Ok(t) => t,
Err(e) => serde_json::json!({ "error": format!("{e:#}") }).to_string(),
};
CString::new(text)
.map(CString::into_raw)
.unwrap_or(std::ptr::null_mut())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gpv_check(target: *const c_char, max_rows: u64) -> *mut c_char {
if target.is_null() {
return json(Err(anyhow::anyhow!("target is NULL")));
}
let target = unsafe { CStr::from_ptr(target) }
.to_string_lossy()
.into_owned();
let max_rows = (max_rows > 0).then_some(max_rows as usize);
json(crate::validate(&target, max_rows))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gpv_check_bytes(
name: *const c_char,
data: *const u8,
len: usize,
max_rows: u64,
) -> *mut c_char {
if data.is_null() {
return json(Err(anyhow::anyhow!("data is NULL")));
}
let name = if name.is_null() {
"memory".to_string()
} else {
unsafe { CStr::from_ptr(name) }
.to_string_lossy()
.into_owned()
};
let bytes = unsafe { std::slice::from_raw_parts(data, len) }.to_vec();
let max_rows = (max_rows > 0).then_some(max_rows as usize);
json(crate::validate_bytes(&name, bytes, max_rows))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gpv_free(s: *mut c_char) {
if !s.is_null() {
drop(unsafe { CString::from_raw(s) });
}
}
#[unsafe(no_mangle)]
pub extern "C" fn gpv_version() -> *const c_char {
concat!(env!("CARGO_PKG_VERSION"), "\0").as_ptr() as *const c_char
}