Skip to content

Commit d58ec6f

Browse files
Add support for temporal types (#33)
Co-authored-by: János Benjamin Antal <[email protected]>
1 parent 3bb92f0 commit d58ec6f

File tree

5 files changed

+383
-3
lines changed

5 files changed

+383
-3
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ exclude = [
1717

1818
[dependencies]
1919
maplit = "1.0.2"
20+
chrono = "0.4.19"
2021

2122
[dev-dependencies]
2223
libc = "0.2"

src/connection/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ fn fetchone_summary() {
291291
"planning_time",
292292
"plan_execution_time",
293293
] {
294-
assert!(summary.contains_key(&key as &str));
294+
assert!(summary.contains_key(key as &str));
295295
}
296296
}
297297

src/value/mod.rs

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
// limitations under the License.
1414

1515
use super::bindings;
16+
use chrono::{Datelike, Duration, NaiveDate, NaiveDateTime, NaiveTime, Timelike};
1617
use std::collections::HashMap;
18+
use std::convert::TryFrom;
1719
use std::ffi::{CStr, CString};
1820
use std::fmt;
1921
use std::fmt::Formatter;
20-
22+
use std::num::TryFromIntError;
2123
use std::slice;
2224

2325
/// Representation of parameter value used in query.
@@ -27,6 +29,10 @@ pub enum QueryParam {
2729
Int(i64),
2830
Float(f64),
2931
String(String),
32+
Date(NaiveDate),
33+
LocalTime(NaiveTime),
34+
LocalDateTime(NaiveDateTime),
35+
Duration(Duration),
3036
List(Vec<QueryParam>),
3137
Map(HashMap<String, QueryParam>),
3238
}
@@ -43,6 +49,16 @@ impl QueryParam {
4349
QueryParam::Int(x) => bindings::mg_value_make_integer(*x),
4450
QueryParam::Float(x) => bindings::mg_value_make_float(*x),
4551
QueryParam::String(x) => bindings::mg_value_make_string(str_to_c_str(x.as_str())),
52+
QueryParam::Date(x) => bindings::mg_value_make_date(naive_date_to_mg_date(x)),
53+
QueryParam::LocalTime(x) => {
54+
bindings::mg_value_make_local_time(naive_local_time_to_mg_local_time(x))
55+
}
56+
QueryParam::LocalDateTime(x) => bindings::mg_value_make_local_date_time(
57+
naive_local_date_time_to_mg_local_date_time(x),
58+
),
59+
QueryParam::Duration(x) => {
60+
bindings::mg_value_make_duration(duration_to_mg_duration(x))
61+
}
4662
QueryParam::List(x) => bindings::mg_value_make_list(vector_to_mg_list(x)),
4763
QueryParam::Map(x) => bindings::mg_value_make_map(hash_map_to_mg_map(x)),
4864
}
@@ -114,6 +130,10 @@ pub enum Value {
114130
Float(f64),
115131
String(String),
116132
List(Vec<Value>),
133+
Date(NaiveDate),
134+
LocalTime(NaiveTime),
135+
LocalDateTime(NaiveDateTime),
136+
Duration(Duration),
117137
Map(HashMap<String, Value>),
118138
Node(Node),
119139
Relationship(Relationship),
@@ -167,6 +187,59 @@ pub(crate) fn mg_value_string(mg_value: *const bindings::mg_value) -> String {
167187
mg_string_to_string(c_str)
168188
}
169189

190+
fn days_as_seconds(days: i64) -> i64 {
191+
hours_as_seconds(days * 24)
192+
}
193+
194+
fn hours_as_seconds(hours: i64) -> i64 {
195+
minutes_as_seconds(hours * 60)
196+
}
197+
198+
fn minutes_as_seconds(minutes: i64) -> i64 {
199+
minutes * 60
200+
}
201+
202+
const NSEC_IN_SEC: i64 = 1_000_000_000;
203+
204+
pub(crate) fn mg_value_naive_date(mg_value: *const bindings::mg_value) -> Result<NaiveDate, ()> {
205+
let c_date = unsafe { bindings::mg_value_date(mg_value) };
206+
let c_delta_days = unsafe { bindings::mg_date_days(c_date) };
207+
let epoch_date = NaiveDate::from_ymd(1970, 1, 1);
208+
let delta_days = Duration::days(c_delta_days);
209+
Ok(epoch_date.checked_add_signed(delta_days).unwrap())
210+
}
211+
212+
pub(crate) fn mg_value_naive_local_time(
213+
mg_value: *const bindings::mg_value,
214+
) -> Result<NaiveTime, TryFromIntError> {
215+
let c_local_time = unsafe { bindings::mg_value_local_time(mg_value) };
216+
let c_nanoseconds = unsafe { bindings::mg_local_time_nanoseconds(c_local_time) };
217+
let seconds = u32::try_from(c_nanoseconds / NSEC_IN_SEC)?;
218+
let nanoseconds = u32::try_from(c_nanoseconds % NSEC_IN_SEC)?;
219+
Ok(NaiveTime::from_num_seconds_from_midnight(
220+
seconds,
221+
nanoseconds,
222+
))
223+
}
224+
225+
pub(crate) fn mg_value_naive_local_date_time(
226+
mg_value: *const bindings::mg_value,
227+
) -> Result<NaiveDateTime, TryFromIntError> {
228+
let c_local_date_time = unsafe { bindings::mg_value_local_date_time(mg_value) };
229+
let c_seconds = unsafe { bindings::mg_local_date_time_seconds(c_local_date_time) };
230+
let c_nanoseconds = unsafe { bindings::mg_local_date_time_nanoseconds(c_local_date_time) };
231+
let nanoseconds = u32::try_from(c_nanoseconds)?;
232+
Ok(NaiveDateTime::from_timestamp(c_seconds, nanoseconds))
233+
}
234+
235+
pub(crate) fn mg_value_duration(mg_value: *const bindings::mg_value) -> Duration {
236+
let c_duration = unsafe { bindings::mg_value_duration(mg_value) };
237+
let days = unsafe { bindings::mg_duration_days(c_duration) };
238+
let seconds = unsafe { bindings::mg_duration_seconds(c_duration) };
239+
let nanoseconds = unsafe { bindings::mg_duration_nanoseconds(c_duration) };
240+
Duration::days(days) + Duration::seconds(seconds) + Duration::nanoseconds(nanoseconds)
241+
}
242+
170243
pub(crate) fn mg_map_to_hash_map(mg_map: *const bindings::mg_map) -> HashMap<String, Value> {
171244
unsafe {
172245
let size = bindings::mg_map_size(mg_map);
@@ -318,6 +391,47 @@ pub(crate) fn str_to_c_str(string: &str) -> *const std::os::raw::c_char {
318391
unsafe { (*c_str).as_ptr() }
319392
}
320393

394+
pub(crate) fn naive_date_to_mg_date(input: &NaiveDate) -> *mut bindings::mg_date {
395+
let unix_epoch = NaiveDate::from_ymd(1970, 1, 1).num_days_from_ce();
396+
unsafe { bindings::mg_date_make((input.num_days_from_ce() - unix_epoch) as i64) }
397+
}
398+
399+
pub(crate) fn naive_local_time_to_mg_local_time(input: &NaiveTime) -> *mut bindings::mg_local_time {
400+
let hours_ns = hours_as_seconds(input.hour() as i64) * NSEC_IN_SEC;
401+
let minutes_ns = minutes_as_seconds(input.minute() as i64) * NSEC_IN_SEC;
402+
let seconds_ns = (input.second() as i64) * NSEC_IN_SEC;
403+
let nanoseconds = input.nanosecond() as i64;
404+
unsafe { bindings::mg_local_time_make(hours_ns + minutes_ns + seconds_ns + nanoseconds) }
405+
}
406+
407+
pub(crate) fn naive_local_date_time_to_mg_local_date_time(
408+
input: &NaiveDateTime,
409+
) -> *mut bindings::mg_local_date_time {
410+
let unix_epoch = NaiveDate::from_ymd(1970, 1, 1).num_days_from_ce();
411+
let days_s = days_as_seconds((input.num_days_from_ce() - unix_epoch) as i64);
412+
let hours_s = hours_as_seconds(input.hour() as i64);
413+
let minutes_s = minutes_as_seconds(input.minute() as i64);
414+
let seconds_s = input.second() as i64;
415+
let nanoseconds = input.nanosecond() as i64;
416+
unsafe {
417+
bindings::mg_local_date_time_make(days_s + hours_s + minutes_s + seconds_s, nanoseconds)
418+
}
419+
}
420+
421+
pub(crate) fn duration_to_mg_duration(input: &Duration) -> *mut bindings::mg_duration {
422+
// Duration returns total number of nanoseconds, in order to create a valid mg_duration object,
423+
// days and seconds have to be reducted from the total duration. In addition, one can get numer
424+
// of nanoseconds and then substract days and seconds, but since nanoseconds can overflow quite
425+
// quicky (with 292 years), it's better to use Duration and first reduce days and seconds.
426+
let mut duration = *input;
427+
let days = input.num_days();
428+
duration = duration - Duration::days(days);
429+
let seconds = input.num_seconds();
430+
duration = duration - Duration::seconds(seconds);
431+
let nanoseconds = duration.num_nanoseconds().unwrap();
432+
unsafe { bindings::mg_duration_make(0, days, seconds, nanoseconds) }
433+
}
434+
321435
pub(crate) fn vector_to_mg_list(vector: &[QueryParam]) -> *mut bindings::mg_list {
322436
let size = vector.len() as u32;
323437
let mg_list = unsafe { bindings::mg_list_make_empty(size) };
@@ -339,6 +453,18 @@ impl Value {
339453
bindings::mg_value_type_MG_VALUE_TYPE_STRING => {
340454
Value::String(mg_value_string(c_mg_value))
341455
}
456+
bindings::mg_value_type_MG_VALUE_TYPE_DATE => {
457+
Value::Date(mg_value_naive_date(c_mg_value).unwrap())
458+
}
459+
bindings::mg_value_type_MG_VALUE_TYPE_LOCAL_TIME => {
460+
Value::LocalTime(mg_value_naive_local_time(c_mg_value).unwrap())
461+
}
462+
bindings::mg_value_type_MG_VALUE_TYPE_LOCAL_DATE_TIME => {
463+
Value::LocalDateTime(mg_value_naive_local_date_time(c_mg_value).unwrap())
464+
}
465+
bindings::mg_value_type_MG_VALUE_TYPE_DURATION => {
466+
Value::Duration(mg_value_duration(c_mg_value))
467+
}
342468
bindings::mg_value_type_MG_VALUE_TYPE_LIST => {
343469
Value::List(mg_value_list_to_vec(c_mg_value))
344470
}
@@ -365,6 +491,10 @@ impl fmt::Display for Value {
365491
Value::Int(x) => write!(f, "{}", x),
366492
Value::Float(x) => write!(f, "{}", x),
367493
Value::String(x) => write!(f, "'{}'", x),
494+
Value::Date(x) => write!(f, "'{}'", x),
495+
Value::LocalTime(x) => write!(f, "'{}'", x),
496+
Value::LocalDateTime(x) => write!(f, "'{}'", x),
497+
Value::Duration(x) => write!(f, "'{}'", x),
368498
Value::List(x) => write!(
369499
f,
370500
"{}",

0 commit comments

Comments
 (0)