pipebase 0.2.0

A tokio based runtime library for data integration app
Documentation
use chrono::{DateTime, Duration, Local, NaiveDate, NaiveDateTime, Utc};
use std::collections::HashMap;

use super::{Period, Timestamp};

#[derive(Debug, Clone, PartialEq)]
pub enum Value {
    Bool(Option<bool>),
    UnsignedInteger(Option<u32>),
    Integer(Option<i32>),
    UnsignedLong(Option<u64>),
    Long(Option<i64>),
    Float(Option<f32>),
    Double(Option<f64>),
    String(Option<String>),
    Date(Option<NaiveDate>),
    DateTime(Option<NaiveDateTime>),
    Duration(Option<Duration>),
    LocalTime(Option<DateTime<Local>>),
    UtcTime(Option<DateTime<Utc>>),
    UnsignedBytes(Vec<u8>),
    Array(Vec<Value>),
    Attributes(HashMap<String, Value>),
}

impl From<bool> for Value {
    fn from(v: bool) -> Self {
        Value::Bool(v.into())
    }
}

impl From<Option<bool>> for Value {
    fn from(v: Option<bool>) -> Self {
        Value::Bool(v)
    }
}

impl From<u32> for Value {
    fn from(v: u32) -> Self {
        Value::UnsignedInteger(v.into())
    }
}

impl From<Option<u32>> for Value {
    fn from(v: Option<u32>) -> Self {
        Value::UnsignedInteger(v)
    }
}

impl From<i32> for Value {
    fn from(v: i32) -> Self {
        Value::Integer(v.into())
    }
}

impl From<Option<i32>> for Value {
    fn from(v: Option<i32>) -> Self {
        Value::Integer(v)
    }
}

impl From<u64> for Value {
    fn from(v: u64) -> Self {
        Value::UnsignedLong(v.into())
    }
}

impl From<Option<u64>> for Value {
    fn from(v: Option<u64>) -> Self {
        Value::UnsignedLong(v)
    }
}

impl From<i64> for Value {
    fn from(v: i64) -> Self {
        Value::Long(v.into())
    }
}

impl From<Option<i64>> for Value {
    fn from(v: Option<i64>) -> Self {
        Value::Long(v)
    }
}

impl From<f32> for Value {
    fn from(v: f32) -> Self {
        Value::Float(v.into())
    }
}

impl From<Option<f32>> for Value {
    fn from(v: Option<f32>) -> Self {
        Value::Float(v)
    }
}

impl From<f64> for Value {
    fn from(v: f64) -> Self {
        Value::Double(v.into())
    }
}

impl From<Option<f64>> for Value {
    fn from(v: Option<f64>) -> Self {
        Value::Double(v)
    }
}

impl From<String> for Value {
    fn from(v: String) -> Self {
        Value::String(v.into())
    }
}

impl From<Option<String>> for Value {
    fn from(v: Option<String>) -> Self {
        Value::String(v)
    }
}

impl From<NaiveDate> for Value {
    fn from(v: NaiveDate) -> Self {
        Value::Date(v.into())
    }
}

impl From<Option<NaiveDate>> for Value {
    fn from(v: Option<NaiveDate>) -> Self {
        Value::Date(v)
    }
}

impl From<NaiveDateTime> for Value {
    fn from(v: NaiveDateTime) -> Self {
        Value::DateTime(v.into())
    }
}

impl From<Option<NaiveDateTime>> for Value {
    fn from(v: Option<NaiveDateTime>) -> Self {
        Value::DateTime(v)
    }
}

impl From<Duration> for Value {
    fn from(v: Duration) -> Self {
        Value::Duration(v.into())
    }
}

impl From<Option<Duration>> for Value {
    fn from(v: Option<Duration>) -> Self {
        Value::Duration(v)
    }
}

impl From<Period> for Value {
    fn from(v: Period) -> Self {
        let v = match v {
            Period::Days(v) => Duration::days(v),
            Period::Hours(v) => Duration::hours(v),
            Period::Minutes(v) => Duration::minutes(v),
            Period::Secs(v) => Duration::seconds(v),
            Period::Millis(v) => Duration::milliseconds(v),
        };
        Value::Duration(v.into())
    }
}

impl From<Option<Period>> for Value {
    fn from(v: Option<Period>) -> Self {
        match v {
            Some(v) => v.into(),
            None => Value::Duration(None),
        }
    }
}

impl From<DateTime<Local>> for Value {
    fn from(v: DateTime<Local>) -> Self {
        Value::LocalTime(v.into())
    }
}

impl From<Option<DateTime<Local>>> for Value {
    fn from(v: Option<DateTime<Local>>) -> Self {
        Value::LocalTime(v)
    }
}

impl From<DateTime<Utc>> for Value {
    fn from(v: DateTime<Utc>) -> Self {
        Value::UtcTime(v.into())
    }
}

impl From<Option<DateTime<Utc>>> for Value {
    fn from(v: Option<DateTime<Utc>>) -> Self {
        Value::UtcTime(v)
    }
}

impl From<Timestamp> for Value {
    fn from(v: Timestamp) -> Self {
        match v {
            Timestamp::Millis(v) => Value::Duration(Duration::milliseconds(v as i64).into()),
            Timestamp::Secs(v) => Value::Duration(Duration::seconds(v as i64).into()),
        }
    }
}

impl From<Option<Timestamp>> for Value {
    fn from(v: Option<Timestamp>) -> Self {
        match v {
            Some(v) => v.into(),
            None => Value::Duration(None),
        }
    }
}

impl From<Vec<u8>> for Value {
    fn from(v: Vec<u8>) -> Self {
        Value::UnsignedBytes(v)
    }
}

impl<V> From<Vec<V>> for Value
where
    V: Into<Value>,
{
    fn from(v: Vec<V>) -> Self {
        let v: Vec<Value> = v.into_iter().map(|item| item.into()).collect();
        Value::Array(v)
    }
}

impl<V> From<HashMap<String, V>> for Value
where
    V: Into<Value>,
{
    fn from(v: HashMap<String, V>) -> Self {
        let v: HashMap<String, Value> = v.into_iter().map(|(k, v)| (k, v.into())).collect();
        Value::Attributes(v)
    }
}

pub trait IntoAttributes {
    fn into_attributes(self) -> HashMap<String, Value>;

    fn into_attribute_tuples(self) -> Vec<(String, Value)>;
}

#[cfg(test)]
mod tests {

    use crate::prelude::*;
    use std::collections::HashMap;

    #[derive(IntoAttributes)]
    struct Record {
        key: String,
        value: u32,
    }

    #[test]
    fn test_record_into_attributes() {
        let r = Record {
            key: "foo".to_owned(),
            value: 1,
        };
        let attributes: HashMap<String, Value> = r.into_attributes();
        assert_eq!(
            &Value::from("foo".to_owned()),
            attributes.get("key").unwrap()
        );
        assert_eq!(&Value::from(1 as u32), attributes.get("value").unwrap());
    }

    #[derive(IntoAttributes)]
    struct RecordWithAlias {
        #[attribute(alias = "id")]
        key: String,
        value: u32,
    }

    #[test]
    fn test_record_with_alias_into_attributes() {
        let r = RecordWithAlias {
            key: "foo".to_owned(),
            value: 1,
        };
        let attributes: HashMap<String, Value> = r.into_attributes();
        assert_eq!(
            &Value::from("foo".to_owned()),
            attributes.get("id").unwrap()
        );
        assert_eq!(&Value::from(1 as u32), attributes.get("value").unwrap());
    }
}