use super::{ErrorCode, cstr_to_str};
use crate::Builder;
use crate::context::ZContext;
use std::ffi::c_char;
#[repr(C)]
pub struct CContext {
inner: Box<ZContext>,
}
#[repr(C)]
pub struct CContextConfig {
pub domain_id: u32,
pub config_file: *const c_char,
pub connect_endpoints: *const *const c_char,
pub connect_endpoints_count: usize,
pub mode: *const c_char,
pub disable_multicast_scouting: bool,
pub connect_to_local_zenohd: bool,
pub json_config: *const c_char,
pub remap_rules: *const *const c_char,
pub remap_rules_count: usize,
pub enable_logging: bool,
pub namespace: *const c_char,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn hiroz_context_create(domain_id: u32) -> *mut CContext {
match crate::context::ZContextBuilder::default()
.with_domain_id(domain_id as usize)
.build()
{
Ok(ctx) => Box::into_raw(Box::new(CContext {
inner: Box::new(ctx),
})),
Err(e) => {
tracing::warn!("hiroz: Failed to create context: {}", e);
std::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn hiroz_context_create_with_config(
config: *const CContextConfig,
) -> *mut CContext {
if config.is_null() {
tracing::warn!("hiroz: Null config pointer");
return std::ptr::null_mut();
}
unsafe {
let cfg = &*config;
let mut builder =
crate::context::ZContextBuilder::default().with_domain_id(cfg.domain_id as usize);
if let Some(Ok(namespace)) = (!cfg.namespace.is_null()).then(|| cstr_to_str(cfg.namespace))
{
builder = builder.with_namespace(namespace);
}
if let Some(Ok(path)) = (!cfg.config_file.is_null()).then(|| cstr_to_str(cfg.config_file)) {
builder = builder.with_config_file(path);
}
if !cfg.connect_endpoints.is_null() && cfg.connect_endpoints_count > 0 {
let endpoints: Vec<String> = (0..cfg.connect_endpoints_count)
.filter_map(|i| {
let ptr = *cfg.connect_endpoints.add(i);
cstr_to_str(ptr).ok().map(|s| s.to_string())
})
.collect();
if !endpoints.is_empty() {
builder = builder.with_connect_endpoints(endpoints);
}
}
if let Some(Ok(mode)) = (!cfg.mode.is_null()).then(|| cstr_to_str(cfg.mode)) {
builder = builder.with_mode(mode);
}
if cfg.disable_multicast_scouting {
builder = builder.disable_multicast_scouting();
}
if cfg.connect_to_local_zenohd {
builder = builder.connect_to_local_zenohd();
}
if let Some(Ok(json_str)) =
(!cfg.json_config.is_null()).then(|| cstr_to_str(cfg.json_config))
{
if let Ok(obj) =
serde_json::from_str::<serde_json::Map<String, serde_json::Value>>(json_str)
{
for (key, value) in obj {
builder = builder.with_json(key, value);
}
} else {
tracing::warn!("hiroz: Failed to parse json_config as JSON object");
}
}
if cfg.enable_logging {
builder = builder.with_logging_enabled();
}
if !cfg.remap_rules.is_null() && cfg.remap_rules_count > 0 {
let rules: Vec<String> = (0..cfg.remap_rules_count)
.filter_map(|i| {
let ptr = *cfg.remap_rules.add(i);
cstr_to_str(ptr).ok().map(|s| s.to_string())
})
.collect();
if !rules.is_empty() {
match builder.with_remap_rules(rules) {
Ok(b) => builder = b,
Err(e) => {
tracing::warn!("hiroz: Invalid remap rules: {}", e);
return std::ptr::null_mut();
}
}
}
}
match builder.build() {
Ok(ctx) => Box::into_raw(Box::new(CContext {
inner: Box::new(ctx),
})),
Err(e) => {
tracing::warn!("hiroz: Failed to create context with config: {}", e);
std::ptr::null_mut()
}
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn hiroz_context_destroy(ctx: *mut CContext) -> i32 {
if ctx.is_null() {
return ErrorCode::NullPointer as i32;
}
unsafe {
let _ = Box::from_raw(ctx);
}
ErrorCode::Success as i32
}
pub(crate) unsafe fn get_context_ref<'a>(ctx: *mut CContext) -> Option<&'a ZContext> {
if ctx.is_null() {
None
} else {
unsafe { Some(&(*ctx).inner) }
}
}