#![allow(unsafe_code)]
#![allow(non_camel_case_types)]
#![allow(dead_code)]
use std::collections::BTreeMap;
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_int, c_void};
use evorule_tcb::JsonValue;
use crate::Reactor;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub enum evorule_error_code {
EVORULE_OK = 0,
EVORULE_ERROR_OOM = 1,
EVORULE_ERROR_INVALID_ARG = 2,
EVORULE_ERROR_RUNTIME = 3,
EVORULE_ERROR_NOT_INITIALIZED = 4,
}
pub type evorule_reactor = c_void;
pub type evorule_result = c_void;
#[no_mangle]
pub extern "C" fn evorule_version() -> *const c_char {
const VERSION: &str = env!("CARGO_PKG_VERSION");
match CString::new(VERSION) {
Ok(s) => s.into_raw(),
Err(_) => std::ptr::null(),
}
}
#[no_mangle]
pub extern "C" fn evorule_free_string(s: *mut c_char) {
if !s.is_null() {
unsafe { drop(CString::from_raw(s)) };
}
}
struct ReactorFfiHandle {
_handle: crate::ReactorHandle,
_runtime: tokio::runtime::Runtime,
command_tx: crate::FactSender,
snapshot: std::sync::Arc<std::sync::Mutex<crate::ReactorStateSnapshot>>,
}
#[no_mangle]
pub extern "C" fn evorule_reactor_new() -> *mut evorule_reactor {
let runtime = match tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
{
Ok(r) => r,
Err(_) => return std::ptr::null_mut(),
};
let reactor = Reactor::builder(Vec::new()).build();
let (command_tx, _event_rx, _event_tx, handle, facts_log) = reactor.spawn();
let snapshot =
std::sync::Arc::new(std::sync::Mutex::new(crate::ReactorStateSnapshot::default()));
let _facts_log = facts_log;
let ffi_handle = Box::new(ReactorFfiHandle {
_handle: handle,
_runtime: runtime,
command_tx,
snapshot,
});
Box::into_raw(ffi_handle) as *mut evorule_reactor
}
#[no_mangle]
pub extern "C" fn evorule_reactor_free(reactor: *mut evorule_reactor) {
if reactor.is_null() {
return;
}
unsafe {
let handle = Box::from_raw(reactor as *mut ReactorFfiHandle);
drop(handle);
}
}
#[no_mangle]
pub extern "C" fn evorule_reactor_send_command(
reactor: *mut evorule_reactor,
instruction_json: *const c_char,
) -> evorule_error_code {
if reactor.is_null() || instruction_json.is_null() {
return evorule_error_code::EVORULE_ERROR_INVALID_ARG;
}
let handle = unsafe { &mut *(reactor as *mut ReactorFfiHandle) };
let json_str = unsafe { CStr::from_ptr(instruction_json) };
let json_str = match json_str.to_str() {
Ok(s) => s,
Err(_) => return evorule_error_code::EVORULE_ERROR_INVALID_ARG,
};
let instruction = match parse_simple_json(json_str) {
Some(v) => v,
None => return evorule_error_code::EVORULE_ERROR_INVALID_ARG,
};
let fact = crate::Fact::Command {
id: crate::FactIdGenerator::new().next_id(),
instruction,
};
match handle.command_tx.send(fact) {
Ok(()) => evorule_error_code::EVORULE_OK,
Err(_) => evorule_error_code::EVORULE_ERROR_RUNTIME,
}
}
fn parse_simple_json(s: &str) -> Option<JsonValue> {
let s = s.trim();
if s.starts_with('{') && s.ends_with('}') {
parse_json_object(s)
} else if s.starts_with('"') && s.ends_with('"') {
Some(JsonValue::string(&s[1..s.len() - 1]))
} else if s.starts_with('[') && s.ends_with(']') {
Some(JsonValue::Array(vec![]))
} else if s == "true" {
Some(JsonValue::bool(true))
} else if s == "false" {
Some(JsonValue::bool(false))
} else if s == "null" {
Some(JsonValue::null())
} else if let Ok(n) = s.parse::<i64>() {
Some(JsonValue::integer(n))
} else {
None
}
}
fn parse_json_object(s: &str) -> Option<JsonValue> {
let s = s.trim();
if !s.starts_with('{') || !s.ends_with('}') {
return None;
}
let inner = &s[1..s.len() - 1].trim();
if inner.is_empty() {
return Some(JsonValue::Object(BTreeMap::new()));
}
let mut obj = BTreeMap::new();
for part in inner.split(',') {
let part = part.trim();
if part.is_empty() {
continue;
}
let colon_pos = part.find(':')?;
let key_part = part[..colon_pos].trim();
let val_part = part[colon_pos + 1..].trim();
if !key_part.starts_with('"') || !key_part.ends_with('"') {
return None;
}
let key = &key_part[1..key_part.len() - 1];
let val = parse_simple_json(val_part)?;
obj.insert(key.to_string(), val);
}
Some(JsonValue::Object(obj))
}
struct ResultHandle {
output: String,
}
#[no_mangle]
pub extern "C" fn evorule_result_get_output(result: *mut evorule_result) -> *const c_char {
if result.is_null() {
return std::ptr::null();
}
let handle = unsafe { &*(result as *mut ResultHandle) };
match CString::new(handle.output.clone()) {
Ok(s) => s.into_raw(),
Err(_) => std::ptr::null(),
}
}
#[no_mangle]
pub extern "C" fn evorule_result_free(result: *mut evorule_result) {
if result.is_null() {
return;
}
unsafe {
let handle = Box::from_raw(result as *mut ResultHandle);
drop(handle);
}
}
#[no_mangle]
pub extern "C" fn evorule_reactor_current_queue_size(reactor: *mut evorule_reactor) -> c_int {
if reactor.is_null() {
return -1;
}
let handle = unsafe { &*(reactor as *mut ReactorFfiHandle) };
handle
.snapshot
.lock()
.map(|s| s.queue_len as c_int)
.unwrap_or(-1)
}