use crate::embedded::c_api::error::linkrs_error_code_t;
use crate::embedded::c_api::session::LinkrsSessionHandle;
use crate::embedded::c_api::types::{linkrs_session_t, linkrs_value_t, linkrs_value_type_t};
use linkrs_query::executor::expression::functions::{
AggregateFinalCallback, AggregateStepCallback, CFunctionContext, CustomFunction,
ScalarFunctionCallback,
};
use log::error;
use std::ffi::{c_char, c_int, c_void, CStr};
#[allow(non_camel_case_types)]
pub type linkrs_scalar_function_callback =
Option<extern "C" fn(context: *mut linkrs_context_t, argc: c_int, argv: *mut linkrs_value_t)>;
#[allow(non_camel_case_types)]
pub type linkrs_aggregate_step_callback =
Option<extern "C" fn(context: *mut linkrs_context_t, argc: c_int, argv: *mut linkrs_value_t)>;
#[allow(non_camel_case_types)]
pub type linkrs_aggregate_final_callback = Option<extern "C" fn(context: *mut linkrs_context_t)>;
#[allow(non_camel_case_types)]
pub type linkrs_function_destroy_callback = Option<extern "C" fn(user_data: *mut c_void)>;
#[repr(C)]
pub struct linkrs_context_t {
_dummy: u8,
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_create_function(
session: *mut linkrs_session_t,
name: *const c_char,
argc: c_int,
user_data: *mut c_void,
x_func: linkrs_scalar_function_callback,
_x_destroy: linkrs_function_destroy_callback,
) -> c_int {
if session.is_null() || name.is_null() || x_func.is_none() {
return linkrs_error_code_t::LINKRS_MISUSE as c_int;
}
let name_str = unsafe {
match CStr::from_ptr(name).to_str() {
Ok(s) => s,
Err(_) => return linkrs_error_code_t::LINKRS_MISUSE as c_int,
}
};
unsafe {
let handle = &*(session as *mut LinkrsSessionHandle);
let callback: ScalarFunctionCallback = std::mem::transmute(x_func);
let func = CustomFunction::new_c(
name_str,
argc as usize,
argc < 0,
format!("C function: {}", name_str),
callback,
user_data,
);
if let Err(e) = handle.inner.register_custom_function(func) {
error!("Registration function failed: {:?}", e);
return linkrs_error_code_t::LINKRS_ERROR as c_int;
}
}
linkrs_error_code_t::LINKRS_OK as c_int
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_create_aggregate(
session: *mut linkrs_session_t,
name: *const c_char,
argc: c_int,
user_data: *mut c_void,
x_step: linkrs_aggregate_step_callback,
x_final: linkrs_aggregate_final_callback,
_x_destroy: linkrs_function_destroy_callback,
) -> c_int {
if session.is_null() || name.is_null() || x_step.is_none() || x_final.is_none() {
return linkrs_error_code_t::LINKRS_MISUSE as c_int;
}
let name_str = unsafe {
match CStr::from_ptr(name).to_str() {
Ok(s) => s,
Err(_) => return linkrs_error_code_t::LINKRS_MISUSE as c_int,
}
};
unsafe {
let handle = &*(session as *mut LinkrsSessionHandle);
let step_callback: AggregateStepCallback = std::mem::transmute(x_step);
let final_callback: AggregateFinalCallback = std::mem::transmute(x_final);
let func = CustomFunction::new_c_aggregate(
name_str,
argc as usize,
argc < 0,
format!("C aggregate function: {}", name_str),
step_callback,
final_callback,
user_data,
);
if let Err(e) = handle.inner.register_custom_function(func) {
error!("Registration of the aggregate function failed: {:?}", e);
return linkrs_error_code_t::LINKRS_ERROR as c_int;
}
}
linkrs_error_code_t::LINKRS_OK as c_int
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_delete_function(
session: *mut linkrs_session_t,
name: *const c_char,
) -> c_int {
if session.is_null() || name.is_null() {
return linkrs_error_code_t::LINKRS_MISUSE as c_int;
}
linkrs_error_code_t::LINKRS_OK as c_int
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_context_set_result(
context: *mut linkrs_context_t,
value: *const linkrs_value_t,
) -> c_int {
if context.is_null() {
return linkrs_error_code_t::LINKRS_MISUSE as c_int;
}
unsafe {
let ctx = &mut *(context as *mut CFunctionContext);
if value.is_null() {
ctx.set_result(linkrs_core::Value::Null(linkrs_core::NullType::Null));
} else {
let val = crate::embedded::c_api::value::linkrs_value_to_core(value);
ctx.set_result(val);
}
}
linkrs_error_code_t::LINKRS_OK as c_int
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_context_result_type(
context: *mut linkrs_context_t,
) -> linkrs_value_type_t {
if context.is_null() {
return linkrs_value_type_t::LINKRS_NULL;
}
unsafe {
let ctx = &*(context as *const CFunctionContext);
match &ctx.result {
Some(val) => linkrs_core::value_conversion::core_value_to_linkrs_type(val),
None => linkrs_value_type_t::LINKRS_NULL,
}
}
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_context_set_error(
context: *mut linkrs_context_t,
error_msg: *const c_char,
) -> c_int {
if context.is_null() || error_msg.is_null() {
return linkrs_error_code_t::LINKRS_MISUSE as c_int;
}
unsafe {
let ctx = &mut *(context as *mut CFunctionContext);
let msg = CStr::from_ptr(error_msg).to_string_lossy().into_owned();
ctx.set_error(msg);
}
linkrs_error_code_t::LINKRS_OK as c_int
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_context_get_arg(
context: *mut linkrs_context_t,
index: c_int,
) -> *const linkrs_value_t {
if context.is_null() {
return std::ptr::null();
}
let ctx = &*(context as *const CFunctionContext);
if index < 0 || index as usize >= ctx.argc {
return std::ptr::null();
}
ctx.argv.as_ptr().add(index as usize)
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_context_arg_count(context: *mut linkrs_context_t) -> c_int {
if context.is_null() {
return 0;
}
(*(context as *const CFunctionContext)).argc as c_int
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_load_extension(
session: *mut linkrs_session_t,
path: *const c_char,
) -> c_int {
if session.is_null() || path.is_null() {
return linkrs_error_code_t::LINKRS_MISUSE as c_int;
}
let path_str = unsafe {
match CStr::from_ptr(path).to_str() {
Ok(s) => s,
Err(_) => return linkrs_error_code_t::LINKRS_MISUSE as c_int,
}
};
unsafe {
let handle = &*(session as *mut LinkrsSessionHandle);
if let Err(e) = handle.inner.load_extension(std::path::Path::new(path_str)) {
error!("Load extension failed: {:?}", e);
return linkrs_error_code_t::LINKRS_ERROR as c_int;
}
}
linkrs_error_code_t::LINKRS_OK as c_int
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_unload_extension(
session: *mut linkrs_session_t,
name: *const c_char,
) -> c_int {
if session.is_null() || name.is_null() {
return linkrs_error_code_t::LINKRS_MISUSE as c_int;
}
let name_str = unsafe {
match CStr::from_ptr(name).to_str() {
Ok(s) => s,
Err(_) => return linkrs_error_code_t::LINKRS_MISUSE as c_int,
}
};
unsafe {
let handle = &*(session as *mut LinkrsSessionHandle);
if let Err(e) = handle.inner.unload_extension(name_str) {
error!("Unload extension failed: {:?}", e);
return linkrs_error_code_t::LINKRS_ERROR as c_int;
}
}
linkrs_error_code_t::LINKRS_OK as c_int
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_reload_extension(
session: *mut linkrs_session_t,
name: *const c_char,
reloaded: *mut c_int,
) -> c_int {
if session.is_null() || name.is_null() {
return linkrs_error_code_t::LINKRS_MISUSE as c_int;
}
let name_str = unsafe {
match CStr::from_ptr(name).to_str() {
Ok(s) => s,
Err(_) => return linkrs_error_code_t::LINKRS_MISUSE as c_int,
}
};
unsafe {
let handle = &*(session as *mut LinkrsSessionHandle);
match handle.inner.reload_extension(name_str) {
Ok(did_reload) => {
if !reloaded.is_null() {
*reloaded = i32::from(did_reload);
}
}
Err(e) => {
error!("Reload extension failed: {:?}", e);
return linkrs_error_code_t::LINKRS_ERROR as c_int;
}
}
}
linkrs_error_code_t::LINKRS_OK as c_int
}