tustvold commented on code in PR #3647:
URL: https://github.com/apache/arrow-rs/pull/3647#discussion_r1093636823


##########
arrow-cast/src/display.rs:
##########
@@ -19,50 +19,301 @@
 //! purposes. See the `pretty` crate for additional functions for
 //! record batch pretty printing.
 
-use std::fmt::Write;
+use std::fmt::{Debug, Display, Formatter};
 use std::sync::Arc;
 
+use arrow_array::temporal_conversions::*;
 use arrow_array::timezone::Tz;
 use arrow_array::types::*;
 use arrow_array::*;
 use arrow_buffer::ArrowNativeType;
 use arrow_schema::*;
-use chrono::prelude::SecondsFormat;
+use chrono::{DateTime, NaiveDate, TimeZone, Utc};
 
-macro_rules! make_string {
-    ($array_type:ty, $column: ident, $row: ident) => {{
-        let array = $column.as_any().downcast_ref::<$array_type>().unwrap();
+/// Options for formatting arrays
+#[derive(Debug, Clone, Default)]
+pub struct FormatOptions {
+    safe: bool,
+}
 
-        Ok(array.value($row).to_string())
-    }};
+impl FormatOptions {
+    /// If set to `true` any formatting errors will be written to the output
+    /// instead of being converted into a [`std::fmt::Error`]
+    pub fn with_display_error(mut self, safe: bool) -> Self {
+        self.safe = safe;
+        self
+    }
 }
 
-macro_rules! make_string_interval_year_month {
-    ($column: ident, $row: ident) => {{
-        let array = $column
-            .as_any()
-            .downcast_ref::<array::IntervalYearMonthArray>()
-            .unwrap();
+/// Implements [`Display`] for a specific array value
+pub struct ValueFormatter<'a> {
+    idx: usize,
+    formatter: &'a ArrayFormatter<'a>,
+}
+
+impl<'a> Display for ValueFormatter<'a> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        match self.formatter.format.fmt(self.idx, f) {
+            Ok(()) => Ok(()),
+            Err(FormatError::Arrow(e)) if self.formatter.safe => {
+                write!(f, "ERROR: {}", e)
+            }
+            Err(_) => Err(std::fmt::Error),
+        }
+    }
+}
+
+/// A string formatter for an [`Array`]
+pub struct ArrayFormatter<'a> {
+    format: Box<dyn DisplayIndex + 'a>,
+    safe: bool,
+}
+
+impl<'a> ArrayFormatter<'a> {
+    /// Returns an [`ArrayFormatter`] that can be used to format `array`
+    ///
+    /// This returns an error if an array of the given data type cannot be 
formatted
+    pub fn try_new(
+        array: &'a dyn Array,
+        options: &FormatOptions,
+    ) -> Result<Self, ArrowError> {
+        let format = downcast_primitive_array! {
+            array => Box::new(ArrayFormat::try_new(array)?) as _,
+            _ => todo!()
+        };
+
+        Ok(Self {
+            format,
+            safe: options.safe,
+        })
+    }
+
+    /// Returns a [`ValueFormatter`] that implements [`Display`] for
+    /// the value of the array at `idx`
+    pub fn value(&self, idx: usize) -> ValueFormatter<'_> {
+        ValueFormatter {
+            formatter: self,
+            idx,
+        }
+    }
+}
+
+/// Either an [`ArrowError`] or [`std::fmt::Error`]
+enum FormatError {
+    Format(std::fmt::Error),
+    Arrow(ArrowError),
+}
+
+type FormatResult = Result<(), FormatError>;
+
+impl From<std::fmt::Error> for FormatError {
+    fn from(value: std::fmt::Error) -> Self {
+        Self::Format(value)
+    }
+}
+
+impl From<ArrowError> for FormatError {
+    fn from(value: ArrowError) -> Self {
+        Self::Arrow(value)
+    }
+}
+
+/// [`Display`] but accepting an index
+trait DisplayIndex {
+    fn fmt(&self, idx: usize, f: &mut Formatter<'_>) -> FormatResult;
+}
+
+/// [`DisplayIndex`] with additional state
+trait DisplayIndexState {
+    type State;
+
+    fn prepare(&self) -> Result<Self::State, ArrowError>;
+
+    fn fmt(&self, state: &Self::State, idx: usize, f: &mut Formatter<'_>)
+        -> FormatResult;
+}
+
+impl<T: DisplayIndex> DisplayIndexState for T {
+    type State = ();
+
+    fn prepare(&self) -> Result<Self::State, ArrowError> {
+        Ok(())
+    }
+
+    fn fmt(&self, _: &Self::State, idx: usize, f: &mut Formatter<'_>) -> 
FormatResult {
+        DisplayIndex::fmt(self, idx, f)
+    }
+}
+
+struct ArrayFormat<F: DisplayIndexState> {
+    state: F::State,
+    array: F,
+}
+
+impl<F: DisplayIndexState> ArrayFormat<F> {
+    fn try_new(array: F) -> Result<Self, ArrowError> {
+        let state = array.prepare()?;
+        Ok(Self { state, array })
+    }
+}
+
+impl<F: DisplayIndexState + Array> DisplayIndex for ArrayFormat<F> {
+    fn fmt(&self, idx: usize, f: &mut Formatter<'_>) -> FormatResult {
+        if self.array.is_null(idx) {
+            return Ok(());
+        }
+        DisplayIndexState::fmt(&self.array, &self.state, idx, f)
+    }
+}
+
+macro_rules! primitive_display {

Review Comment:
   Unfortunately the lack of trait specialization means we are forced to use 
macros here



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscr...@arrow.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to