use super::types::{FFIResult, JSONEvalHandle};
use crate::JSONEval;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::ptr;
#[no_mangle]
pub extern "C" fn json_eval_version() -> *const c_char {
concat!(env!("CARGO_PKG_VERSION"), "\0").as_ptr() as *const c_char
}
#[no_mangle]
pub unsafe extern "C" fn json_eval_new_from_msgpack(
schema_msgpack: *const u8,
schema_len: usize,
context: *const c_char,
data: *const c_char,
) -> *mut JSONEvalHandle {
if schema_msgpack.is_null() || schema_len == 0 {
eprintln!("[FFI ERROR] json_eval_new_from_msgpack: invalid schema pointer or length");
return ptr::null_mut();
}
let schema_bytes = std::slice::from_raw_parts(schema_msgpack, schema_len);
let context_str = if !context.is_null() {
match CStr::from_ptr(context).to_str() {
Ok(s) => Some(s),
Err(e) => {
eprintln!(
"[FFI ERROR] json_eval_new_from_msgpack: invalid UTF-8 in context: {}",
e
);
return ptr::null_mut();
}
}
} else {
None
};
let data_str = if !data.is_null() {
match CStr::from_ptr(data).to_str() {
Ok(s) => Some(s),
Err(e) => {
eprintln!(
"[FFI ERROR] json_eval_new_from_msgpack: invalid UTF-8 in data: {}",
e
);
return ptr::null_mut();
}
}
} else {
None
};
match JSONEval::new_from_msgpack(schema_bytes, context_str, data_str) {
Ok(eval) => {
let handle = Box::new(JSONEvalHandle {
inner: Box::new(eval),
current_token: None,
});
Box::into_raw(handle)
}
Err(e) => {
let error_msg = format!("Failed to create JSONEval instance from MessagePack: {}", e);
eprintln!("[FFI ERROR] json_eval_new_from_msgpack: {}", error_msg);
ptr::null_mut()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn json_eval_new(
schema: *const c_char,
context: *const c_char,
data: *const c_char,
) -> *mut JSONEvalHandle {
if schema.is_null() {
eprintln!("[FFI ERROR] json_eval_new: schema pointer is null");
return ptr::null_mut();
}
let schema_str = match CStr::from_ptr(schema).to_str() {
Ok(s) => s,
Err(e) => {
eprintln!("[FFI ERROR] json_eval_new: invalid UTF-8 in schema: {}", e);
return ptr::null_mut();
}
};
let context_str = if !context.is_null() {
match CStr::from_ptr(context).to_str() {
Ok(s) => Some(s),
Err(e) => {
eprintln!("[FFI ERROR] json_eval_new: invalid UTF-8 in context: {}", e);
return ptr::null_mut();
}
}
} else {
None
};
let data_str = if !data.is_null() {
match CStr::from_ptr(data).to_str() {
Ok(s) => Some(s),
Err(e) => {
eprintln!("[FFI ERROR] json_eval_new: invalid UTF-8 in data: {}", e);
return ptr::null_mut();
}
}
} else {
None
};
match JSONEval::new(schema_str, context_str, data_str) {
Ok(eval) => {
let handle = Box::new(JSONEvalHandle {
inner: Box::new(eval),
current_token: None,
});
Box::into_raw(handle)
}
Err(e) => {
let error_msg = format!("Failed to create JSONEval instance: {}", e);
eprintln!("[FFI ERROR] json_eval_new: {}", error_msg);
ptr::null_mut()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn json_eval_new_with_error(
schema: *const c_char,
context: *const c_char,
data: *const c_char,
error_out: *mut *mut c_char,
) -> *mut JSONEvalHandle {
if schema.is_null() {
if !error_out.is_null() {
*error_out = CString::new("Schema pointer is null").unwrap().into_raw();
}
return ptr::null_mut();
}
let schema_str = match CStr::from_ptr(schema).to_str() {
Ok(s) => s,
Err(e) => {
if !error_out.is_null() {
let msg = format!("Invalid UTF-8 in schema: {}", e);
*error_out = CString::new(msg).unwrap().into_raw();
}
return ptr::null_mut();
}
};
let context_str = if !context.is_null() {
match CStr::from_ptr(context).to_str() {
Ok(s) => Some(s),
Err(e) => {
if !error_out.is_null() {
let msg = format!("Invalid UTF-8 in context: {}", e);
*error_out = CString::new(msg).unwrap().into_raw();
}
return ptr::null_mut();
}
}
} else {
None
};
let data_str = if !data.is_null() {
match CStr::from_ptr(data).to_str() {
Ok(s) => Some(s),
Err(e) => {
if !error_out.is_null() {
let msg = format!("Invalid UTF-8 in data: {}", e);
*error_out = CString::new(msg).unwrap().into_raw();
}
return ptr::null_mut();
}
}
} else {
None
};
match JSONEval::new(schema_str, context_str, data_str) {
Ok(eval) => {
let handle = Box::new(JSONEvalHandle {
inner: Box::new(eval),
current_token: None,
});
Box::into_raw(handle)
}
Err(e) => {
if !error_out.is_null() {
let msg = format!("Failed to create JSONEval instance: {}", e);
*error_out = CString::new(msg).unwrap().into_raw();
}
ptr::null_mut()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn json_eval_free_result(result: super::types::FFIResult) {
if !result._owned_data.is_null() {
drop(Box::from_raw(result._owned_data));
}
if !result.error.is_null() {
drop(CString::from_raw(result.error));
}
}
#[no_mangle]
pub unsafe extern "C" fn json_eval_free_string(ptr: *mut c_char) {
if !ptr.is_null() {
drop(CString::from_raw(ptr));
}
}
#[no_mangle]
pub unsafe extern "C" fn json_eval_free(handle: *mut JSONEvalHandle) {
if !handle.is_null() {
drop(Box::from_raw(handle));
}
}
#[no_mangle]
pub unsafe extern "C" fn json_eval_reload_schema(
handle: *mut JSONEvalHandle,
schema: *const c_char,
context: *const c_char,
data: *const c_char,
) -> FFIResult {
if handle.is_null() || schema.is_null() {
return FFIResult::error("Invalid handle or schema pointer".to_string());
}
let eval = &mut (*handle).inner;
let schema_str = match CStr::from_ptr(schema).to_str() {
Ok(s) => s,
Err(_) => return FFIResult::error("Invalid UTF-8 in schema".to_string()),
};
let context_str = if !context.is_null() {
match CStr::from_ptr(context).to_str() {
Ok(s) => Some(s),
Err(_) => return FFIResult::error("Invalid UTF-8 in context".to_string()),
}
} else {
None
};
let data_str = if !data.is_null() {
match CStr::from_ptr(data).to_str() {
Ok(s) => Some(s),
Err(_) => return FFIResult::error("Invalid UTF-8 in data".to_string()),
}
} else {
None
};
match eval.reload_schema(schema_str, context_str, data_str) {
Ok(_) => FFIResult::success(Vec::new()),
Err(e) => FFIResult::error(e),
}
}
#[no_mangle]
pub unsafe extern "C" fn json_eval_reload_schema_msgpack(
handle: *mut JSONEvalHandle,
schema_msgpack: *const u8,
schema_len: usize,
context: *const c_char,
data: *const c_char,
) -> FFIResult {
if handle.is_null() || schema_msgpack.is_null() || schema_len == 0 {
return FFIResult::error("Invalid handle, schema pointer, or length".to_string());
}
let eval = &mut (*handle).inner;
let schema_bytes = std::slice::from_raw_parts(schema_msgpack, schema_len);
let context_str = if !context.is_null() {
match CStr::from_ptr(context).to_str() {
Ok(s) => Some(s),
Err(_) => return FFIResult::error("Invalid UTF-8 in context".to_string()),
}
} else {
None
};
let data_str = if !data.is_null() {
match CStr::from_ptr(data).to_str() {
Ok(s) => Some(s),
Err(_) => return FFIResult::error("Invalid UTF-8 in data".to_string()),
}
} else {
None
};
match eval.reload_schema_msgpack(schema_bytes, context_str, data_str) {
Ok(_) => FFIResult::success(Vec::new()),
Err(e) => FFIResult::error(e),
}
}
#[no_mangle]
pub unsafe extern "C" fn json_eval_new_from_cache(
cache_key: *const c_char,
context: *const c_char,
data: *const c_char,
) -> *mut JSONEvalHandle {
if cache_key.is_null() {
eprintln!("[FFI ERROR] json_eval_new_from_cache: cache_key pointer is null");
return ptr::null_mut();
}
let key_str = match CStr::from_ptr(cache_key).to_str() {
Ok(s) => s,
Err(e) => {
eprintln!(
"[FFI ERROR] json_eval_new_from_cache: invalid UTF-8 in cache_key: {}",
e
);
return ptr::null_mut();
}
};
let context_str = if !context.is_null() {
match CStr::from_ptr(context).to_str() {
Ok(s) => Some(s),
Err(e) => {
eprintln!(
"[FFI ERROR] json_eval_new_from_cache: invalid UTF-8 in context: {}",
e
);
return ptr::null_mut();
}
}
} else {
None
};
let data_str = if !data.is_null() {
match CStr::from_ptr(data).to_str() {
Ok(s) => Some(s),
Err(e) => {
eprintln!(
"[FFI ERROR] json_eval_new_from_cache: invalid UTF-8 in data: {}",
e
);
return ptr::null_mut();
}
}
} else {
None
};
let parsed = match crate::PARSED_SCHEMA_CACHE.get(key_str) {
Some(p) => p,
None => {
eprintln!(
"[FFI ERROR] json_eval_new_from_cache: schema '{}' not found in cache",
key_str
);
return ptr::null_mut();
}
};
match crate::JSONEval::with_parsed_schema(parsed, context_str, data_str) {
Ok(eval) => {
let handle = Box::new(JSONEvalHandle {
inner: Box::new(eval),
current_token: None,
});
Box::into_raw(handle)
}
Err(e) => {
let error_msg = format!("Failed to create JSONEval instance from cache: {}", e);
eprintln!("[FFI ERROR] json_eval_new_from_cache: {}", error_msg);
ptr::null_mut()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn json_eval_new_from_cache_with_error(
cache_key: *const c_char,
context: *const c_char,
data: *const c_char,
error_out: *mut *mut c_char,
) -> *mut JSONEvalHandle {
if cache_key.is_null() {
if !error_out.is_null() {
*error_out = CString::new("cache_key pointer is null")
.unwrap()
.into_raw();
}
return ptr::null_mut();
}
let key_str = match CStr::from_ptr(cache_key).to_str() {
Ok(s) => s,
Err(e) => {
if !error_out.is_null() {
let error_msg = format!("Invalid UTF-8 in cache_key: {}", e);
*error_out = CString::new(error_msg).unwrap().into_raw();
}
return ptr::null_mut();
}
};
let context_str = if !context.is_null() {
match CStr::from_ptr(context).to_str() {
Ok(s) => Some(s),
Err(e) => {
if !error_out.is_null() {
let error_msg = format!("Invalid UTF-8 in context: {}", e);
*error_out = CString::new(error_msg).unwrap().into_raw();
}
return ptr::null_mut();
}
}
} else {
None
};
let data_str = if !data.is_null() {
match CStr::from_ptr(data).to_str() {
Ok(s) => Some(s),
Err(e) => {
if !error_out.is_null() {
let error_msg = format!("Invalid UTF-8 in data: {}", e);
*error_out = CString::new(error_msg).unwrap().into_raw();
}
return ptr::null_mut();
}
}
} else {
None
};
let parsed = match crate::PARSED_SCHEMA_CACHE.get(key_str) {
Some(p) => p,
None => {
if !error_out.is_null() {
let error_msg = format!("Schema '{}' not found in cache", key_str);
*error_out = CString::new(error_msg).unwrap().into_raw();
}
return ptr::null_mut();
}
};
match crate::JSONEval::with_parsed_schema(parsed, context_str, data_str) {
Ok(eval) => {
if !error_out.is_null() {
*error_out = ptr::null_mut(); }
let handle = Box::new(JSONEvalHandle {
inner: Box::new(eval),
current_token: None,
});
Box::into_raw(handle)
}
Err(e) => {
if !error_out.is_null() {
let error_msg = format!("Failed to create JSONEval from cache: {}", e);
*error_out = CString::new(error_msg).unwrap().into_raw();
}
ptr::null_mut()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn json_eval_reload_schema_from_cache(
handle: *mut JSONEvalHandle,
cache_key: *const c_char,
context: *const c_char,
data: *const c_char,
) -> FFIResult {
if handle.is_null() || cache_key.is_null() {
return FFIResult::error("Invalid handle or cache_key".to_string());
}
let eval = &mut (*handle).inner;
let key_str = match CStr::from_ptr(cache_key).to_str() {
Ok(s) => s,
Err(_) => return FFIResult::error("Invalid UTF-8 in cache_key".to_string()),
};
let context_str = if !context.is_null() {
match CStr::from_ptr(context).to_str() {
Ok(s) => Some(s),
Err(_) => return FFIResult::error("Invalid UTF-8 in context".to_string()),
}
} else {
None
};
let data_str = if !data.is_null() {
match CStr::from_ptr(data).to_str() {
Ok(s) => Some(s),
Err(_) => return FFIResult::error("Invalid UTF-8 in data".to_string()),
}
} else {
None
};
match eval.reload_schema_from_cache(key_str, context_str, data_str) {
Ok(_) => FFIResult::success(Vec::new()),
Err(e) => FFIResult::error(e),
}
}
#[no_mangle]
pub unsafe extern "C" fn json_eval_set_timezone_offset(
handle: *mut JSONEvalHandle,
offset_minutes: i32,
) {
if handle.is_null() {
eprintln!("[FFI ERROR] json_eval_set_timezone_offset: handle is null");
return;
}
let eval = &mut (*handle).inner;
let offset = if offset_minutes == i32::MIN {
None
} else {
Some(offset_minutes)
};
eval.set_timezone_offset(offset);
}
#[no_mangle]
pub unsafe extern "C" fn json_eval_cancel(handle: *mut JSONEvalHandle) {
if handle.is_null() {
return;
}
if let Some(token) = &(*handle).current_token {
token.cancel();
}
}