use std::ffi::{CStr, CString};
use crate::memgraph::*;
use crate::mgp::*;
use crate::result::*;
use crate::value::*;
#[double]
use crate::mgp::ffi;
use mockall_double::double;
pub struct Map {
ptr: *mut mgp_map,
memgraph: Memgraph,
}
impl Drop for Map {
fn drop(&mut self) {
unsafe {
if !self.ptr.is_null() {
ffi::mgp_map_destroy(self.ptr);
}
}
}
}
pub struct MapItem {
pub key: CString,
pub value: Value,
}
pub struct MapIterator {
ptr: *mut mgp_map_items_iterator,
is_first: bool,
memgraph: Memgraph,
}
impl MapIterator {
pub(crate) fn new(ptr: *mut mgp_map_items_iterator, memgraph: &Memgraph) -> MapIterator {
MapIterator {
ptr,
is_first: true,
memgraph: memgraph.clone(),
}
}
}
impl Drop for MapIterator {
fn drop(&mut self) {
unsafe {
if !self.ptr.is_null() {
ffi::mgp_map_items_iterator_destroy(self.ptr);
}
}
}
}
impl Iterator for MapIterator {
type Item = MapItem;
fn next(&mut self) -> Option<MapItem> {
unsafe {
let data = if self.is_first {
self.is_first = false;
ffi::mgp_map_items_iterator_get(self.ptr)
} else {
ffi::mgp_map_items_iterator_next(self.ptr)
};
if data.is_null() {
None
} else {
let mgp_map_item_key = ffi::mgp_map_item_key(data);
let mgp_map_item_value = ffi::mgp_map_item_value(data);
let key = match create_cstring(mgp_map_item_key) {
Ok(v) => v,
Err(_) => panic!("Unable to create map item key."),
};
let value = match mgp_raw_value_to_value(mgp_map_item_value, &self.memgraph) {
Ok(v) => v,
Err(_) => panic!("Unable to create map item value."),
};
Some(MapItem { key, value })
}
}
}
}
impl Map {
pub(crate) fn new(ptr: *mut mgp_map, memgraph: &Memgraph) -> Map {
#[cfg(not(test))]
assert!(
!ptr.is_null(),
"Unable to create map because the given pointer is null."
);
Map {
ptr,
memgraph: memgraph.clone(),
}
}
pub(crate) unsafe fn mgp_copy(ptr: *const mgp_map, memgraph: &Memgraph) -> MgpResult<Map> {
#[cfg(not(test))]
assert!(
!ptr.is_null(),
"Unable to create map copy because the given pointer is null."
);
let mgp_map_copy = ffi::mgp_map_make_empty(memgraph.memory_ptr());
let mgp_map_iterator = ffi::mgp_map_iter_items(ptr, memgraph.memory_ptr());
if mgp_map_iterator.is_null() {
ffi::mgp_map_destroy(mgp_map_copy);
return Err(MgpError::UnableToCopyMap);
}
let map_iterator = MapIterator::new(mgp_map_iterator, &memgraph);
for item in map_iterator {
let mgp_value = item.value.to_mgp_value(&memgraph)?;
if ffi::mgp_map_insert(mgp_map_copy, item.key.as_ptr(), mgp_value.mgp_ptr()) == 0 {
ffi::mgp_map_destroy(mgp_map_copy);
return Err(MgpError::UnableToCopyMap);
}
}
Ok(Map::new(mgp_map_copy, &memgraph))
}
pub fn make_empty(memgraph: &Memgraph) -> MgpResult<Map> {
unsafe {
let mgp_ptr = ffi::mgp_map_make_empty(memgraph.memory_ptr());
if mgp_ptr.is_null() {
return Err(MgpError::UnableToCreateEmptyMap);
}
Ok(Map::new(mgp_ptr, &memgraph))
}
}
pub fn insert(&self, key: &CStr, value: &Value) -> MgpResult<()> {
unsafe {
let mgp_value = value.to_mgp_value(&self.memgraph)?;
if ffi::mgp_map_insert(self.ptr, key.as_ptr(), mgp_value.mgp_ptr()) == 0 {
return Err(MgpError::UnableToInsertMapValue);
}
Ok(())
}
}
pub fn size(&self) -> u64 {
unsafe { ffi::mgp_map_size(self.ptr) }
}
pub fn at(&self, key: &CStr) -> MgpResult<Value> {
unsafe {
let c_value = ffi::mgp_map_at(self.ptr, key.as_ptr());
if c_value.is_null() {
return Err(MgpError::UnableToAccessMapValue);
}
mgp_raw_value_to_value(c_value, &self.memgraph)
}
}
pub fn iter(&self) -> MgpResult<MapIterator> {
unsafe {
let mgp_iterator = ffi::mgp_map_iter_items(self.ptr, self.memgraph.memory_ptr());
if mgp_iterator.is_null() {
return Err(MgpError::UnableToCreateMapIterator);
}
Ok(MapIterator::new(mgp_iterator, &self.memgraph))
}
}
}
#[cfg(test)]
mod tests;