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}