1use super::{
2 super::{
3 ffi,
4 function::{Context, InternalContext},
5 types::*,
6 value::*,
7 },
8 ConstraintOp, VTab,
9};
10use std::{
11 borrow::Cow,
12 cell::{Cell, RefCell},
13 os::raw::c_int,
14 pin::Pin,
15 slice,
16};
17
18type CFunc = unsafe extern "C" fn(*mut ffi::sqlite3_context, c_int, *mut *mut ffi::sqlite3_value);
19
20pub struct VTabFunctionList<'vtab, T: VTab<'vtab>> {
25 list: RefCell<Vec<Pin<Box<VTabFunction<'vtab, T>>>>>,
26}
27
28impl<'vtab, T: VTab<'vtab>> Default for VTabFunctionList<'vtab, T> {
29 fn default() -> Self {
30 Self {
31 list: RefCell::new(Vec::new()),
32 }
33 }
34}
35
36impl<'vtab, T: VTab<'vtab> + 'vtab> VTabFunctionList<'vtab, T> {
37 fn _add(
38 &self,
39 n_args: i32,
40 name: impl Into<Cow<'vtab, str>>,
41 constraint: Option<ConstraintOp>,
42 func: Box<dyn Fn(&'vtab T, &InternalContext, &mut [&mut ValueRef]) + 'vtab>,
43 ) {
44 assert!((-1..128).contains(&n_args), "n_args invalid");
45 if let Some(c) = &constraint {
46 c.assert_valid_function_constraint();
47 }
48 self.list
49 .borrow_mut()
50 .push(VTabFunction::new(n_args, name, constraint, func));
51 }
52
53 pub fn add<F>(
65 &self,
66 n_args: i32,
67 name: impl Into<Cow<'vtab, str>>,
68 constraint: Option<ConstraintOp>,
69 func: F,
70 ) where
71 F: Fn(&Context, &mut [&mut ValueRef]) -> Result<()> + 'vtab,
72 {
73 let func = wrap_fn(func);
74 self._add(n_args, name, constraint, func);
75 }
76
77 pub fn add_method<F>(
82 &self,
83 n_args: i32,
84 name: impl Into<Cow<'vtab, str>>,
85 constraint: Option<ConstraintOp>,
86 func: F,
87 ) where
88 F: Fn(&'vtab T, &Context, &mut [&mut ValueRef]) -> Result<()> + 'vtab,
89 {
90 let func = wrap_method(func);
91 self._add(n_args, name, constraint, func);
92 }
93
94 pub(crate) fn find(
100 &self,
101 vtab: &'vtab T,
102 n_args: i32,
103 name: &str,
104 ) -> Option<((CFunc, *mut ::std::os::raw::c_void), Option<ConstraintOp>)> {
105 let list = self.list.borrow();
106 let found = [n_args, -1]
107 .into_iter()
108 .find_map(|n_args| list.iter().find(|f| f.n_args == n_args && f.name == name));
109 found.map(|r| (r.bind(vtab), r.constraint))
110 }
111}
112
113fn wrap_fn<'vtab, T, F>(
114 func: F,
115) -> Box<dyn Fn(&'vtab T, &InternalContext, &mut [&mut ValueRef]) + 'vtab>
116where
117 T: VTab<'vtab>,
118 F: Fn(&Context, &mut [&mut ValueRef]) -> Result<()> + 'vtab,
119{
120 Box::new(
121 move |_: &T, ic: &InternalContext, a: &mut [&mut ValueRef]| {
122 let ctx = unsafe { Context::from_ptr(ic.as_ptr()) };
123 if let Err(e) = func(ctx, a) {
124 ctx.set_result(e).unwrap();
125 }
126 },
127 )
128}
129
130fn wrap_method<'vtab, T, F>(
131 func: F,
132) -> Box<dyn Fn(&'vtab T, &InternalContext, &mut [&mut ValueRef]) + 'vtab>
133where
134 T: VTab<'vtab>,
135 F: Fn(&'vtab T, &Context, &mut [&mut ValueRef]) -> Result<()> + 'vtab,
136{
137 Box::new(
138 move |t: &T, ic: &InternalContext, a: &mut [&mut ValueRef]| {
139 let ctx = unsafe { Context::from_ptr(ic.as_ptr()) };
140 if let Err(e) = func(t, ctx, a) {
141 ctx.set_result(e).unwrap();
142 }
143 },
144 )
145}
146
147struct VTabFunction<'vtab, T: VTab<'vtab>> {
148 n_args: i32,
149 name: Cow<'vtab, str>,
150 constraint: Option<ConstraintOp>,
151 vtab: Cell<Option<&'vtab T>>,
152 func: Box<dyn Fn(&'vtab T, &InternalContext, &mut [&mut ValueRef]) + 'vtab>,
153}
154
155impl<'vtab, T: VTab<'vtab>> VTabFunction<'vtab, T> {
156 pub fn new(
157 n_args: i32,
158 name: impl Into<Cow<'vtab, str>>,
159 constraint: Option<ConstraintOp>,
160 func: Box<dyn Fn(&'vtab T, &InternalContext, &mut [&mut ValueRef]) + 'vtab>,
161 ) -> Pin<Box<Self>> {
162 Box::pin(Self {
163 n_args,
164 name: name.into(),
165 constraint,
166 vtab: Cell::new(None),
167 func,
168 })
169 }
170
171 pub fn bind(&self, vtab: &'vtab T) -> (CFunc, *mut ::std::os::raw::c_void) {
172 self.vtab.set(Some(vtab));
173 (call_vtab_method::<T>, self as *const Self as *mut Self as _)
174 }
175
176 pub fn invoke(&self, ic: &InternalContext, a: &mut [&mut ValueRef]) {
177 (*self.func)(self.vtab.get().unwrap(), ic, a);
178 }
179}
180
181unsafe extern "C" fn call_vtab_method<'vtab, T>(
182 context: *mut ffi::sqlite3_context,
183 argc: i32,
184 argv: *mut *mut ffi::sqlite3_value,
185) where
186 T: VTab<'vtab> + 'vtab,
187{
188 let ic = InternalContext::from_ptr(context);
189 let vtab_function = ic.user_data::<VTabFunction<'vtab, T>>();
190 let args = slice::from_raw_parts_mut(argv as *mut &mut ValueRef, argc as _);
191 vtab_function.invoke(ic, args);
192}