tarantool/proc.rs
1use crate::error::IntoBoxError;
2use crate::ffi::tarantool as ffi;
3use crate::tuple::{FunctionCtx, RawByteBuf, RawBytes, Tuple, TupleBuffer};
4use serde::Serialize;
5use std::os::raw::c_int;
6use std::path::Path;
7
8macro_rules! unwrap_or_report_err {
9 ($res:expr) => {
10 match $res {
11 Ok(o) => o,
12 Err(e) => {
13 e.set_last_error();
14 -1
15 }
16 }
17 };
18}
19
20////////////////////////////////////////////////////////////////////////////////
21// Proc
22////////////////////////////////////////////////////////////////////////////////
23
24/// Description of a tarantool stored procedure defined using the
25/// `#[`[`tarantool::proc`]`]` macro attribute.
26///
27/// [`tarantool::proc`]: macro@crate::proc
28#[derive(Debug, Clone)]
29pub struct Proc {
30 name: &'static str,
31 proc: ffi::Proc,
32 public: bool,
33}
34
35impl Proc {
36 /// Create a new stored proc description.
37 ///
38 /// This function is called when `#[`[`tarantool::proc`]`]` attribute is
39 /// used, so users don't usually use it directly.
40 ///
41 /// See also [`module_path`]
42 ///
43 /// [`tarantool::proc`]: macro@crate::proc
44 /// [`module_path`]: module_path()
45 #[inline(always)]
46 pub const fn new(name: &'static str, proc: ffi::Proc) -> Self {
47 Self {
48 name,
49 proc,
50 public: false,
51 }
52 }
53
54 #[inline(always)]
55 pub const fn with_public(mut self, public: bool) -> Self {
56 self.public = public;
57 self
58 }
59
60 /// Get the name of the stored procedure NOT including the module name.
61 #[inline(always)]
62 pub const fn name(&self) -> &'static str {
63 self.name
64 }
65
66 /// Get the proc's function pointer.
67 ///
68 /// This function is usually not necessary for defining tarantool's stored
69 /// procedures, the name is enough. But it is there if you need it for some
70 /// reason.
71 #[inline(always)]
72 pub const fn proc(&self) -> ffi::Proc {
73 self.proc
74 }
75
76 /// Returns `true` if the proc has `pub` visibility specifier, but can be
77 /// overriden with the `public` attribute.
78 ///
79 /// Can be used when choosing which stored procedures the "public" role
80 /// should have access to.
81 ///
82 /// See <https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/_user/>
83 /// for more info about role "public".
84 #[inline(always)]
85 pub const fn is_public(&self) -> bool {
86 self.public
87 }
88}
89
90// In picodata, we cannot guarantee that tarantool module will be linked
91// exactly once due to possible stale versions from various dependencies.
92// This whole feature is here to disable the distributed slice definition
93// in all but one instance of this crate; otherwise we'll trigger linkme's
94// "duplicate distributed slice" check introduced in 0.3.1.
95#[cfg(feature = "stored_procs_slice")]
96pub use stored_procs_slice::*;
97#[cfg(feature = "stored_procs_slice")]
98mod stored_procs_slice {
99 use super::*;
100
101 // Linkme distributed_slice exports a symbol with the given name, so we must
102 // make sure the name is unique, so as not to conflict with distributed slices
103 // from other crates or any other global symbols.
104 /// *INTERNAL API* It is only marked `pub` because it needs to be accessed
105 /// from procedural macros.
106 #[doc(hidden)]
107 #[::linkme::distributed_slice]
108 pub static TARANTOOL_MODULE_STORED_PROCS: [Proc] = [..];
109
110 /// Returns a slice of all stored procedures defined using the
111 /// `#[`[`tarantool::proc`]`]` macro attribute.
112 ///
113 /// The order of procs in the slice is undefined.
114 ///
115 /// [`tarantool::proc`]: macro@crate::proc
116 #[inline(always)]
117 pub fn all_procs() -> &'static [Proc] {
118 &TARANTOOL_MODULE_STORED_PROCS
119 }
120}
121
122#[cfg(not(feature = "stored_procs_slice"))]
123pub fn all_procs() -> &'static [Proc] {
124 panic!("`stored_procs_slice` feature is disabled, calling this function doesn't make sense");
125}
126
127////////////////////////////////////////////////////////////////////////////////
128// module_name
129////////////////////////////////////////////////////////////////////////////////
130
131/// Returns a path to the dynamically linked ojbect in which the symbol pointed
132/// to by `sym` is defined.
133///
134/// This can be used to dynamically figure out the module name for tarantool's
135/// stored procedure definition, for example by passing in a pointer to the
136/// function defined using `#[`[`tarantool::proc`]`]` macro attribute, but is
137/// NOT GUARANTEED TO WORK.
138///
139/// ```no_run
140/// use tarantool::proc::module_path;
141///
142/// #[tarantool::proc]
143/// fn my_proc() -> i32 {
144/// 69
145/// }
146///
147/// let path = module_path(my_proc as _).unwrap();
148/// let filename = path.file_stem().unwrap();
149/// assert_eq!(filename, std::ffi::OsStr::new("libmy_library"));
150/// ```
151///
152/// [`tarantool::proc`]: macro@crate::proc
153pub fn module_path(sym: *const ()) -> Option<&'static Path> {
154 unsafe {
155 let mut info: libc::Dl_info = std::mem::zeroed();
156 if libc::dladdr(sym as _, &mut info) == 0 {
157 return None;
158 }
159
160 if info.dli_fname.is_null() {
161 return None;
162 }
163
164 let path = std::ffi::CStr::from_ptr(info.dli_fname);
165 let path: &std::ffi::OsStr = std::os::unix::ffi::OsStrExt::from_bytes(path.to_bytes());
166 Some(Path::new(path))
167 }
168}
169
170////////////////////////////////////////////////////////////////////////////////
171// ReturnMsgpack
172////////////////////////////////////////////////////////////////////////////////
173
174/// A wrapper type for returning custom types from stored procedures. Consider
175/// using the `custom_ret` attribute parameter instead (see [`tarantool::proc`]
176/// docs for examples).
177///
178/// # using `ReturnMsgpack` directly
179///
180/// You can either return `ReturnMsgpack` directly:
181///
182/// ```no_run
183/// use tarantool::proc::ReturnMsgpack;
184///
185/// #[tarantool::proc]
186/// fn foo(x: i32) -> ReturnMsgpack<MyStruct> {
187/// ReturnMsgpack(MyStruct { x, y: x * 2 })
188/// }
189///
190/// #[derive(serde::Serialize)]
191/// struct MyStruct { x: i32, y: i32 }
192/// ```
193///
194/// # implementing `Return` for custom type
195///
196/// Or you can use it to implement `Return` for your custom type:
197///
198/// ```no_run
199/// use std::os::raw::c_int;
200/// use tarantool::{proc::{Return, ReturnMsgpack}, tuple::FunctionCtx};
201///
202/// #[tarantool::proc]
203/// fn foo(x: i32) -> MyStruct {
204/// MyStruct { x, y: x * 2 }
205/// }
206///
207/// #[derive(serde::Serialize)]
208/// struct MyStruct { x: i32, y: i32 }
209///
210/// impl Return for MyStruct {
211/// fn ret(self, ctx: FunctionCtx) -> c_int {
212/// ReturnMsgpack(self).ret(ctx)
213/// }
214/// }
215/// ```
216///
217/// [`tarantool::proc`]: macro@crate::proc
218pub struct ReturnMsgpack<T>(pub T);
219
220impl<T: Serialize> Return for ReturnMsgpack<T> {
221 #[inline(always)]
222 #[track_caller]
223 fn ret(self, ctx: FunctionCtx) -> c_int {
224 unwrap_or_report_err!(ctx.return_mp(&self.0))
225 }
226}
227
228////////////////////////////////////////////////////////////////////////////////
229// Return
230////////////////////////////////////////////////////////////////////////////////
231
232pub trait Return: Sized {
233 fn ret(self, ctx: FunctionCtx) -> c_int;
234}
235
236impl Return for Tuple {
237 #[inline]
238 #[track_caller]
239 fn ret(self, ctx: FunctionCtx) -> c_int {
240 let res = ctx.return_tuple(&self);
241 unwrap_or_report_err!(res)
242 }
243}
244
245impl<E> Return for Result<Tuple, E>
246where
247 E: IntoBoxError,
248{
249 #[inline(always)]
250 #[track_caller]
251 fn ret(self, ctx: FunctionCtx) -> c_int {
252 unwrap_or_report_err!(self.map(|t| t.ret(ctx)))
253 }
254}
255
256impl Return for TupleBuffer {
257 #[inline]
258 #[track_caller]
259 fn ret(self, ctx: FunctionCtx) -> c_int {
260 let res = ctx.return_bytes(self.as_ref());
261 unwrap_or_report_err!(res)
262 }
263}
264
265impl<E> Return for Result<TupleBuffer, E>
266where
267 E: IntoBoxError,
268{
269 #[inline(always)]
270 #[track_caller]
271 fn ret(self, ctx: FunctionCtx) -> c_int {
272 unwrap_or_report_err!(self.map(|t| t.ret(ctx)))
273 }
274}
275
276impl Return for &RawBytes {
277 #[inline]
278 #[track_caller]
279 fn ret(self, ctx: FunctionCtx) -> c_int {
280 let res = ctx.return_bytes(self);
281 unwrap_or_report_err!(res)
282 }
283}
284
285impl<E> Return for Result<&RawBytes, E>
286where
287 E: IntoBoxError,
288{
289 #[inline(always)]
290 #[track_caller]
291 fn ret(self, ctx: FunctionCtx) -> c_int {
292 unwrap_or_report_err!(self.map(|t| t.ret(ctx)))
293 }
294}
295
296impl Return for RawByteBuf {
297 #[inline]
298 #[track_caller]
299 fn ret(self, ctx: FunctionCtx) -> c_int {
300 let res = ctx.return_bytes(&self);
301 unwrap_or_report_err!(res)
302 }
303}
304
305impl<E> Return for Result<RawByteBuf, E>
306where
307 E: IntoBoxError,
308{
309 #[inline(always)]
310 #[track_caller]
311 fn ret(self, ctx: FunctionCtx) -> c_int {
312 unwrap_or_report_err!(self.map(|t| t.ret(ctx)))
313 }
314}
315
316impl Return for () {
317 #[inline(always)]
318 fn ret(self, _: FunctionCtx) -> c_int {
319 0
320 }
321}
322
323impl<O, E> Return for Result<O, E>
324where
325 O: Serialize,
326 E: IntoBoxError,
327{
328 #[inline(always)]
329 #[track_caller]
330 fn ret(self, ctx: FunctionCtx) -> c_int {
331 match self {
332 Ok(o) => match ctx.return_mp(&o) {
333 Ok(_) => 0,
334 Err(e) => {
335 e.set_last_error();
336 -1
337 }
338 },
339 Err(e) => {
340 e.set_last_error();
341 -1
342 }
343 }
344 }
345}
346
347macro_rules! impl_return {
348 (impl $([ $( $tp:tt )* ])? for $t:ty) => {
349 impl $(< $($tp)* >)? Return for $t
350 where
351 Self: Serialize,
352 {
353 #[inline(always)]
354 #[track_caller]
355 fn ret(self, ctx: FunctionCtx) -> c_int {
356 unwrap_or_report_err!(ctx.return_mp(&self))
357 }
358 }
359 };
360 ($( $t:ty )+) => {
361 $( impl_return!{ impl for $t } )+
362 }
363}
364
365impl_return! { impl[V] for Option<V> }
366impl_return! { impl[V] for Vec<V> }
367impl_return! { impl[V] for &'_ [V] }
368impl_return! { impl[V, const N: usize] for [V; N] }
369impl_return! { impl[K, V] for std::collections::HashMap<K, V> }
370impl_return! { impl[K] for std::collections::HashSet<K> }
371impl_return! { impl[K, V] for std::collections::BTreeMap<K, V> }
372impl_return! { impl[K] for std::collections::BTreeSet<K> }
373impl_return! {
374 bool
375 i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 isize usize
376 f32 f64
377 String &'_ str
378 std::ffi::CString &'_ std::ffi::CStr
379}
380
381macro_rules! impl_return_for_tuple {
382 () => {};
383 ($h:ident $($t:ident)*) => {
384 impl<$h, $($t),*> Return for ($h, $($t,)*)
385 where
386 Self: Serialize,
387 {
388 #[inline(always)]
389 #[track_caller]
390 fn ret(self, ctx: FunctionCtx) -> c_int {
391 unwrap_or_report_err!(ctx.return_mp(&self))
392 }
393 }
394
395 impl_return_for_tuple!{$($t)*}
396 }
397}
398impl_return_for_tuple! {A B C D E F G H I J K L M N O P Q}