use std::{
fmt::{Debug, Display},
ops::Deref,
};
use chrono::Datelike;
use crate::{
ffi,
raw::{RawDate, RawDateTime, RawEdgeUid},
};
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EdgeUid {
pub src: i64,
pub lid: u16,
pub tid: i64,
pub dst: i64,
pub eid: i64,
}
impl EdgeUid {
pub(crate) fn from_raw(raw: &RawEdgeUid) -> Self {
unsafe {
let (src, lid, tid, dst, eid) = (
ffi::lgraph_api_edge_euid_get_src(raw.as_ptr_mut()),
ffi::lgraph_api_edge_euid_get_lid(raw.as_ptr_mut()),
ffi::lgraph_api_edge_euid_get_tid(raw.as_ptr_mut()),
ffi::lgraph_api_edge_euid_get_dst(raw.as_ptr_mut()),
ffi::lgraph_api_edge_euid_get_eid(raw.as_ptr_mut()),
);
EdgeUid {
src,
lid,
tid,
dst,
eid,
}
}
}
pub(crate) fn as_raw(&self) -> RawEdgeUid {
unsafe {
let ptr =
ffi::lgraph_api_create_edge_euid(self.src, self.dst, self.lid, self.tid, self.eid);
RawEdgeUid::from_ptr(ptr)
}
}
}
impl Display for EdgeUid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum AccessLevel {
#[default]
None = ffi::lgraph_api_access_level_none as isize,
Read = ffi::lgraph_api_access_level_read as isize,
Write = ffi::lgraph_api_access_level_write as isize,
Full = ffi::lgraph_api_access_level_full as isize,
}
impl TryFrom<u32> for AccessLevel {
type Error = crate::Error;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
ffi::lgraph_api_access_level_none => Ok(Self::None),
ffi::lgraph_api_access_level_read => Ok(Self::Read),
ffi::lgraph_api_access_level_write => Ok(Self::Write),
ffi::lgraph_api_access_level_full => Ok(Self::Full),
_ => Err(crate::Error::new("Invalid parameter.".to_string())),
}
}
}
impl Display for AccessLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(transparent)]
pub struct Date {
inner: chrono::NaiveDate,
}
impl Deref for Date {
type Target = chrono::NaiveDate;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl AsRef<chrono::NaiveDate> for Date {
fn as_ref(&self) -> &chrono::NaiveDate {
&self.inner
}
}
impl Date {
pub fn from_native(native: chrono::NaiveDate) -> Self {
Date { inner: native }
}
pub(crate) fn from_raw_date(raw: &RawDate) -> Self {
Date {
inner: chrono::NaiveDate::from_num_days_from_ce_opt(raw.days_since_epoch()).unwrap(),
}
}
pub(crate) fn as_raw_date(&self) -> RawDate {
RawDate::from_days(self.num_days_from_ce())
}
}
impl Debug for Date {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.inner)
}
}
impl Display for Date {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Default)]
#[repr(transparent)]
pub struct DateTime {
inner: chrono::NaiveDateTime,
}
impl Deref for DateTime {
type Target = chrono::NaiveDateTime;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl AsRef<chrono::NaiveDateTime> for DateTime {
fn as_ref(&self) -> &chrono::NaiveDateTime {
&self.inner
}
}
impl DateTime {
pub fn from_native(native: chrono::NaiveDateTime) -> Self {
DateTime { inner: native }
}
pub fn from_timestamp_opt(secs: i64) -> Option<Self> {
chrono::NaiveDateTime::from_timestamp_opt(secs, 0).map(Self::from_native)
}
pub(crate) fn from_raw_datetime(raw: &RawDateTime) -> Self {
DateTime {
inner: chrono::NaiveDateTime::from_timestamp_opt(raw.seconds_since_epoch(), 0).unwrap(),
}
}
pub(crate) fn as_raw_datetime(&self) -> RawDateTime {
RawDateTime::from_seconds_since_epoch(self.timestamp())
}
}
impl Debug for DateTime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.inner)
}
}
impl Display for DateTime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
#[cfg(test)]
mod tests {
use super::{AccessLevel, Date, DateTime, EdgeUid};
use chrono::Datelike;
#[test]
fn test_edge_uid() {
let euid = EdgeUid {
src: 1,
lid: 1,
tid: 1,
dst: 1,
eid: 1,
};
let raw_euid = euid.as_raw();
assert_eq!(raw_euid.src(), 1);
assert_eq!(raw_euid.lid(), 1);
assert_eq!(raw_euid.tid(), 1);
assert_eq!(raw_euid.dst(), 1);
assert_eq!(raw_euid.eid(), 1);
}
#[test]
fn test_access_level() {
let none: AccessLevel = libtugraph_sys::lgraph_api_access_level_none
.try_into()
.expect("faield to convert access_level_none");
assert!(matches!(none, AccessLevel::None));
let read: AccessLevel = libtugraph_sys::lgraph_api_access_level_read
.try_into()
.expect("faield to convert access_level_read");
assert!(matches!(read, AccessLevel::Read));
let write: AccessLevel = libtugraph_sys::lgraph_api_access_level_write
.try_into()
.expect("faield to convert access_level_write");
assert!(matches!(write, AccessLevel::Write));
let full: AccessLevel = libtugraph_sys::lgraph_api_access_level_full
.try_into()
.expect("faield to convert access_level_full");
assert!(matches!(full, AccessLevel::Full));
let invalid: Result<AccessLevel, _> = 4_u32.try_into();
assert!(invalid.is_err());
}
#[test]
fn test_date() {
let date = Date::from_native(chrono::NaiveDate::from_num_days_from_ce_opt(10000).unwrap());
let raw_date = date.as_raw_date();
assert_eq!(raw_date.days_since_epoch(), date.num_days_from_ce());
}
#[test]
fn test_datetime() {
let datetime = DateTime::from_native(
chrono::NaiveDateTime::from_timestamp_opt(1_000_000_000, 0).unwrap(),
);
let raw_datetime = datetime.as_raw_datetime();
assert_eq!(raw_datetime.seconds_since_epoch(), datetime.timestamp());
}
}