use std::ffi::{CStr, c_char};
use std::sync::{Arc, Mutex};
use tokio::runtime::{Builder, Runtime};
#[cfg(feature = "tracing")]
use ombrac_macros::{error, info};
use crate::config::{ServiceConfig, load_from_json};
#[cfg(feature = "tracing")]
use crate::logging::LogCallback;
use crate::service::OmbracServer;
static SERVICE_HANDLE: Mutex<Option<ServiceHandle>> = Mutex::new(None);
struct ServiceHandle {
service: Option<OmbracServer>,
runtime: Runtime,
}
unsafe fn c_str_to_string(s: *const c_char) -> String {
if s.is_null() {
return String::new();
}
unsafe { CStr::from_ptr(s).to_str().unwrap_or("").to_string() }
}
#[cfg(feature = "tracing")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ombrac_server_set_log_callback(callback: *const LogCallback) {
let _ = std::panic::catch_unwind(|| {
let callback = if callback.is_null() {
None
} else {
Some(unsafe { *callback })
};
crate::logging::set_log_callback(callback);
});
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ombrac_server_service_startup(config_json: *const c_char) -> i32 {
let result = std::panic::catch_unwind(|| {
let config_str = unsafe { c_str_to_string(config_json) };
let service_config: ServiceConfig = match load_from_json(&config_str) {
Ok(cfg) => cfg,
Err(e) => {
#[cfg(feature = "tracing")]
error!("Failed to parse config JSON: {}", e);
return -1;
}
};
#[cfg(feature = "tracing")]
crate::logging::init_for_ffi(&service_config.logging);
let runtime = match Builder::new_multi_thread().enable_all().build() {
Ok(rt) => rt,
Err(_e) => {
#[cfg(feature = "tracing")]
error!("Failed to create Tokio runtime: {_e}");
return -1;
}
};
let service =
runtime.block_on(async { OmbracServer::build(Arc::new(service_config)).await });
let service = match service {
Ok(s) => s,
Err(e) => {
#[cfg(feature = "tracing")]
error!("Failed to build service: {}", e);
return -1;
}
};
let mut handle_guard = SERVICE_HANDLE.lock().unwrap();
if handle_guard.is_some() {
#[cfg(feature = "tracing")]
error!("Service is already running. Please shut down the existing service first.");
return -1;
}
*handle_guard = Some(ServiceHandle {
service: Some(service),
runtime,
});
#[cfg(feature = "tracing")]
info!("Service started successfully");
0
});
match result {
Ok(ret) => ret,
Err(_) => {
#[cfg(feature = "tracing")]
error!("Panic occurred in ombrac_server_service_startup");
-1
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn ombrac_server_service_shutdown() -> i32 {
let result = std::panic::catch_unwind(|| {
let mut handle_guard = SERVICE_HANDLE.lock().unwrap();
if let Some(mut handle) = handle_guard.take() {
#[cfg(feature = "tracing")]
info!("Shutting down service");
if let Some(service) = handle.service.take() {
let shutdown_result =
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
handle.runtime.block_on(async {
service.shutdown().await;
});
}));
if shutdown_result.is_err() {
#[cfg(feature = "tracing")]
error!("Panic occurred during service shutdown");
}
}
handle.runtime.shutdown_background();
#[cfg(feature = "tracing")]
info!("Service shut down complete.");
} else {
#[cfg(feature = "tracing")]
info!("Service was not running.");
}
#[cfg(feature = "tracing")]
crate::logging::shutdown_logging();
0
});
match result {
Ok(ret) => ret,
Err(_) => {
#[cfg(feature = "tracing")]
error!("Panic occurred in ombrac_server_service_shutdown");
#[cfg(feature = "tracing")]
let _ = std::panic::catch_unwind(|| crate::logging::shutdown_logging());
0
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn ombrac_server_get_version() -> *const c_char {
const VERSION_WITH_NULL: &str = concat!(env!("CARGO_PKG_VERSION"), "\0");
VERSION_WITH_NULL.as_ptr() as *const c_char
}