rsmgp_sys/
rsmgp.rs

1// Copyright (c) 2016-2021 Memgraph Ltd. [https://memgraph.com]
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//! Macro definitions and top level data structures.
15//!
16//! [define_procedure], [init_module], [close_module] all accept a function accepting [Memgraph]
17//! and returning [crate::result::MgpResult] because that allows using `?` operator which is a very
18//! convenient way of propagating execution errors.
19//!
20//! Example
21//!
22//! ```no run
23//! |memgraph: &Memgraph| -> MgpResult<()> {
24//!     // Implementation
25//! }
26//! ```
27
28use std::ffi::CStr;
29
30use crate::memgraph::*;
31#[double]
32use crate::mgp::ffi;
33use crate::value::*;
34use mockall_double::double;
35
36/// Defines a new procedure callable by Memgraph engine.
37///
38/// Wraps call to the Rust function provided as a second argument. In addition to the function
39/// call, it also sets the catch_unwind hook and properly handles execution errors. The first macro
40/// argument is the exact name of the C procedure Memgraph will be able to run (the exact same name
41/// has to be registered inside [init_module] phase. The second argument has to be a function
42/// accepting reference [Memgraph]. The [Memgraph] object could be used to interact with Memgraph
43/// instance.
44///
45/// Example
46///
47/// ```no run
48/// define_procedure!(procedure_name, |memgraph: &Memgraph| -> MgpResult<()> {
49///     // Implementation
50/// }
51/// ```
52#[macro_export]
53macro_rules! define_procedure {
54    ($c_name:ident, $rs_func:expr) => {
55        #[no_mangle]
56        extern "C" fn $c_name(
57            args: *const mgp_list,
58            graph: *const mgp_graph,
59            result: *mut mgp_result,
60            memory: *mut mgp_memory,
61        ) {
62            let prev_hook = panic::take_hook();
63            panic::set_hook(Box::new(|_| { /* Do nothing. */ }));
64
65            let procedure_result = panic::catch_unwind(|| {
66                let memgraph = Memgraph::new(args, graph, result, memory, std::ptr::null_mut());
67                match $rs_func(&memgraph) {
68                    Ok(_) => (),
69                    Err(e) => {
70                        let msg = e.to_string();
71                        println!("{}", msg);
72                        let c_msg =
73                            CString::new(msg).expect("Unable to create Memgraph error message!");
74                        set_memgraph_error_msg(&c_msg, &memgraph);
75                    }
76                }
77            });
78
79            panic::set_hook(prev_hook);
80            match procedure_result {
81                Ok(_) => {}
82                Err(e) => {
83                    println!("Procedure panic!");
84                    let memgraph = Memgraph::new(args, graph, result, memory, std::ptr::null_mut());
85                    match e.downcast::<&str>() {
86                        Ok(msg) => {
87                            println!("{}", msg);
88                            let c_msg = CString::new(msg.as_bytes())
89                                .expect("Unable to create Memgraph PANIC error message!");
90                            set_memgraph_error_msg(&c_msg, &memgraph);
91                        }
92                        Err(_) => {
93                            println!("Unknown type of panic!.");
94                        }
95                    }
96                }
97            }
98        }
99    };
100}
101
102/// Initializes Memgraph query module.
103///
104/// Example
105///
106/// ```no run
107/// init_module!(|memgraph: &Memgraph| -> MgpResult<()> {
108///     memgraph.add_read_procedure(
109///         procedure_name,
110///         c_str!("procedure_name"),
111///         &[define_type!("list", Type::List, Type::Int),],
112///     )?;
113///     Ok(())
114/// });
115/// ```
116#[macro_export]
117macro_rules! init_module {
118    ($init_func:expr) => {
119        #[no_mangle]
120        pub extern "C" fn mgp_init_module(
121            module: *mut mgp_module,
122            memory: *mut mgp_memory,
123        ) -> c_int {
124            let prev_hook = panic::take_hook();
125            panic::set_hook(Box::new(|_| { /* Do nothing. */ }));
126            let result = panic::catch_unwind(|| {
127                let memgraph = Memgraph::new(
128                    std::ptr::null_mut(),
129                    std::ptr::null_mut(),
130                    std::ptr::null_mut(),
131                    memory,
132                    module,
133                );
134                $init_func(&memgraph)
135            });
136            panic::set_hook(prev_hook);
137            match result {
138                Ok(_) => 0,
139                Err(_) => 1,
140            }
141        }
142    };
143}
144
145/// Closes Memgraph query module.
146///
147/// Example
148///
149/// ```no run
150/// close_module!(|memgraph: &Memgraph| -> MgpResult<()> {
151///     // Implementation
152/// }
153#[macro_export]
154macro_rules! close_module {
155    ($close_func:expr) => {
156        #[no_mangle]
157        pub extern "C" fn mgp_shutdown_module() -> c_int {
158            let prev_hook = panic::take_hook();
159            panic::set_hook(Box::new(|_| { /* Do nothing. */ }));
160            let result = panic::catch_unwind(|| $close_func());
161            panic::set_hook(prev_hook);
162            match result {
163                Ok(_) => 0,
164                Err(_) => 1,
165            }
166        }
167    };
168}
169
170/// Used to pass expected types (arguments, optional arguments, result field types) to the Memgraph
171/// engine.
172pub enum Type {
173    Any,
174    Bool,
175    Number,
176    Int,
177    Double,
178    String,
179    Map,
180    Vertex,
181    Edge,
182    Path,
183    Nullable,
184    List,
185}
186
187/// Used to pass expected type during procedure registration for required parameters and result
188/// values.
189///
190/// The final type is deduced by processing types field from left to right. E.g., [Type::Any]
191/// means any return type, [Type::List], [Type::Int] means a list of integer values.
192///
193/// `deprecated` is used to register deprecated result type.
194pub struct NamedType<'a> {
195    pub name: &'a CStr,
196    pub types: &'a [Type],
197    pub deprecated: bool,
198}
199
200/// Used to pass expected type during procedure registration for optional parameters.
201///
202/// The final type is deduced by processing types field from left to right. E.g., [Type::Any]
203/// means any return type, [Type::List], [Type::Int] means a list of integer values.
204///
205/// `default` stores value in case the caller doesn't pass any value.
206pub struct OptionalNamedType<'a> {
207    pub name: &'a CStr,
208    pub types: &'a [Type],
209    pub default: &'a MgpValue,
210}
211
212/// Defines a regular type.
213///
214/// Example of defining a list of integers
215///
216/// ```no run
217/// define_type!("name", Type::List, Type::Int);
218/// ```
219#[macro_export]
220macro_rules! define_type {
221    ($name:literal, $($types:expr),+) => {
222        NamedType {
223            name: &c_str!($name),
224            types: &[$($types),+],
225            deprecated: false,
226        }
227    };
228}
229
230/// Defines a deprecated type.
231///
232/// Example of defining a list of integers
233///
234/// ```no run
235/// define_deprecated_type!("name", Type::List, Type::Int);
236/// ```
237#[macro_export]
238macro_rules! define_deprecated_type {
239    ($name:literal, $($types:expr),+) => {
240        NamedType {
241            name: &c_str!($name),
242            types: &[$($types),+],
243            depricated: true,
244        }
245    };
246}
247
248/// Defines an optional type.
249///
250/// Example of defining an int.
251///
252/// ```no run
253/// define_optional_type!("name", &MgpValue::make_int(0, &memgraph), Type::List, Type::Int);
254/// ```
255#[macro_export]
256macro_rules! define_optional_type {
257    ($name:literal, $default:expr, $($types:expr),+) => {
258        OptionalNamedType {
259            name: &c_str!($name),
260            types: &[$($types),+],
261            default: $default,
262        }
263    };
264}
265
266/// Sets error that will be returned to the caller.
267pub fn set_memgraph_error_msg(msg: &CStr, memgraph: &Memgraph) {
268    unsafe {
269        let status = ffi::mgp_result_set_error_msg(memgraph.result_ptr(), msg.as_ptr());
270        if status == 0 {
271            panic!("Unable to pass error message to the Memgraph engine.");
272        }
273    }
274}
275
276#[cfg(test)]
277mod tests {
278    use c_str_macro::c_str;
279    use serial_test::serial;
280
281    use super::*;
282    use crate::mgp::mock_ffi::*;
283    use crate::{mock_mgp_once, with_dummy};
284
285    #[test]
286    #[serial]
287    fn test_set_error_msg() {
288        mock_mgp_once!(mgp_result_set_error_msg_context, |_, _| 1);
289
290        with_dummy!(|memgraph: &Memgraph| {
291            set_memgraph_error_msg(c_str!("test_error"), &memgraph);
292        });
293    }
294}