rsmgp_sys/value/
mod.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//! All related to the value (container for any data type).
15
16use std::convert::From;
17use std::ffi::{CStr, CString};
18
19use crate::edge::*;
20use crate::list::*;
21use crate::map::*;
22use crate::memgraph::*;
23use crate::mgp::*;
24use crate::path::*;
25use crate::result::*;
26use crate::vertex::*;
27// Required here, if not present tests linking fails.
28#[double]
29use crate::mgp::ffi;
30use mockall_double::double;
31
32/// Creates a copy of the provided string.
33///
34/// # Safety
35///
36/// The caller has provided a pointer that points to a valid C string. More here [CStr::from_ptr].
37pub(crate) unsafe fn create_cstring(c_char_ptr: *const i8) -> MgpResult<CString> {
38    match CString::new(CStr::from_ptr(c_char_ptr).to_bytes()) {
39        Ok(v) => Ok(v),
40        Err(_) => Err(MgpError::UnableToCreateCString),
41    }
42}
43
44// NOTE: on why mutable pointer to mgp_value has to be owned by this code.
45//
46// mgp_value used to return data is non-const, owned by the module code. Function to delete
47// mgp_value is non-const.
48// mgp_value containing data from Memgraph is const.
49//
50// `make` functions return non-const value that has to be deleted.
51// `get_property` functions return non-const copied value that has to be deleted.
52// `mgp_property` holds *const mgp_value.
53//
54// Possible solutions:
55//   * An enum containing *mut and *const can work but the implementation would also contain
56//   duplicated code.
57//   * A generic data type seems complex to implement https://stackoverflow.com/questions/40317860.
58//   * Holding a *const mgp_value + an ownership flag + convert to *mut when delete function has to
59//   be called.
60//   * Hold only *mut mgp_value... as soon as there is *const mgp_value, make a copy (own *mut
61//   mgp_value). The same applies for all other data types, e.g. mgp_edge, mgp_vertex.
62//   mgp_value_make_vertex accepts *mut mgp_vartex (required to return user data), but data from
63//   the graph is all *const T.
64//
65// The decision is to move on with a copy of mgp_value because it's cheap to make the copy.
66
67/// Useful to own `mgp_value` coming from / going into Memgraph as a result.
68///
69/// Underlying pointer object is going to be automatically deleted.
70///
71/// NOTE: Implementing From<Value> for MgpValue is not simple because not all Value objects can
72/// contain Memgraph object (primitive types).
73pub struct MgpValue {
74    // It's not wise to create a new MgpValue out of the existing value pointer because drop with a
75    // valid pointer will be called multiple times -> double free problem.
76    ptr: *mut mgp_value,
77    memgraph: Memgraph,
78}
79
80impl Drop for MgpValue {
81    fn drop(&mut self) {
82        unsafe {
83            if !self.ptr.is_null() {
84                ffi::mgp_value_destroy(self.ptr);
85            }
86        }
87    }
88}
89
90impl MgpValue {
91    pub(crate) fn new(ptr: *mut mgp_value, memgraph: &Memgraph) -> MgpValue {
92        #[cfg(not(test))]
93        assert!(
94            !ptr.is_null(),
95            "Unable to create Memgraph value because the given pointer is null."
96        );
97
98        MgpValue {
99            ptr,
100            memgraph: memgraph.clone(),
101        }
102    }
103
104    pub(crate) fn mgp_ptr(&self) -> *const mgp_value {
105        self.ptr
106    }
107
108    pub fn to_value(&self) -> MgpResult<Value> {
109        unsafe { mgp_raw_value_to_value(self.mgp_ptr(), &self.memgraph) }
110    }
111
112    pub fn make_null(memgraph: &Memgraph) -> MgpResult<MgpValue> {
113        unsafe {
114            let mgp_ptr = ffi::mgp_value_make_null(memgraph.memory_ptr());
115            if mgp_ptr.is_null() {
116                return Err(MgpError::UnableToMakeNullValue);
117            }
118            Ok(MgpValue::new(mgp_ptr, &memgraph))
119        }
120    }
121
122    pub fn is_null(&self) -> bool {
123        unsafe { ffi::mgp_value_is_null(self.ptr) != 0 }
124    }
125
126    pub fn make_bool(value: bool, memgraph: &Memgraph) -> MgpResult<MgpValue> {
127        unsafe {
128            let mgp_ptr =
129                ffi::mgp_value_make_bool(if !value { 0 } else { 1 }, memgraph.memory_ptr());
130            if mgp_ptr.is_null() {
131                return Err(MgpError::UnableToMakeBoolValue);
132            }
133            Ok(MgpValue::new(mgp_ptr, &memgraph))
134        }
135    }
136
137    pub fn is_bool(&self) -> bool {
138        unsafe { ffi::mgp_value_is_bool(self.ptr) != 0 }
139    }
140
141    pub fn make_int(value: i64, memgraph: &Memgraph) -> MgpResult<MgpValue> {
142        unsafe {
143            let mgp_ptr = ffi::mgp_value_make_int(value, memgraph.memory_ptr());
144            if mgp_ptr.is_null() {
145                return Err(MgpError::UnableToMakeIntegerValue);
146            }
147            Ok(MgpValue::new(mgp_ptr, &memgraph))
148        }
149    }
150
151    pub fn is_int(&self) -> bool {
152        unsafe { ffi::mgp_value_is_int(self.ptr) != 0 }
153    }
154
155    pub fn make_double(value: f64, memgraph: &Memgraph) -> MgpResult<MgpValue> {
156        unsafe {
157            let mgp_ptr = ffi::mgp_value_make_double(value, memgraph.memory_ptr());
158            if mgp_ptr.is_null() {
159                return Err(MgpError::UnableToMakeDoubleValue);
160            }
161            Ok(MgpValue::new(mgp_ptr, &memgraph))
162        }
163    }
164
165    pub fn is_double(&self) -> bool {
166        unsafe { ffi::mgp_value_is_double(self.ptr) != 0 }
167    }
168
169    pub fn make_string(value: &CStr, memgraph: &Memgraph) -> MgpResult<MgpValue> {
170        unsafe {
171            let mgp_ptr = ffi::mgp_value_make_string(value.as_ptr(), memgraph.memory_ptr());
172            if mgp_ptr.is_null() {
173                return Err(MgpError::UnableToMakeMemgraphStringValue);
174            }
175            Ok(MgpValue::new(mgp_ptr, &memgraph))
176        }
177    }
178
179    pub fn is_string(&self) -> bool {
180        unsafe { ffi::mgp_value_is_string(self.ptr) != 0 }
181    }
182
183    /// Makes a copy of the given object returning the [MgpValue] object. [MgpValue] objects owns
184    /// the new object.
185    pub fn make_list(list: &List, memgraph: &Memgraph) -> MgpResult<MgpValue> {
186        unsafe {
187            // The new object should be manually destroyed in case something within this function
188            // fails.
189            let mgp_list = ffi::mgp_list_make_empty(list.size(), memgraph.memory_ptr());
190            for item in list.iter()? {
191                let mgp_value = item.to_mgp_value(&memgraph)?;
192                if ffi::mgp_list_append(mgp_list, mgp_value.ptr) == 0 {
193                    ffi::mgp_list_destroy(mgp_list);
194                    return Err(MgpError::UnableToMakeListValue);
195                }
196            }
197            let mgp_value = ffi::mgp_value_make_list(mgp_list);
198            if mgp_value.is_null() {
199                ffi::mgp_list_destroy(mgp_list);
200                return Err(MgpError::UnableToMakeListValue);
201            }
202            Ok(MgpValue::new(mgp_value, &memgraph))
203        }
204    }
205
206    pub fn is_list(&self) -> bool {
207        unsafe { ffi::mgp_value_is_list(self.ptr) != 0 }
208    }
209
210    /// Makes a copy of the given object returning the [MgpValue] object. [MgpValue] objects owns
211    /// the new object.
212    pub fn make_map(map: &Map, memgraph: &Memgraph) -> MgpResult<MgpValue> {
213        unsafe {
214            // The new object should be manually destroyed in case something within this function
215            // fails.
216            let mgp_map = ffi::mgp_map_make_empty(memgraph.memory_ptr());
217            for item in map.iter()? {
218                let mgp_value = match item.value.to_mgp_value(&memgraph) {
219                    Ok(v) => v,
220                    Err(_) => {
221                        ffi::mgp_map_destroy(mgp_map);
222                        return Err(MgpError::UnableToMakeMapValue);
223                    }
224                };
225                if ffi::mgp_map_insert(mgp_map, item.key.as_ptr(), mgp_value.ptr) == 0 {
226                    ffi::mgp_map_destroy(mgp_map);
227                    return Err(MgpError::UnableToMakeMapValue);
228                }
229            }
230            let mgp_value = ffi::mgp_value_make_map(mgp_map);
231            if mgp_value.is_null() {
232                ffi::mgp_map_destroy(mgp_map);
233                return Err(MgpError::UnableToMakeMapValue);
234            }
235            Ok(MgpValue::new(mgp_value, &memgraph))
236        }
237    }
238
239    pub fn is_map(&self) -> bool {
240        unsafe { ffi::mgp_value_is_map(self.ptr) != 0 }
241    }
242
243    /// Makes a copy of the given object returning the [MgpValue] object. [MgpValue] objects owns
244    /// the new object.
245    pub fn make_vertex(vertex: &Vertex, memgraph: &Memgraph) -> MgpResult<MgpValue> {
246        unsafe {
247            // The new object should be manually destroyed in case something within this function
248            // fails.
249            let mgp_copy = ffi::mgp_vertex_copy(vertex.mgp_ptr(), memgraph.memory_ptr());
250            if mgp_copy.is_null() {
251                return Err(MgpError::UnableToMakeVertexValue);
252            }
253            let mgp_value = ffi::mgp_value_make_vertex(mgp_copy);
254            if mgp_value.is_null() {
255                ffi::mgp_vertex_destroy(mgp_copy);
256                return Err(MgpError::UnableToMakeVertexValue);
257            }
258            Ok(MgpValue::new(mgp_value, &memgraph))
259        }
260    }
261
262    pub fn is_vertex(&self) -> bool {
263        unsafe { ffi::mgp_value_is_vertex(self.ptr) != 0 }
264    }
265
266    /// Makes a copy of the given object returning the [MgpValue] object. [MgpValue] objects owns
267    /// the new object.
268    pub fn make_edge(edge: &Edge, memgraph: &Memgraph) -> MgpResult<MgpValue> {
269        unsafe {
270            // The new object should be manually destroyed in case something within this function
271            // fails.
272            let mgp_copy = ffi::mgp_edge_copy(edge.mgp_ptr(), memgraph.memory_ptr());
273            if mgp_copy.is_null() {
274                return Err(MgpError::UnableToMakeEdgeValue);
275            }
276            let mgp_value = ffi::mgp_value_make_edge(mgp_copy);
277            if mgp_value.is_null() {
278                ffi::mgp_edge_destroy(mgp_copy);
279                return Err(MgpError::UnableToMakeEdgeValue);
280            }
281            Ok(MgpValue::new(mgp_value, &memgraph))
282        }
283    }
284
285    pub fn is_edge(&self) -> bool {
286        unsafe { ffi::mgp_value_is_edge(self.ptr) != 0 }
287    }
288
289    /// Makes a copy of the given object returning the [MgpValue] object. [MgpValue] objects owns
290    /// the new object.
291    pub fn make_path(path: &Path, memgraph: &Memgraph) -> MgpResult<MgpValue> {
292        unsafe {
293            // The new object should be manually destroyed in case something within this function
294            // fails.
295            let mgp_copy = ffi::mgp_path_copy(path.mgp_ptr(), memgraph.memory_ptr());
296            if mgp_copy.is_null() {
297                return Err(MgpError::UnableToMakePathValue);
298            }
299            let mgp_value = ffi::mgp_value_make_path(mgp_copy);
300            if mgp_value.is_null() {
301                ffi::mgp_path_destroy(mgp_copy);
302                return Err(MgpError::UnableToMakePathValue);
303            }
304            Ok(MgpValue::new(mgp_value, &memgraph))
305        }
306    }
307
308    pub fn is_path(&self) -> bool {
309        unsafe { ffi::mgp_value_is_path(self.ptr) != 0 }
310    }
311}
312
313/// Object containing/owning concrete underlying mgp objects (e.g., mgp_vertex).
314///
315/// User code should mostly deal with these objects.
316pub enum Value {
317    Null,
318    Bool(bool),
319    Int(i64),
320    Float(f64),
321    String(CString),
322    Vertex(Vertex),
323    Edge(Edge),
324    Path(Path),
325    List(List),
326    Map(Map),
327}
328
329impl Value {
330    pub fn to_mgp_value(&self, memgraph: &Memgraph) -> MgpResult<MgpValue> {
331        match self {
332            Value::Null => MgpValue::make_null(&memgraph),
333            Value::Bool(x) => MgpValue::make_bool(*x, &memgraph),
334            Value::Int(x) => MgpValue::make_int(*x, &memgraph),
335            Value::Float(x) => MgpValue::make_double(*x, &memgraph),
336            Value::String(x) => MgpValue::make_string(&*x.as_c_str(), &memgraph),
337            Value::List(x) => MgpValue::make_list(&x, &memgraph),
338            Value::Map(x) => MgpValue::make_map(&x, &memgraph),
339            Value::Vertex(x) => MgpValue::make_vertex(&x, &memgraph),
340            Value::Edge(x) => MgpValue::make_edge(&x, &memgraph),
341            Value::Path(x) => MgpValue::make_path(&x, &memgraph),
342        }
343    }
344}
345
346impl From<MgpValue> for Value {
347    fn from(item: MgpValue) -> Self {
348        match item.to_value() {
349            Ok(v) => v,
350            Err(_) => panic!("Unable to create Value from MgpValue."),
351        }
352    }
353}
354
355/// Creates copy of [mgp_value] object as a [Value] object.
356///
357/// NOTE: If would be more optimal not to copy [mgp_list], [mgp_map] and [mgp_path], but that's not
358/// possible at this point because of the way how C API iterators are implemented. E.g., after each
359/// `mgp_properties_iterator_next()` call, the previous `mgp_value` pointer shouldn't be used.
360/// There is no known way of defining the right lifetime of the returned `MgpValue` object. A
361/// solution would be to change the C API to preserve underlying values of the returned pointers
362/// during the lifetime of the Rust iterator.
363///
364/// # Safety
365///
366/// Calls C API unsafe functions. The provided [mgp_value] object has to be a valid non-null
367/// pointer.
368pub(crate) unsafe fn mgp_raw_value_to_value(
369    value: *const mgp_value,
370    memgraph: &Memgraph,
371) -> MgpResult<Value> {
372    #[allow(non_upper_case_globals)]
373    match ffi::mgp_value_get_type(value) {
374        mgp_value_type_MGP_VALUE_TYPE_NULL => Ok(Value::Null),
375        mgp_value_type_MGP_VALUE_TYPE_BOOL => Ok(Value::Bool(ffi::mgp_value_get_bool(value) == 0)),
376        mgp_value_type_MGP_VALUE_TYPE_INT => Ok(Value::Int(ffi::mgp_value_get_int(value))),
377        mgp_value_type_MGP_VALUE_TYPE_STRING => {
378            let mgp_string = ffi::mgp_value_get_string(value);
379            match create_cstring(mgp_string) {
380                Ok(value) => Ok(Value::String(value)),
381                Err(_) => Err(MgpError::UnableToMakeValueString),
382            }
383        }
384        mgp_value_type_MGP_VALUE_TYPE_DOUBLE => Ok(Value::Float(ffi::mgp_value_get_double(value))),
385        mgp_value_type_MGP_VALUE_TYPE_VERTEX => {
386            let mgp_vertex = ffi::mgp_value_get_vertex(value);
387            Ok(Value::Vertex(Vertex::mgp_copy(mgp_vertex, &memgraph)?))
388        }
389        mgp_value_type_MGP_VALUE_TYPE_EDGE => {
390            let mgp_edge = ffi::mgp_value_get_edge(value);
391            Ok(Value::Edge(Edge::mgp_copy(mgp_edge, &memgraph)?))
392        }
393        mgp_value_type_MGP_VALUE_TYPE_PATH => {
394            let mgp_path = ffi::mgp_value_get_path(value);
395            Ok(Value::Path(Path::mgp_copy(mgp_path, &memgraph)?))
396        }
397        mgp_value_type_MGP_VALUE_TYPE_LIST => {
398            let mgp_list = ffi::mgp_value_get_list(value);
399            Ok(Value::List(List::mgp_copy(mgp_list, &memgraph)?))
400        }
401        mgp_value_type_MGP_VALUE_TYPE_MAP => {
402            let mgp_map = ffi::mgp_value_get_map(value);
403            Ok(Value::Map(Map::mgp_copy(mgp_map, &memgraph)?))
404        }
405        _ => {
406            panic!("Unable to create value object because of uncovered mgp_value type.");
407        }
408    }
409}
410
411#[cfg(test)]
412mod tests;