tarantool/lib.rs
1#![allow(unknown_lints)]
2#![allow(clippy::let_and_return)]
3#![allow(clippy::approx_constant)]
4#![allow(clippy::needless_return)]
5#![allow(clippy::redundant_closure_call)]
6#![allow(clippy::len_without_is_empty)]
7#![allow(clippy::unnecessary_cast)]
8#![allow(clippy::needless_late_init)]
9#![allow(clippy::bool_assert_comparison)]
10#![allow(clippy::field_reassign_with_default)]
11#![allow(clippy::manual_unwrap_or)]
12#![allow(rustdoc::redundant_explicit_links)]
13//! Tarantool C API bindings for Rust.
14//! This library contains the following Tarantool API's:
15//!
16//! - Box: [spaces](space), [indexes](index), [sequences](sequence)
17//! - [Fibers: fiber attributes, conditional variables, latches, async runtime](fiber)
18//! - [CoIO](coio)
19//! - [Transactions](transaction)
20//! - [Schema management](schema)
21//! - [Protocol implementation](net_box) (`net.box`): CRUD, stored procedure call, triggers
22//! - [Alternative async protocol implementation](network) (`network::client`): Async, coio based CRUD
23//! - [Tuple utils](mod@tuple)
24//! - [Decimal numbers](mod@decimal)
25//! - [Logging](log) (see <https://docs.rs/log/>)
26//! - [Error handling](error)
27//! - [Stored procedures](macro@crate::proc)
28//!
29//! > **Caution!** The library is currently under development.
30//! > API may be unstable until version 1.0 is released.
31//!
32//! ### Features
33//!
34//! - `net_box` - Enables protocol implementation (enabled by default)
35//! - `schema` - Enables schema manipulation utils (WIP as for now)
36//!
37//! ### Prerequisites
38//!
39//! - rustc 1.67.1 or newer
40//! - tarantool 2.2
41//!
42//! ### Stored procedures
43//!
44//! There are several ways Tarantool can call Rust code. It can use either a plugin, a Lua FFI module,
45//! or a [stored procedure]. In this file we only cover the third option, namely Rust stored procedures.
46//! Even though Tarantool always treats Rust routines just as "C functions", we keep on using the "stored procedure"
47//! term as an agreed convention and also for historical reasons.
48//!
49//! This tutorial contains the following simple steps:
50//! 1. `examples/easy` - prints "hello world";
51//! 1. `examples/harder` - decodes a passed parameter value;
52//! 1. `examples/hardest` - uses this library to do a DBMS insert;
53//! 1. `examples/read` - uses this library to do a DBMS select;
54//! 1. `examples/write` - uses this library to do a DBMS replace.
55//!
56//! Our examples are a good starting point for users who want to confidently start writing their own stored procedures.
57//!
58//! [stored procedure]: macro@crate::proc
59pub mod access_control;
60pub mod auth;
61#[cfg(feature = "picodata")]
62pub mod cbus;
63pub mod clock;
64pub mod coio;
65pub mod datetime;
66pub mod decimal;
67pub mod define_str_enum;
68pub mod error;
69pub mod ffi;
70pub mod fiber;
71pub mod index;
72pub mod log;
73pub mod msgpack;
74pub mod net_box;
75pub mod network;
76pub mod proc;
77#[cfg(feature = "picodata")]
78pub mod read_view;
79pub mod schema;
80pub mod sequence;
81pub mod session;
82pub mod space;
83pub mod sql;
84#[cfg(feature = "test")]
85pub mod test;
86pub mod time;
87pub mod transaction;
88pub mod trigger;
89pub mod tuple;
90pub mod util;
91pub mod uuid;
92pub mod vclock;
93
94/// `#[tarantool::proc]` is a macro attribute for creating stored procedure
95/// functions.
96///
97/// ```no_run
98/// #[tarantool::proc]
99/// fn add(x: i32, y: i32) -> i32 {
100/// x + y
101/// }
102/// ```
103///
104/// Create a "C" stored procedure from Tarantool and call it with arguments wrapped
105/// within a Lua table:
106/// ```lua
107/// box.schema.func.create("libname.add", { language = 'C' })
108/// assert(box.func['libname.add']:call({ 1, 2 }) == 3)
109/// ```
110///
111/// # Collecting stored procedures
112///
113/// All stored procs defined with `#[tarantool::proc]` attribute are
114/// automatically added to a global array and can be accessed via
115/// `proc::all_procs` function.
116/// ```no_run
117/// # #[cfg(feature = "stored_procs_slice")] {
118/// use tarantool::proc::all_procs;
119///
120/// #[tarantool::proc]
121/// fn my_proc() -> i32 { 69 }
122///
123/// let procs = all_procs();
124/// assert_eq!(procs[0].name(), "my_proc");
125/// # }
126/// ```
127///
128/// This can be used to generate stored procedure defintions for tarantool's
129/// `box.schema.func.create`. Although there's currently no easy way to fully
130/// automate this process, because of how loading dynamic modules works in
131/// tarantool. To be able to access the list of procs from a module you need to
132/// call a function defined in that module.
133///
134/// See how you can bootstrap proc definitions in example in `examples/all_procs`.
135///
136/// **NOTE:** collecting stored procedures is implemented by inserting the
137/// pointers to the functions into a static array. This can sometimes cause
138/// problems when functions marked `#[tarantool::proc]` are defined in the same
139/// mod as unit tests marked `#[::test]`, specifically on MacOS. This is most
140/// likely due to a bug in the compiler, but until it is fixed we've added a
141/// workaround, such that procs are added to the global array under the
142/// `#[cfg(not(test))]` attribute. This can only affect you, if you try
143/// collecting stored procedures from the rust builtin unit tests, which there's
144/// no reason for you to do. If you want to test collecting of stored procedures,
145/// consider using `#[tarantool::test]`.
146///
147/// # Public attribute
148///
149/// Proc metadata returned from `all_procs` also contains the flag indicating if
150/// the procedure is defined with `pub` visibility modifier. This info may be
151/// useful to specify to which stored procedures the "public" tarantool role
152/// should have access to. Use [`Proc::is_public`] method to get this data.
153///
154/// Also, if for some reason you don't want to use the visibility modifier or
155/// you want to override it (e.g. your proc is pub, but you want the flag to be
156/// false) you can specify the `public` attribute.
157/// ```no_run
158/// # #[cfg(feature = "stored_procs_slice")] {
159/// use tarantool::proc::all_procs;
160///
161/// #[tarantool::proc]
162/// pub fn public_proc() {}
163///
164/// #[tarantool::proc]
165/// fn private_proc() {}
166///
167/// #[tarantool::proc(public = true)]
168/// fn also_public_proc() {}
169///
170/// #[tarantool::proc(public = false)]
171/// pub fn also_private_proc() {}
172///
173/// for proc in all_procs() {
174/// if proc.name() == "public_proc" { assert!( proc.is_public()) }
175/// if proc.name() == "private_proc" { assert!(!proc.is_public()) }
176/// if proc.name() == "also_public_proc" { assert!( proc.is_public()) }
177/// if proc.name() == "also_private_proc" { assert!(!proc.is_public()) }
178/// }
179/// # }
180/// ```
181///
182/// # Accepting borrowed arguments
183///
184/// It can sometimes be more efficient to borrow the procedure's arguments
185/// rather than copying them. This usecase is supported, however it is not
186/// entirely safe. Due to how stored procedures are implemented in tarantool,
187/// the arguments are allocated in a volatile region of memory, which can be
188/// overwritten by some tarantool operations. Therefore you cannot rely on the
189/// borrowed arguments being valid for the lifetime of the procedure call.
190///
191/// This proc is safe, because the data is accessed before any other calls to
192/// tarantool api:
193/// ```no_run
194/// #[tarantool::proc]
195/// fn strlen(s: &str) -> usize {
196/// s.len()
197/// }
198/// ```
199///
200/// This one however is unsafe:
201/// ```no_run
202/// use tarantool::{error::Error, index::IteratorType::Eq, space::Space};
203/// use std::collections::HashSet;
204///
205/// #[tarantool::proc]
206/// fn count_common_friends(user1: &str, user2: String) -> Result<usize, Error> {
207/// // A call to tarantool api.
208/// let space = Space::find("friends_with").unwrap();
209///
210/// // This call is unsafe, because borrowed data `user1` is accessed
211/// // after a call to tarantool api.
212/// let iter = space.select(Eq, &[user1])?;
213/// let user1_friends: HashSet<String> = iter
214/// .map(|tuple| tuple.get(1).unwrap())
215/// .collect();
216///
217/// // This call is safe, because `user2` is owned.
218/// let iter = space.select(Eq, &[user2])?;
219/// let user2_friends: HashSet<String> = iter
220/// .map(|tuple| tuple.get(1).unwrap())
221/// .collect();
222///
223/// Ok(user1_friends.intersection(&user2_friends).count())
224/// }
225/// ```
226///
227/// # Returning errors
228///
229/// Assuming the function's return type is [`Result`]`<T, E>` (where `E` implements
230/// [`Display`]), the return values read as follows:
231/// - `Ok(v)`: the stored procedure will return `v`
232/// - `Err(e)`: the stored procedure will fail and `e` will be set as the last
233/// Tarantool error (see also [`TarantoolError::last`])
234/// ```no_run
235/// use tarantool::{error::Error, index::IteratorType::Eq, space::Space};
236///
237/// #[tarantool::proc]
238/// fn get_name(id: usize) -> Result<Option<String>, Error> {
239/// Ok(
240/// if let Some(space) = Space::find("users") {
241/// if let Some(row) = space.select(Eq, &[id])?.next() {
242/// row.get("name")
243/// } else {
244/// None
245/// }
246/// } else {
247/// None
248/// }
249/// )
250/// }
251/// ```
252///
253/// # Returning custom types
254///
255/// The return type of the stored procedure must implement the [`Return`] trait which is
256/// implemented for most built-in types. To return an arbitrary type that
257/// implements [`serde::Serialize`] you can use the [`ReturnMsgpack`] wrapper
258/// type or the `custom_ret` attribute parameter.
259/// ```no_run
260/// #[derive(serde::Serialize)]
261/// struct Complex {
262/// re: f64,
263/// im: f64,
264/// }
265///
266/// #[tarantool::proc(custom_ret)]
267/// fn sqrt(x: f64) -> Complex {
268/// if x < 0. {
269/// Complex { re: 0., im: x.abs().sqrt() }
270/// } else {
271/// Complex { re: x.sqrt(), im: 0. }
272/// }
273/// }
274///
275/// // above is equivalent to this
276/// use tarantool::proc::ReturnMsgpack;
277/// #[tarantool::proc]
278/// fn sqrt_explicit(x: f64) -> ReturnMsgpack<Complex> {
279/// ReturnMsgpack(
280/// if x < 0. {
281/// Complex { re: 0., im: x.abs().sqrt() }
282/// } else {
283/// Complex { re: x.sqrt(), im: 0. }
284/// }
285/// )
286/// }
287/// ```
288///
289/// # Packed arguments
290///
291/// By default the stored procedure unpacks the received tuple and assigns the
292/// **i**th field of the tuple to the **i**th argument. If there are fewer
293/// arguments than there are fields in the input tuple, the unused tuple fields are ignored.
294///
295/// If you want to instead deserialize the tuple directly into your structure
296/// you can use the `packed_args`
297/// attribute parameter
298/// ```no_run
299/// #[tarantool::proc(packed_args)]
300/// fn sum_all(vals: Vec<i32>) -> i32 {
301/// vals.into_iter().sum()
302/// }
303///
304/// #[tarantool::proc]
305/// fn sum_first_3(a: i32, b: i32, c: i32) -> i32 {
306/// a + b + c
307/// }
308/// ```
309///
310/// In the above example `sum_all` will sum all the inputs values it received
311/// whereas `sum_first_3` will only sum up the first 3 values
312///
313/// # Injecting arguments
314///
315/// Because the return value of the stored procedure is immediately serialized
316/// it is in theory ok to return borrowed values. Rust however will not allow
317/// you to return references to the values owned by the function. In that case
318/// you can use an *injected* argument, which will be created just outside
319/// the stored procedure and will be passed to it as a corresponding argument.
320///
321/// ```no_run
322/// fn global_data() -> &'static [String] {
323/// todo!()
324/// }
325///
326/// #[tarantool::proc]
327/// fn get_ith<'a>(
328/// #[inject(global_data())]
329/// data: &'a [String],
330/// i: usize,
331/// ) -> &'a str {
332/// &data[i]
333/// }
334/// ```
335///
336/// When calling the stored procedure only the actual arguments need to be
337/// specified, so in the above example `get_ith` will effectively have just 1
338/// argument `i`. And `data` will be automatically injected and it's value will
339/// be set to `global_data()` each time it is called.
340///
341/// # Debugging
342///
343/// There's also a `debug` attribute parameter which enables debug printing of
344/// the arguments received by the stored procedure
345/// ```no_run
346/// #[tarantool::proc(debug)]
347/// fn print_what_you_got() {}
348/// ```
349///
350/// The above stored procedure will just print any of it's arguments to
351/// stderr and return immediately.
352///
353/// [`Result`]: std::result::Result
354/// [`Display`]: std::fmt::Display
355/// [`TarantoolError::last`]: crate::error::TarantoolError::last
356/// [`Return`]: crate::proc::Return
357/// [`ReturnMsgpack`]: crate::proc::ReturnMsgpack
358/// [`Proc::is_public`]: crate::proc::Proc::is_public
359pub use tarantool_proc::stored_proc as proc;
360pub use tlua;
361
362/// A re-export of [linkme] crate used inside #[`[tarantool::test]`]
363/// and #[`[tarantool::proc]`] macro attributes.
364pub use linkme;
365
366/// Mark a function as a test. This will add the function to the list of tests
367/// in a special global section. The tests can be accessed using
368/// [`test::test_cases`] or [`test::collect_tester`].
369///
370/// # Example
371/// ```no_run
372/// #[tarantool::test]
373/// fn my_test() {
374/// assert!(true);
375/// }
376///
377/// #[tarantool::test(should_panic)]
378/// fn my_panicking_test() {
379/// assert!(false);
380/// }
381/// ```
382///
383/// You can also have tests which should panic only in certain conditions.
384/// This is done by specifying a constant expression as an argument to the
385/// `should_panic` keyword. Here's an example:
386/// ```no_run
387/// #[tarantool::test(should_panic = cfg!(debug_assertions))]
388/// fn only_panics_in_debug_mode() {
389/// debug_assert!(false);
390/// }
391/// ```
392///
393/// You can also use `#[tarantool::test]` with `async` functions, in which case
394/// the body of the test will be wrapped inside `fiber::block_on(async {})`
395/// block. The following two tests are equivalent:
396/// ```no_run
397/// #[tarantool::test]
398/// async fn async_test_1() {
399/// assert_eq!(foo().await, 1);
400/// }
401///
402/// #[tarantool::test]
403/// fn async_test_2() {
404/// tarantool::fiber::block_on(async {
405/// assert_eq!(foo().await, 1);
406/// })
407/// }
408///
409/// async fn foo() -> i32 {
410/// 1
411/// }
412/// ```
413///
414#[cfg(feature = "test")]
415pub use tarantool_proc::test;
416
417/// Return a global tarantool lua state.
418///
419/// **WARNING:** using global lua state is error prone, especially when writing
420/// code that will be executed in multiple fibers. Consider using [`lua_state`]
421/// instead. Use with caution if necessary.
422fn global_lua() -> tlua::StaticLua {
423 unsafe { tlua::Lua::from_static(ffi::tarantool::luaT_state()) }
424}
425
426/// Create a new lua state with an isolated stack. The new state has access to
427/// all the global and tarantool data (Lua variables, tables, modules, etc.).
428pub fn lua_state() -> tlua::LuaThread {
429 global_lua().new_thread()
430}
431
432pub use error::Result;
433pub type StdResult<T, E> = std::result::Result<T, E>;