use chrono::{
DateTime, Datelike, FixedOffset, LocalResult, NaiveDate, NaiveDateTime, TimeZone, Utc,
};
use chrono_tz::Tz;
use std::collections::HashMap;
use std::hash::Hash;
use crate::error::{ConvertError, Error, Result};
use super::{NumberValue, Value, DAYS_FROM_CE};
use jiff::{tz::TimeZone as JiffTimeZone, Timestamp, Zoned};
impl TryFrom<Value> for bool {
type Error = Error;
fn try_from(val: Value) -> Result<Self> {
match val {
Value::Boolean(b) => Ok(b),
Value::Number(n) => Ok(n != NumberValue::Int8(0)),
_ => Err(ConvertError::new("bool", format!("{val:?}")).into()),
}
}
}
macro_rules! impl_try_from_number_value {
($($t:ty),*) => {
$(
impl TryFrom<Value> for $t {
type Error = Error;
fn try_from(val: Value) -> Result<Self> {
match val {
Value::Number(NumberValue::Int8(i)) => Ok(i as $t),
Value::Number(NumberValue::Int16(i)) => Ok(i as $t),
Value::Number(NumberValue::Int32(i)) => Ok(i as $t),
Value::Number(NumberValue::Int64(i)) => Ok(i as $t),
Value::Number(NumberValue::UInt8(i)) => Ok(i as $t),
Value::Number(NumberValue::UInt16(i)) => Ok(i as $t),
Value::Number(NumberValue::UInt32(i)) => Ok(i as $t),
Value::Number(NumberValue::UInt64(i)) => Ok(i as $t),
Value::Number(NumberValue::Float32(i)) => Ok(i as $t),
Value::Number(NumberValue::Float64(i)) => Ok(i as $t),
Value::Date(i) => Ok(i as $t),
_ => Err(ConvertError::new("number", format!("{:?}", val)).into()),
}
}
}
)*
};
}
impl_try_from_number_value!(u8);
impl_try_from_number_value!(u16);
impl_try_from_number_value!(u32);
impl_try_from_number_value!(u64);
impl_try_from_number_value!(i8);
impl_try_from_number_value!(i16);
impl_try_from_number_value!(i32);
impl_try_from_number_value!(i64);
impl_try_from_number_value!(f32);
impl_try_from_number_value!(f64);
fn unix_micros_from_zoned(zdt: &Zoned) -> i64 {
zdt.timestamp().as_microsecond()
}
fn naive_datetime_from_micros(micros: i64) -> Result<NaiveDateTime> {
DateTime::<Utc>::from_timestamp_micros(micros)
.map(|dt| dt.naive_utc())
.ok_or_else(|| Error::Parsing(format!("invalid unix timestamp {micros}")))
}
pub fn zoned_to_chrono_datetime(zdt: &Zoned) -> Result<DateTime<Tz>> {
let tz_name = zdt.time_zone().iana_name().ok_or_else(|| {
ConvertError::new(
"DateTime",
"timestamp does not contain an IANA time zone".to_string(),
)
})?;
let tz: Tz = tz_name.parse().map_err(|_| {
ConvertError::new(
"DateTime",
format!("invalid time zone identifier {tz_name}"),
)
})?;
let micros = unix_micros_from_zoned(zdt);
match tz.timestamp_micros(micros) {
LocalResult::Single(dt) => Ok(dt),
LocalResult::Ambiguous(dt, _) => Ok(dt),
LocalResult::None => Err(Error::Parsing(format!(
"time {micros} not exists in timezone {tz_name}"
))),
}
}
pub fn zoned_to_chrono_fixed_offset(zdt: &Zoned) -> Result<DateTime<FixedOffset>> {
let offset_seconds = zdt.offset().seconds();
let offset = FixedOffset::east_opt(offset_seconds)
.ok_or_else(|| Error::Parsing(format!("invalid offset {offset_seconds}")))?;
let micros = unix_micros_from_zoned(zdt);
let naive = naive_datetime_from_micros(micros)?;
Ok(DateTime::<FixedOffset>::from_naive_utc_and_offset(
naive, offset,
))
}
fn zoned_from_naive_datetime(naive_dt: &NaiveDateTime) -> Zoned {
let micros = naive_dt.and_utc().timestamp_micros();
let timestamp = Timestamp::from_microsecond(micros)
.expect("NaiveDateTime out of range for Timestamp conversion");
timestamp.to_zoned(JiffTimeZone::UTC)
}
impl TryFrom<Value> for NaiveDateTime {
type Error = Error;
fn try_from(val: Value) -> Result<Self> {
match val {
Value::Timestamp(dt) => naive_datetime_from_micros(unix_micros_from_zoned(&dt)),
_ => Err(ConvertError::new("NaiveDateTime", format!("{val}")).into()),
}
}
}
impl TryFrom<Value> for DateTime<Tz> {
type Error = Error;
fn try_from(val: Value) -> Result<Self> {
match val {
Value::Timestamp(dt) => zoned_to_chrono_datetime(&dt),
_ => Err(ConvertError::new("DateTime", format!("{val}")).into()),
}
}
}
impl TryFrom<Value> for NaiveDate {
type Error = Error;
fn try_from(val: Value) -> Result<Self> {
match val {
Value::Date(i) => {
let days = i + DAYS_FROM_CE;
match NaiveDate::from_num_days_from_ce_opt(days) {
Some(d) => Ok(d),
None => Err(ConvertError::new("NaiveDate", "".to_string()).into()),
}
}
_ => Err(ConvertError::new("NaiveDate", format!("{val}")).into()),
}
}
}
impl<V> TryFrom<Value> for Vec<V>
where
V: TryFrom<Value, Error = Error>,
{
type Error = Error;
fn try_from(val: Value) -> Result<Self> {
match val {
Value::Binary(vals) => vals
.into_iter()
.map(|v| V::try_from(Value::Number(NumberValue::UInt8(v))))
.collect(),
Value::Array(vals) => vals.into_iter().map(V::try_from).collect(),
Value::EmptyArray => Ok(vec![]),
_ => Err(ConvertError::new("Vec", format!("{val}")).into()),
}
}
}
impl<K, V> TryFrom<Value> for HashMap<K, V>
where
K: TryFrom<Value, Error = Error> + Eq + Hash,
V: TryFrom<Value, Error = Error>,
{
type Error = Error;
fn try_from(val: Value) -> Result<Self> {
match val {
Value::Map(kvs) => {
let mut map = HashMap::new();
for (k, v) in kvs {
let k = K::try_from(k)?;
let v = V::try_from(v)?;
map.insert(k, v);
}
Ok(map)
}
Value::EmptyMap => Ok(HashMap::new()),
_ => Err(ConvertError::new("HashMap", format!("{val}")).into()),
}
}
}
macro_rules! replace_expr {
($_t:tt $sub:expr) => {
$sub
};
}
macro_rules! impl_tuple_from_value {
( $($Ti:tt),+ ) => {
impl<$($Ti),+> TryFrom<Value> for ($($Ti,)+)
where
$($Ti: TryFrom<Value>),+
{
type Error = String;
fn try_from(val: Value) -> Result<Self, String> {
let expected_len = <[()]>::len(&[$(replace_expr!(($Ti) ())),*]);
match val {
Value::Tuple(vals) => {
if expected_len != vals.len() {
return Err(format!("value tuple size mismatch: expected {} columns, got {}", expected_len, vals.len()));
}
let mut vals_iter = vals.into_iter().enumerate();
Ok((
$(
{
let (col_ix, col_value) = vals_iter
.next()
.unwrap(); let t = col_value.get_type();
$Ti::try_from(col_value)
.map_err(|_| format!("failed converting column {} from type({:?}) to type({})", col_ix, t, std::any::type_name::<$Ti>()))?
}
,)+
))
}
_ => Err(format!("expected tuple, got {:?}", val)),
}
}
}
}
}
impl_tuple_from_value!(T1);
impl_tuple_from_value!(T1, T2);
impl_tuple_from_value!(T1, T2, T3);
impl_tuple_from_value!(T1, T2, T3, T4);
impl_tuple_from_value!(T1, T2, T3, T4, T5);
impl_tuple_from_value!(T1, T2, T3, T4, T5, T6);
impl_tuple_from_value!(T1, T2, T3, T4, T5, T6, T7);
impl_tuple_from_value!(T1, T2, T3, T4, T5, T6, T7, T8);
impl_tuple_from_value!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
impl_tuple_from_value!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
impl_tuple_from_value!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
impl_tuple_from_value!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
impl_tuple_from_value!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
impl_tuple_from_value!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
impl_tuple_from_value!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
impl_tuple_from_value!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
impl_tuple_from_value!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17);
impl_tuple_from_value!(
T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18
);
impl_tuple_from_value!(
T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19
);
impl_tuple_from_value!(
T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20
);
impl_tuple_from_value!(
T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21
);
impl_tuple_from_value!(
T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21,
T22
);
macro_rules! impl_try_from_to_option {
($($t:ty),*) => {
$(
impl TryFrom<Value> for Option<$t> {
type Error = Error;
fn try_from(val: Value) -> Result<Self> {
match val {
Value::Null => Ok(None),
_ => {
let inner: $t = val.try_into()?;
Ok(Some(inner))
},
}
}
}
)*
};
}
impl_try_from_to_option!(String);
impl_try_from_to_option!(bool);
impl_try_from_to_option!(u8);
impl_try_from_to_option!(u16);
impl_try_from_to_option!(u32);
impl_try_from_to_option!(u64);
impl_try_from_to_option!(i8);
impl_try_from_to_option!(i16);
impl_try_from_to_option!(i32);
impl_try_from_to_option!(i64);
impl_try_from_to_option!(f32);
impl_try_from_to_option!(f64);
impl_try_from_to_option!(NaiveDateTime);
impl_try_from_to_option!(NaiveDate);
impl From<&String> for Value {
fn from(s: &String) -> Self {
Value::String(s.clone())
}
}
impl From<String> for Value {
fn from(s: String) -> Self {
Value::String(s)
}
}
impl From<&str> for Value {
fn from(s: &str) -> Self {
Value::String(s.to_string())
}
}
impl From<bool> for Value {
fn from(b: bool) -> Self {
Value::Boolean(b)
}
}
impl From<&bool> for Value {
fn from(b: &bool) -> Self {
Value::Boolean(*b)
}
}
impl From<u8> for Value {
fn from(n: u8) -> Self {
Value::Number(NumberValue::UInt8(n))
}
}
impl From<&u8> for Value {
fn from(n: &u8) -> Self {
Value::Number(NumberValue::UInt8(*n))
}
}
impl From<u16> for Value {
fn from(n: u16) -> Self {
Value::Number(NumberValue::UInt16(n))
}
}
impl From<&u16> for Value {
fn from(n: &u16) -> Self {
Value::Number(NumberValue::UInt16(*n))
}
}
impl From<u32> for Value {
fn from(n: u32) -> Self {
Value::Number(NumberValue::UInt32(n))
}
}
impl From<&u32> for Value {
fn from(n: &u32) -> Self {
Value::Number(NumberValue::UInt32(*n))
}
}
impl From<u64> for Value {
fn from(n: u64) -> Self {
Value::Number(NumberValue::UInt64(n))
}
}
impl From<&u64> for Value {
fn from(n: &u64) -> Self {
Value::Number(NumberValue::UInt64(*n))
}
}
impl From<i8> for Value {
fn from(n: i8) -> Self {
Value::Number(NumberValue::Int8(n))
}
}
impl From<&i8> for Value {
fn from(n: &i8) -> Self {
Value::Number(NumberValue::Int8(*n))
}
}
impl From<i16> for Value {
fn from(n: i16) -> Self {
Value::Number(NumberValue::Int16(n))
}
}
impl From<&i16> for Value {
fn from(n: &i16) -> Self {
Value::Number(NumberValue::Int16(*n))
}
}
impl From<i32> for Value {
fn from(n: i32) -> Self {
Value::Number(NumberValue::Int32(n))
}
}
impl From<&i32> for Value {
fn from(n: &i32) -> Self {
Value::Number(NumberValue::Int32(*n))
}
}
impl From<i64> for Value {
fn from(n: i64) -> Self {
Value::Number(NumberValue::Int64(n))
}
}
impl From<&i64> for Value {
fn from(n: &i64) -> Self {
Value::Number(NumberValue::Int64(*n))
}
}
impl From<f32> for Value {
fn from(n: f32) -> Self {
Value::Number(NumberValue::Float32(n))
}
}
impl From<&f32> for Value {
fn from(n: &f32) -> Self {
Value::Number(NumberValue::Float32(*n))
}
}
impl From<f64> for Value {
fn from(n: f64) -> Self {
Value::Number(NumberValue::Float64(n))
}
}
impl From<NaiveDate> for Value {
fn from(date: NaiveDate) -> Self {
let days = date.num_days_from_ce() - DAYS_FROM_CE;
Value::Date(days)
}
}
impl From<&NaiveDate> for Value {
fn from(date: &NaiveDate) -> Self {
let days = date.num_days_from_ce() - DAYS_FROM_CE;
Value::Date(days)
}
}
impl From<NaiveDateTime> for Value {
fn from(naive_dt: NaiveDateTime) -> Self {
Value::Timestamp(zoned_from_naive_datetime(&naive_dt))
}
}
impl From<&NaiveDateTime> for Value {
fn from(naive_dt: &NaiveDateTime) -> Self {
Value::Timestamp(zoned_from_naive_datetime(naive_dt))
}
}
impl From<&f64> for Value {
fn from(n: &f64) -> Self {
Value::Number(NumberValue::Float64(*n))
}
}