use std::{collections::HashMap, hash::BuildHasher};
use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, Offset, TimeZone};
use chrono_tz::Tz;
use crate::value::*;
#[doc(hidden)]
macro_rules! impl_from {
($T:ty, $V:ident) => {
impl ::std::convert::From<$T> for $crate::Value {
fn from(value: $T) -> Self {
Value::$V(value)
}
}
};
}
impl_from!(bool, Boolean);
macro_rules! impl_from_int {
($($T:ty),+) => {
$(
impl ::std::convert::From<$T> for $crate::Value {
fn from(value: $T) -> Self {
Value::Integer(value as i64)
}
}
)*
};
}
impl_from_int!(i8, i16, i32, i64);
impl_from!(f64, Float);
impl From<&[u8]> for Value {
fn from(value: &[u8]) -> Self {
Value::Bytes(value.to_vec())
}
}
impl_from!(Vec<u8>, Bytes);
impl<T> From<Vec<T>> for Value
where
T: Into<Value>,
{
fn from(value: Vec<T>) -> Self {
Value::List(value.into_iter().map(T::into).collect())
}
}
impl<K, V, S> From<HashMap<K, V, S>> for Value
where
K: Into<std::string::String>,
V: Into<Value>,
S: BuildHasher,
{
fn from(value: HashMap<K, V, S>) -> Self {
Value::Map(
value
.into_iter()
.map(|(k, v)| (K::into(k), V::into(v)))
.collect(),
)
}
}
impl From<&str> for Value {
fn from(value: &str) -> Self {
Value::String(String::from(value))
}
}
impl_from!(String, String);
impl_from!(Node, Node);
impl_from!(Relationship, Relationship);
impl_from!(Path, Path);
impl_from!(UnboundRelationship, UnboundRelationship);
impl_from!(NaiveDate, Date);
impl<O: Offset> From<(NaiveTime, O)> for Value {
fn from(pair: (NaiveTime, O)) -> Self {
Value::Time(pair.0, pair.1.fix())
}
}
impl<T: TimeZone> From<DateTime<T>> for Value {
fn from(value: DateTime<T>) -> Self {
Value::DateTimeOffset(DateTime::from_utc(value.naive_utc(), value.offset().fix()))
}
}
impl From<(NaiveDateTime, chrono_tz::Tz)> for Value {
fn from(pair: (NaiveDateTime, chrono_tz::Tz)) -> Self {
Value::DateTimeZoned(pair.1.from_utc_datetime(&pair.0))
}
}
impl_from!(NaiveTime, LocalTime);
impl_from!(NaiveDateTime, LocalDateTime);
impl_from!(Duration, Duration);
impl From<std::time::Duration> for Value {
fn from(value: std::time::Duration) -> Self {
Value::Duration(Duration::from(value))
}
}
impl_from!(Point2D, Point2D);
impl_from!(Point3D, Point3D);
impl<T: Into<Value>> From<Option<T>> for Value {
fn from(option: Option<T>) -> Self {
match option {
Some(v) => v.into(),
None => Value::Null,
}
}
}
#[doc(hidden)]
macro_rules! impl_try_from_value {
($T:ty, $V:ident) => {
impl ::std::convert::TryFrom<$crate::Value> for $T {
type Error = $crate::error::ConversionError;
fn try_from(value: $crate::Value) -> $crate::error::ConversionResult<Self> {
match value {
$crate::Value::$V(inner) => Ok(inner),
_ => Err($crate::error::ConversionError::FromValue(value)),
}
}
}
};
}
impl_try_from_value!(bool, Boolean);
macro_rules! impl_try_from_value_for_ints {
($($T:ty),+) => {
$(
impl TryFrom<$crate::Value> for $T {
type Error = $crate::error::ConversionError;
fn try_from(value: $crate::Value) -> $crate::error::ConversionResult<Self> {
use ::std::convert::TryInto;
match value {
$crate::Value::Integer(integer) => Ok(integer.try_into()?),
_ => Err($crate::error::ConversionError::FromValue(value)),
}
}
}
)*
};
}
impl_try_from_value_for_ints!(i8, i16, i32, i64);
impl_try_from_value!(f64, Float);
impl_try_from_value!(Vec<u8>, Bytes);
impl<T> TryFrom<Value> for Vec<T>
where
T: TryFrom<Value, Error = ConversionError>,
{
type Error = ConversionError;
fn try_from(value: Value) -> ConversionResult<Self> {
match value {
Value::List(list) => list.into_iter().map(T::try_from).collect(),
_ => Err(ConversionError::FromValue(value)),
}
}
}
impl_try_from_value!(Vec<Value>, List);
impl<V, S> TryFrom<Value> for HashMap<std::string::String, V, S>
where
V: TryFrom<Value, Error = ConversionError>,
S: BuildHasher + Default,
{
type Error = ConversionError;
fn try_from(value: Value) -> ConversionResult<Self> {
match value {
Value::Map(map) => {
let mut new_map = HashMap::with_capacity_and_hasher(map.len(), Default::default());
for (k, v) in map {
new_map.insert(k, V::try_from(v)?);
}
Ok(new_map)
}
_ => Err(ConversionError::FromValue(value)),
}
}
}
impl<S> TryFrom<Value> for HashMap<std::string::String, Value, S>
where
S: BuildHasher + Default,
{
type Error = ConversionError;
fn try_from(value: Value) -> ConversionResult<Self> {
match value {
Value::Map(map) => {
let mut new_map = HashMap::with_capacity_and_hasher(map.len(), Default::default());
for (k, v) in map {
new_map.insert(k, v);
}
Ok(new_map)
}
_ => Err(ConversionError::FromValue(value)),
}
}
}
impl_try_from_value!(String, String);
impl_try_from_value!(Node, Node);
impl_try_from_value!(Relationship, Relationship);
impl_try_from_value!(Path, Path);
impl_try_from_value!(UnboundRelationship, UnboundRelationship);
impl_try_from_value!(NaiveDate, Date);
impl TryFrom<Value> for DateTime<FixedOffset> {
type Error = ConversionError;
fn try_from(value: Value) -> ConversionResult<Self> {
match value {
Value::DateTimeOffset(date_time_offset) => Ok(date_time_offset),
Value::DateTimeZoned(date_time_zoned) => {
Ok(date_time_zoned.with_timezone(&date_time_zoned.offset().fix()))
}
_ => Err(ConversionError::FromValue(value)),
}
}
}
impl_try_from_value!(DateTime<Tz>, DateTimeZoned);
impl_try_from_value!(NaiveTime, LocalTime);
impl_try_from_value!(NaiveDateTime, LocalDateTime);
impl_try_from_value!(Duration, Duration);
impl_try_from_value!(Point2D, Point2D);
impl_try_from_value!(Point3D, Point3D);