| @@ -2654,6 +2654,7 @@ version = "0.3.10" | |||||
| dependencies = [ | dependencies = [ | ||||
| "arrow 54.2.1", | "arrow 54.2.1", | ||||
| "arrow_convert", | "arrow_convert", | ||||
| "chrono", | |||||
| "eyre", | "eyre", | ||||
| ] | ] | ||||
| @@ -13,3 +13,4 @@ repository.workspace = true | |||||
| arrow = { workspace = true } | arrow = { workspace = true } | ||||
| arrow_convert = "0.8.1" | arrow_convert = "0.8.1" | ||||
| eyre = "0.6.8" | eyre = "0.6.8" | ||||
| chrono = "0.4.39" | |||||
| @@ -2,6 +2,7 @@ use arrow::{ | |||||
| array::{Array, AsArray, PrimitiveArray, StringArray}, | array::{Array, AsArray, PrimitiveArray, StringArray}, | ||||
| datatypes::ArrowPrimitiveType, | datatypes::ArrowPrimitiveType, | ||||
| }; | }; | ||||
| use chrono::{NaiveDate, NaiveTime}; | |||||
| use arrow_convert::deserialize::TryIntoCollection; | use arrow_convert::deserialize::TryIntoCollection; | ||||
| use eyre::ContextCompat; | use eyre::ContextCompat; | ||||
| @@ -355,6 +356,45 @@ impl<'a> TryFrom<&'a ArrowData> for String { | |||||
| } | } | ||||
| } | } | ||||
| impl TryFrom<&ArrowData> for NaiveDate { | |||||
| type Error = eyre::Report; | |||||
| fn try_from(value: &ArrowData) -> Result<Self, Self::Error> { | |||||
| let array = value | |||||
| .as_primitive_opt::<arrow::datatypes::Date32Type>() | |||||
| .context("not a primitive Date32Type array")?; | |||||
| if array.is_empty() { | |||||
| eyre::bail!("empty array"); | |||||
| } | |||||
| if array.len() != 1 { | |||||
| eyre::bail!("expected length 1"); | |||||
| } | |||||
| if array.null_count() != 0 { | |||||
| eyre::bail!("array has nulls"); | |||||
| } | |||||
| Ok(array.value_as_date(0).context("data type cannot be converted to NaiveDate")?) | |||||
| } | |||||
| } | |||||
| impl TryFrom<&ArrowData> for NaiveTime { | |||||
| type Error = eyre::Report; | |||||
| fn try_from(value: &ArrowData) -> Result<Self, Self::Error> { | |||||
| let array = value | |||||
| .as_primitive_opt::<arrow::datatypes::Time64NanosecondType>() | |||||
| .context("not a primitive Time64NanosecondType array")?; | |||||
| if array.is_empty() { | |||||
| eyre::bail!("empty array"); | |||||
| } | |||||
| if array.len() != 1 { | |||||
| eyre::bail!("expected length 1"); | |||||
| } | |||||
| if array.null_count() != 0 { | |||||
| eyre::bail!("array has nulls"); | |||||
| } | |||||
| Ok(array.value_as_time(0).context("data type cannot be converted to NaiveTime")?) | |||||
| } | |||||
| } | |||||
| fn extract_single_primitive<T>(array: &PrimitiveArray<T>) -> Result<T::Native, eyre::Error> | fn extract_single_primitive<T>(array: &PrimitiveArray<T>) -> Result<T::Native, eyre::Error> | ||||
| where | where | ||||
| T: ArrowPrimitiveType, | T: ArrowPrimitiveType, | ||||
| @@ -1,6 +1,8 @@ | |||||
| use crate::IntoArrow; | use crate::IntoArrow; | ||||
| use arrow::array::{Array, ArrayRef, PrimitiveArray, StringArray}; | use arrow::array::{Array, ArrayRef, PrimitiveArray, StringArray}; | ||||
| use arrow_convert::serialize::TryIntoArrow; | use arrow_convert::serialize::TryIntoArrow; | ||||
| use crate::IntoArrow; | |||||
| use chrono::{NaiveDate, NaiveTime}; | |||||
| impl IntoArrow for bool { | impl IntoArrow for bool { | ||||
| type A = arrow::array::BooleanArray; | type A = arrow::array::BooleanArray; | ||||
| @@ -146,7 +148,21 @@ impl IntoArrow for () { | |||||
| } | } | ||||
| } | } | ||||
| impl IntoArrow for &String { | |||||
| impl IntoArrow for NaiveDate { | |||||
| type A = arrow::array::Date32Array; | |||||
| fn into_arrow(self) -> Self::A { | |||||
| arrow::array::Date32Array::from(vec![arrow::datatypes::Date32Type::from_naive_date(self)]) | |||||
| } | |||||
| } | |||||
| impl IntoArrow for NaiveTime { | |||||
| type A = arrow::array::Time64NanosecondArray; | |||||
| fn into_arrow(self) -> Self::A { | |||||
| arrow::array::Time64NanosecondArray::from(vec![arrow::array::temporal_conversions::time_to_time64ns(self)]) | |||||
| } | |||||
| } | |||||
| impl IntoArrow for String { | |||||
| type A = StringArray; | type A = StringArray; | ||||
| fn into_arrow(self) -> Self::A { | fn into_arrow(self) -> Self::A { | ||||
| @@ -1,6 +1,5 @@ | |||||
| use dora_arrow_convert::{ArrowData, IntoArrow}; | use dora_arrow_convert::{ArrowData, IntoArrow}; | ||||
| use std::convert::TryFrom; | |||||
| use std::sync::Arc; | |||||
| use chrono::{NaiveDate, NaiveTime}; | |||||
| #[cfg(test)] | #[cfg(test)] | ||||
| mod tests { | mod tests { | ||||
| @@ -235,4 +234,24 @@ mod tests { | |||||
| assert_eq!(value_vec_f64, result_vec_f64); | assert_eq!(value_vec_f64, result_vec_f64); | ||||
| Ok(()) | Ok(()) | ||||
| } | } | ||||
| #[test] | |||||
| fn test_naivedate_round_trip() -> Result<(), Report> { | |||||
| let value_naivedate = NaiveDate::from_ymd_opt(2024,4,4).unwrap(); | |||||
| let arrow_array = value_naivedate.into_arrow(); | |||||
| let data: ArrowData = ArrowData(Arc::new(arrow_array)); | |||||
| let result_naivedate: NaiveDate = TryFrom::try_from(&data)?; | |||||
| assert_eq!(value_naivedate,result_naivedate); | |||||
| Ok(()) | |||||
| } | |||||
| #[test] | |||||
| fn test_naivetime_round_trip() -> Result<(), Report> { | |||||
| let value_naivetime = NaiveTime::from_hms_milli_opt(23,56,4,10).unwrap(); | |||||
| let arrow_array = value_naivetime.into_arrow(); | |||||
| let data: ArrowData = ArrowData(Arc::new(arrow_array)); | |||||
| let result_naivetime: NaiveTime = TryFrom::try_from(&data)?; | |||||
| assert_eq!(value_naivetime,result_naivetime); | |||||
| Ok(()) | |||||
| } | |||||
| } | } | ||||