pub mod exports;
pub mod wrapper;
use std::collections::HashMap;
use postcard::{from_bytes, to_stdvec};
use reifydb_abi::{constants::FFI_OK, context::context::ContextFFI, data::buffer::BufferFFI};
use reifydb_core::value::column::columns::Columns;
use reifydb_type::{
params::Params,
value::{Value, frame::frame::Frame},
};
use crate::error::{FFIError, Result};
pub trait FFIProcedureMetadata {
const NAME: &'static str;
const API: u32;
const VERSION: &'static str;
const DESCRIPTION: &'static str;
}
pub trait FFIProcedure: 'static {
fn new(config: &HashMap<String, Value>) -> Result<Self>
where
Self: Sized;
fn call(&mut self, ctx: &FFIProcedureContext, params: Params) -> Result<Columns>;
}
pub trait FFIProcedureWithMetadata: FFIProcedure + FFIProcedureMetadata {}
impl<T> FFIProcedureWithMetadata for T where T: FFIProcedure + FFIProcedureMetadata {}
pub struct FFIProcedureContext {
pub(crate) ctx: *mut ContextFFI,
}
impl FFIProcedureContext {
pub fn new(ctx: *mut ContextFFI) -> Self {
assert!(!ctx.is_null(), "ContextFFI pointer must not be null");
Self {
ctx,
}
}
pub fn rql(&self, rql: &str, params: Params) -> Result<Vec<Frame>> {
raw_procedure_rql(self, rql, params)
}
}
pub(crate) fn raw_procedure_rql(ctx: &FFIProcedureContext, rql: &str, params: Params) -> Result<Vec<Frame>> {
let params_bytes = to_stdvec(¶ms)
.map_err(|e| FFIError::Serialization(format!("failed to serialize params: {}", e)))?;
let mut output = BufferFFI::empty();
unsafe {
let result = ((*ctx.ctx).callbacks.rql.rql)(
ctx.ctx,
rql.as_ptr(),
rql.len(),
params_bytes.as_ptr(),
params_bytes.len(),
&mut output,
);
if result == FFI_OK {
let result_bytes = output.as_slice();
let frames: Vec<Frame> = from_bytes(result_bytes)
.map_err(|e| FFIError::Serialization(format!("failed to deserialize result: {}", e)))?;
Ok(frames)
} else {
Err(FFIError::Other(format!("host_rql failed with code {}", result)))
}
}
}