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}