diff --git a/src/array/dictionary/mod.rs b/src/array/dictionary/mod.rs index e52ff4f754a..b71aa13d511 100644 --- a/src/array/dictionary/mod.rs +++ b/src/array/dictionary/mod.rs @@ -127,7 +127,10 @@ impl Array for DictionaryArray { } } -impl std::fmt::Display for DictionaryArray { +impl std::fmt::Display for DictionaryArray +where + PrimitiveArray: std::fmt::Display, +{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "{:?}{{", self.data_type())?; writeln!(f, "keys: {},", self.keys())?; diff --git a/src/array/mod.rs b/src/array/mod.rs index 1574a45abe9..33ff57d5e05 100644 --- a/src/array/mod.rs +++ b/src/array/mod.rs @@ -1,4 +1,5 @@ use std::any::Any; +use std::fmt::Display; use crate::error::Result; use crate::types::days_ms; @@ -7,7 +8,7 @@ use crate::{ datatypes::{DataType, IntervalUnit}, }; -pub trait Array: std::fmt::Debug + std::fmt::Display + Send + Sync + ToFfi { +pub trait Array: std::fmt::Debug + Send + Sync + ToFfi { fn as_any(&self) -> &dyn Any; /// The length of the array @@ -63,6 +64,74 @@ pub trait Array: std::fmt::Debug + std::fmt::Display + Send + Sync + ToFfi { fn slice(&self, offset: usize, length: usize) -> Box; } +macro_rules! general_dyn { + ($array:expr, $ty:ty, $f:expr) => {{ + let array = $array.as_any().downcast_ref::<$ty>().unwrap(); + ($f)(array) + }}; +} + +macro_rules! fmt_dyn { + ($array:expr, $ty:ty, $f:expr) => {{ + let mut f = |x: &$ty| x.fmt($f); + general_dyn!($array, $ty, f) + }}; +} + +impl Display for dyn Array { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.data_type() { + DataType::Null => fmt_dyn!(self, NullArray, f), + DataType::Boolean => fmt_dyn!(self, BooleanArray, f), + DataType::Int8 => fmt_dyn!(self, PrimitiveArray, f), + DataType::Int16 => fmt_dyn!(self, PrimitiveArray, f), + DataType::Int32 + | DataType::Date32 + | DataType::Time32(_) + | DataType::Interval(IntervalUnit::YearMonth) => { + fmt_dyn!(self, PrimitiveArray, f) + } + DataType::Interval(IntervalUnit::DayTime) => { + fmt_dyn!(self, PrimitiveArray, f) + } + DataType::Int64 + | DataType::Date64 + | DataType::Time64(_) + | DataType::Timestamp(_, _) + | DataType::Duration(_) => fmt_dyn!(self, PrimitiveArray, f), + DataType::Decimal(_, _) => fmt_dyn!(self, PrimitiveArray, f), + DataType::UInt8 => fmt_dyn!(self, PrimitiveArray, f), + DataType::UInt16 => fmt_dyn!(self, PrimitiveArray, f), + DataType::UInt32 => fmt_dyn!(self, PrimitiveArray, f), + DataType::UInt64 => fmt_dyn!(self, PrimitiveArray, f), + DataType::Float16 => unreachable!(), + DataType::Float32 => fmt_dyn!(self, PrimitiveArray, f), + DataType::Float64 => fmt_dyn!(self, PrimitiveArray, f), + DataType::Binary => fmt_dyn!(self, BinaryArray, f), + DataType::LargeBinary => fmt_dyn!(self, BinaryArray, f), + DataType::FixedSizeBinary(_) => fmt_dyn!(self, FixedSizeBinaryArray, f), + DataType::Utf8 => fmt_dyn!(self, Utf8Array::, f), + DataType::LargeUtf8 => fmt_dyn!(self, Utf8Array::, f), + DataType::List(_) => fmt_dyn!(self, ListArray::, f), + DataType::LargeList(_) => fmt_dyn!(self, ListArray::, f), + DataType::FixedSizeList(_, _) => fmt_dyn!(self, FixedSizeListArray, f), + DataType::Struct(_) => fmt_dyn!(self, StructArray, f), + DataType::Union(_) => unimplemented!(), + DataType::Dictionary(key_type, _) => match key_type.as_ref() { + DataType::Int8 => fmt_dyn!(self, DictionaryArray::, f), + DataType::Int16 => fmt_dyn!(self, DictionaryArray::, f), + DataType::Int32 => fmt_dyn!(self, DictionaryArray::, f), + DataType::Int64 => fmt_dyn!(self, DictionaryArray::, f), + DataType::UInt8 => fmt_dyn!(self, DictionaryArray::, f), + DataType::UInt16 => fmt_dyn!(self, DictionaryArray::, f), + DataType::UInt32 => fmt_dyn!(self, DictionaryArray::, f), + DataType::UInt64 => fmt_dyn!(self, DictionaryArray::, f), + _ => unreachable!(), + }, + } + } +} + /// Creates a new empty dynamic array pub fn new_empty_array(data_type: DataType) -> Box { match data_type { @@ -171,8 +240,8 @@ pub fn new_null_array(data_type: DataType, length: usize) -> Box { macro_rules! clone_dyn { ($array:expr, $ty:ty) => {{ - let array = $array.as_any().downcast_ref::<$ty>().unwrap(); - Box::new(array.clone()) + let f = |x: &$ty| Box::new(x.clone()); + general_dyn!($array, $ty, f) }}; } diff --git a/src/array/primitive/display.rs b/src/array/primitive/display.rs new file mode 100644 index 00000000000..968d4d9880a --- /dev/null +++ b/src/array/primitive/display.rs @@ -0,0 +1,303 @@ +use crate::{datatypes::*, temporal_conversions, types::days_ms}; + +use super::super::{display_fmt, Array}; +use super::PrimitiveArray; + +impl std::fmt::Display for PrimitiveArray { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let new_lines = false; + let head = &format!("{}", self.data_type()); + match self.data_type() { + DataType::Int32 => display_fmt(self.iter(), head, f, new_lines), + DataType::Date32 => display_fmt( + self.iter() + .map(|x| x.copied().map(temporal_conversions::date32_to_date)), + head, + f, + new_lines, + ), + DataType::Time32(TimeUnit::Second) => display_fmt( + self.iter() + .map(|x| x.copied().map(temporal_conversions::time32s_to_time)), + head, + f, + new_lines, + ), + DataType::Time32(TimeUnit::Millisecond) => display_fmt( + self.iter() + .map(|x| x.copied().map(temporal_conversions::time32ms_to_time)), + head, + f, + new_lines, + ), + DataType::Interval(IntervalUnit::YearMonth) => display_fmt( + self.iter().map(|x| x.map(|x| format!("{}d", x))), + head, + f, + new_lines, + ), + _ => unreachable!(), + } + } +} + +impl std::fmt::Display for PrimitiveArray { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let new_lines = false; + let head = &format!("{}", self.data_type()); + match self.data_type() { + DataType::Int64 => display_fmt(self.iter(), head, f, new_lines), + DataType::Date64 => display_fmt( + self.iter() + .map(|x| x.copied().map(temporal_conversions::date64_to_date)), + head, + f, + new_lines, + ), + DataType::Time64(TimeUnit::Microsecond) => display_fmt( + self.iter() + .map(|x| x.copied().map(temporal_conversions::time64us_to_time)), + head, + f, + new_lines, + ), + DataType::Time64(TimeUnit::Nanosecond) => display_fmt( + self.iter() + .map(|x| x.copied().map(temporal_conversions::time64ns_to_time)), + head, + f, + new_lines, + ), + DataType::Timestamp(TimeUnit::Second, None) => display_fmt( + self.iter().map(|x| { + x.copied() + .map(temporal_conversions::timestamp_s_to_datetime) + }), + head, + f, + new_lines, + ), + DataType::Timestamp(TimeUnit::Millisecond, None) => display_fmt( + self.iter().map(|x| { + x.copied() + .map(temporal_conversions::timestamp_ms_to_datetime) + }), + head, + f, + new_lines, + ), + DataType::Timestamp(TimeUnit::Microsecond, None) => display_fmt( + self.iter().map(|x| { + x.copied() + .map(temporal_conversions::timestamp_us_to_datetime) + }), + head, + f, + new_lines, + ), + DataType::Timestamp(TimeUnit::Nanosecond, None) => display_fmt( + self.iter().map(|x| { + x.copied() + .map(temporal_conversions::timestamp_ns_to_datetime) + }), + head, + f, + new_lines, + ), + DataType::Duration(unit) => { + let unit = match unit { + TimeUnit::Second => "s", + TimeUnit::Millisecond => "ms", + TimeUnit::Microsecond => "us", + TimeUnit::Nanosecond => "ns", + }; + display_fmt( + self.iter() + .map(|x| x.copied().map(|x| format!("{}{}", x, unit))), + head, + f, + new_lines, + ) + } + // todo + DataType::Timestamp(_, Some(_)) => display_fmt(self.iter(), head, f, new_lines), + _ => unreachable!(), + } + } +} + +impl std::fmt::Display for PrimitiveArray { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.data_type() { + DataType::Decimal(_, scale) => { + let new_lines = false; + let head = &format!("{}", self.data_type()); + // The number 999.99 has a precision of 5 and scale of 2 + let iter = self.iter().map(|x| { + x.copied().map(|x| { + let base = x / 10i128.pow(*scale as u32); + let decimals = x - base * 10i128.pow(*scale as u32); + format!("{}.{}", base, decimals) + }) + }); + display_fmt(iter, head, f, new_lines) + } + _ => unreachable!(), + } + } +} + +impl std::fmt::Display for PrimitiveArray { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let new_lines = false; + let head = &format!("{}", self.data_type()); + let iter = self.iter().map(|x| { + x.copied() + .map(|x| format!("{}d{}ms", x.days(), x.milliseconds())) + }); + display_fmt(iter, head, f, new_lines) + } +} + +macro_rules! display { + ($ty:ty) => { + impl std::fmt::Display for PrimitiveArray<$ty> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let head = &format!("{}", self.data_type()); + display_fmt(self.iter(), head, f, false) + } + } + }; +} + +display!(i8); +display!(i16); +display!(u8); +display!(u16); +display!(u32); +display!(u64); +display!(f32); +display!(f64); + +#[cfg(test)] +mod tests { + use super::super::Primitive; + use super::*; + + #[test] + fn display_int32() { + let array = Primitive::::from(&[Some(1), None, Some(2)]).to(DataType::Int32); + assert_eq!(format!("{}", array), "Int32[1, , 2]"); + } + + #[test] + fn display_date32() { + let array = Primitive::::from(&[Some(1), None, Some(2)]).to(DataType::Date32); + assert_eq!(format!("{}", array), "Date32[0001-01-01, , 0001-01-02]"); + } + + #[test] + fn display_time32s() { + let array = Primitive::::from(&[Some(1), None, Some(2)]) + .to(DataType::Time32(TimeUnit::Second)); + assert_eq!(format!("{}", array), "Time32(Second)[00:00:01, , 00:00:02]"); + } + + #[test] + fn display_time32ms() { + let array = Primitive::::from(&[Some(1), None, Some(2)]) + .to(DataType::Time32(TimeUnit::Millisecond)); + assert_eq!( + format!("{}", array), + "Time32(Millisecond)[00:00:00.001, , 00:00:00.002]" + ); + } + + #[test] + fn display_interval_d() { + let array = Primitive::::from(&[Some(1), None, Some(2)]) + .to(DataType::Interval(IntervalUnit::YearMonth)); + assert_eq!(format!("{}", array), "Interval(YearMonth)[1d, , 2d]"); + } + + #[test] + fn display_int64() { + let array = Primitive::::from(&[Some(1), None, Some(2)]).to(DataType::Int64); + assert_eq!(format!("{}", array), "Int64[1, , 2]"); + } + + #[test] + fn display_date64() { + let array = Primitive::::from(&[Some(1), None, Some(86400000)]).to(DataType::Date64); + assert_eq!(format!("{}", array), "Date64[1970-01-01, , 1970-01-02]"); + } + + #[test] + fn display_time64us() { + let array = Primitive::::from(&[Some(1), None, Some(2)]) + .to(DataType::Time64(TimeUnit::Microsecond)); + assert_eq!( + format!("{}", array), + "Time64(Microsecond)[00:00:00.000001, , 00:00:00.000002]" + ); + } + + #[test] + fn display_time64ns() { + let array = Primitive::::from(&[Some(1), None, Some(2)]) + .to(DataType::Time64(TimeUnit::Nanosecond)); + assert_eq!( + format!("{}", array), + "Time64(Nanosecond)[00:00:00.000000001, , 00:00:00.000000002]" + ); + } + + #[test] + fn display_timestamp_s() { + let array = Primitive::::from(&[Some(1), None, Some(2)]) + .to(DataType::Timestamp(TimeUnit::Second, None)); + assert_eq!( + format!("{}", array), + "Timestamp(Second, None)[1970-01-01 00:00:01, , 1970-01-01 00:00:02]" + ); + } + + #[test] + fn display_timestamp_us() { + let array = Primitive::::from(&[Some(1), None, Some(2)]) + .to(DataType::Timestamp(TimeUnit::Microsecond, None)); + assert_eq!( + format!("{}", array), + "Timestamp(Microsecond, None)[1970-01-01 00:00:00.000001, , 1970-01-01 00:00:00.000002]" + ); + } + + #[test] + fn display_duration_ms() { + let array = Primitive::::from(&[Some(1), None, Some(2)]) + .to(DataType::Duration(TimeUnit::Millisecond)); + assert_eq!(format!("{}", array), "Duration(Millisecond)[1ms, , 2ms]"); + } + + #[test] + fn display_decimal() { + let array = + Primitive::::from(&[Some(12345), None, Some(23456)]).to(DataType::Decimal(5, 2)); + assert_eq!(format!("{}", array), "Decimal(5, 2)[123.45, , 234.56]"); + } + + #[test] + fn display_decimal1() { + let array = + Primitive::::from(&[Some(12345), None, Some(23456)]).to(DataType::Decimal(5, 1)); + assert_eq!(format!("{}", array), "Decimal(5, 1)[1234.5, , 2345.6]"); + } + + #[test] + fn display_interval_days_ms() { + let array = + Primitive::::from(&[Some(days_ms::new(1, 1)), None, Some(days_ms::new(2, 2))]) + .to(DataType::Interval(IntervalUnit::DayTime)); + assert_eq!(format!("{}", array), "Interval(DayTime)[1d1ms, , 2d2ms]"); + } +} diff --git a/src/array/primitive/mod.rs b/src/array/primitive/mod.rs index 77cb8beaab7..4c64b948a6a 100644 --- a/src/array/primitive/mod.rs +++ b/src/array/primitive/mod.rs @@ -1,8 +1,6 @@ -use crate::{ - bitmap::Bitmap, buffer::Buffer, datatypes::DataType, error::ArrowError, types::NativeType, -}; +use crate::{bitmap::Bitmap, buffer::Buffer, datatypes::*, error::ArrowError, types::NativeType}; -use super::{display_fmt, Array}; +use super::Array; /// A [`PrimitiveArray`] is arrow's equivalent to `Vec>`, i.e. /// an array designed for highly performant operations on optionally nullable slots, @@ -118,13 +116,7 @@ impl Array for PrimitiveArray { } } -impl std::fmt::Display for PrimitiveArray { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // todo: match data_type and format dates - display_fmt(self.iter(), &format!("{}", self.data_type()), f, false) - } -} - +mod display; mod ffi; mod from; pub use from::Primitive; @@ -136,12 +128,6 @@ mod tests { use super::*; use std::iter::FromIterator; - #[test] - fn display() { - let array = Primitive::::from(&[Some(1), None, Some(2)]).to(DataType::Int32); - assert_eq!(format!("{}", array), "Int32[1, , 2]"); - } - #[test] fn basics() { let data = vec![Some(1), None, Some(10)]; diff --git a/src/temporal_conversions.rs b/src/temporal_conversions.rs index 82876e36e55..64075767d09 100644 --- a/src/temporal_conversions.rs +++ b/src/temporal_conversions.rs @@ -1,7 +1,7 @@ //! Conversion methods for dates and times. use crate::datatypes::TimeUnit; -use chrono::{NaiveDateTime, NaiveTime}; +use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; /// Number of seconds in a day pub const SECONDS_IN_DAY: i64 = 86_400; @@ -22,6 +22,12 @@ pub fn date32_to_datetime(v: i32) -> NaiveDateTime { NaiveDateTime::from_timestamp(v as i64 * SECONDS_IN_DAY, 0) } +/// converts a `i32` representing a `date32` to [`NaiveDate`] +#[inline] +pub fn date32_to_date(days: i32) -> NaiveDate { + NaiveDate::from_num_days_from_ce(days) +} + /// converts a `i64` representing a `date64` to [`NaiveDateTime`] #[inline] pub fn date64_to_datetime(v: i64) -> NaiveDateTime { @@ -33,6 +39,12 @@ pub fn date64_to_datetime(v: i64) -> NaiveDateTime { ) } +/// converts a `i64` representing a `date64` to [`NaiveDate`] +#[inline] +pub fn date64_to_date(milliseconds: i64) -> NaiveDate { + date64_to_datetime(milliseconds).date() +} + /// converts a `i32` representing a `time32(s)` to [`NaiveDateTime`] #[inline] pub fn time32s_to_time(v: i32) -> NaiveTime {