use super::{
super::{ffi, function::Context, types::*, value::*},
ConstraintOp, VTab,
};
use std::{
borrow::Cow,
cell::{Cell, RefCell},
os::raw::c_int,
pin::Pin,
slice,
};
type CFunc = unsafe extern "C" fn(*mut ffi::sqlite3_context, c_int, *mut *mut ffi::sqlite3_value);
type VTabFunc<'vtab, T> = Box<dyn Fn(&'vtab T, &mut Context, &mut [&mut ValueRef]) + 'vtab>;
pub struct VTabFunctionList<'vtab, T: VTab<'vtab>> {
list: RefCell<Vec<Pin<Box<VTabFunction<'vtab, T>>>>>,
}
impl<'vtab, T: VTab<'vtab>> Default for VTabFunctionList<'vtab, T> {
fn default() -> Self {
Self {
list: RefCell::new(Vec::new()),
}
}
}
impl<'vtab, T: VTab<'vtab> + 'vtab> VTabFunctionList<'vtab, T> {
fn _add(
&self,
n_args: i32,
name: impl Into<Cow<'vtab, str>>,
constraint: Option<ConstraintOp>,
func: VTabFunc<'vtab, T>,
) {
assert!((-1..128).contains(&n_args), "n_args invalid");
if let Some(c) = &constraint {
c.assert_valid_function_constraint();
}
self.list
.borrow_mut()
.push(VTabFunction::new(n_args, name, constraint, func));
}
pub fn add<F>(
&self,
n_args: i32,
name: impl Into<Cow<'vtab, str>>,
constraint: Option<ConstraintOp>,
func: F,
) where
F: Fn(&Context, &mut [&mut ValueRef]) -> Result<()> + 'vtab,
{
let func = wrap_fn(func);
self._add(n_args, name, constraint, func);
}
pub fn add_method<F>(
&self,
n_args: i32,
name: impl Into<Cow<'vtab, str>>,
constraint: Option<ConstraintOp>,
func: F,
) where
F: Fn(&'vtab T, &Context, &mut [&mut ValueRef]) -> Result<()> + 'vtab,
{
let func = wrap_method(func);
self._add(n_args, name, constraint, func);
}
pub(crate) fn find(
&self,
vtab: &'vtab T,
n_args: i32,
name: &str,
) -> Option<((CFunc, *mut ::std::os::raw::c_void), Option<ConstraintOp>)> {
let list = self.list.borrow();
let found = [n_args, -1]
.into_iter()
.find_map(|n_args| list.iter().find(|f| f.n_args == n_args && f.name == name));
found.map(|r| (r.bind(vtab), r.constraint))
}
}
fn wrap_fn<'vtab, T, F>(func: F) -> VTabFunc<'vtab, T>
where
T: VTab<'vtab>,
F: Fn(&Context, &mut [&mut ValueRef]) -> Result<()> + 'vtab,
{
Box::new(move |_: &T, ctx: &mut Context, a: &mut [&mut ValueRef]| {
if let Err(e) = func(ctx, a) {
ctx.set_result(e).unwrap();
}
})
}
fn wrap_method<'vtab, T, F>(func: F) -> VTabFunc<'vtab, T>
where
T: VTab<'vtab>,
F: Fn(&'vtab T, &Context, &mut [&mut ValueRef]) -> Result<()> + 'vtab,
{
Box::new(move |t: &T, ctx: &mut Context, a: &mut [&mut ValueRef]| {
if let Err(e) = func(t, ctx, a) {
ctx.set_result(e).unwrap();
}
})
}
struct VTabFunction<'vtab, T: VTab<'vtab>> {
n_args: i32,
name: Cow<'vtab, str>,
constraint: Option<ConstraintOp>,
vtab: Cell<Option<&'vtab T>>,
func: VTabFunc<'vtab, T>,
}
impl<'vtab, T: VTab<'vtab>> VTabFunction<'vtab, T> {
pub fn new(
n_args: i32,
name: impl Into<Cow<'vtab, str>>,
constraint: Option<ConstraintOp>,
func: VTabFunc<'vtab, T>,
) -> Pin<Box<Self>> {
Box::pin(Self {
n_args,
name: name.into(),
constraint,
vtab: Cell::new(None),
func,
})
}
pub fn bind(&self, vtab: &'vtab T) -> (CFunc, *mut ::std::os::raw::c_void) {
self.vtab.set(Some(vtab));
(call_vtab_method::<T>, self as *const Self as *mut Self as _)
}
pub fn invoke(&self, ctx: &mut Context, a: &mut [&mut ValueRef]) {
(*self.func)(self.vtab.get().unwrap(), ctx, a);
}
}
unsafe extern "C" fn call_vtab_method<'vtab, T>(
context: *mut ffi::sqlite3_context,
argc: i32,
argv: *mut *mut ffi::sqlite3_value,
) where
T: VTab<'vtab> + 'vtab,
{
let vtab_function = &mut *(ffi::sqlite3_user_data(context) as *mut VTabFunction<'vtab, T>);
let ctx = Context::from_ptr(context);
let args = slice::from_raw_parts_mut(argv as *mut &mut ValueRef, argc as _);
vtab_function.invoke(ctx, args);
}