Skip to main content

arrow_cast/
display.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Functions for printing array values as human-readable strings.
19//!
20//! This is often used for debugging or logging purposes.
21//!
22//! See the [`pretty`] crate for additional functions for
23//! record batch pretty printing.
24//!
25//! [`pretty`]: crate::pretty
26use std::fmt::{Debug, Display, Formatter, Write};
27use std::hash::{Hash, Hasher};
28use std::ops::Range;
29
30use arrow_array::cast::*;
31use arrow_array::temporal_conversions::*;
32use arrow_array::timezone::Tz;
33use arrow_array::types::*;
34use arrow_array::*;
35use arrow_buffer::ArrowNativeType;
36use arrow_schema::*;
37use chrono::format::{Item, StrftimeItems};
38use chrono::{NaiveDate, NaiveDateTime, SecondsFormat, TimeZone, Utc};
39use lexical_core::FormattedSize;
40
41type TimeFormat<'a> = Option<&'a str>;
42
43struct CompiledItems<'a>(Vec<Item<'a>>);
44
45enum CompiledTimeFormat<'a> {
46    Default,
47    Custom(Box<CompiledItems<'a>>),
48}
49
50impl<'a> CompiledTimeFormat<'a> {
51    fn new(format: TimeFormat<'a>) -> Self {
52        match format {
53            Some(format) => Self::Custom(Box::new(CompiledItems(
54                StrftimeItems::new(format).collect(),
55            ))),
56            None => Self::Default,
57        }
58    }
59}
60
61/// Format for displaying durations
62#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
63#[non_exhaustive]
64pub enum DurationFormat {
65    /// ISO 8601 - `P198DT72932.972880S`
66    ISO8601,
67    /// A human readable representation - `198 days 16 hours 34 mins 15.407810000 secs`
68    Pretty,
69}
70
71/// Options for formatting arrays
72///
73/// By default nulls are formatted as `""` and temporal types formatted
74/// according to RFC3339
75///
76/// # Equality
77///
78/// Most fields in [`FormatOptions`] are compared by value, except `formatter_factory`. As the trait
79/// does not require an [`Eq`] and [`Hash`] implementation, this struct only compares the pointer of
80/// the factories.
81#[derive(Debug, Clone)]
82pub struct FormatOptions<'a> {
83    /// If set to `true` any formatting errors will be written to the output
84    /// instead of being converted into a [`std::fmt::Error`]
85    safe: bool,
86    /// Format string for nulls
87    null: &'a str,
88    /// Date format for date arrays
89    date_format: TimeFormat<'a>,
90    /// Format for DateTime arrays
91    datetime_format: TimeFormat<'a>,
92    /// Timestamp format for timestamp arrays
93    timestamp_format: TimeFormat<'a>,
94    /// Timestamp format for timestamp with timezone arrays
95    timestamp_tz_format: TimeFormat<'a>,
96    /// Time format for time arrays
97    time_format: TimeFormat<'a>,
98    /// Duration format
99    duration_format: DurationFormat,
100    /// Show types in visual representation batches
101    types_info: bool,
102    /// Whether string values should be quoted
103    quoted_strings: bool,
104    /// Formatter factory used to instantiate custom [`ArrayFormatter`]s. This allows users to
105    /// provide custom formatters.
106    formatter_factory: Option<&'a dyn ArrayFormatterFactory>,
107}
108
109impl Default for FormatOptions<'_> {
110    fn default() -> Self {
111        Self::new()
112    }
113}
114
115impl PartialEq for FormatOptions<'_> {
116    fn eq(&self, other: &Self) -> bool {
117        self.safe == other.safe
118            && self.null == other.null
119            && self.date_format == other.date_format
120            && self.datetime_format == other.datetime_format
121            && self.timestamp_format == other.timestamp_format
122            && self.timestamp_tz_format == other.timestamp_tz_format
123            && self.time_format == other.time_format
124            && self.duration_format == other.duration_format
125            && self.types_info == other.types_info
126            && self.quoted_strings == other.quoted_strings
127            && match (self.formatter_factory, other.formatter_factory) {
128                (Some(f1), Some(f2)) => std::ptr::eq(f1, f2),
129                (None, None) => true,
130                _ => false,
131            }
132    }
133}
134
135impl Eq for FormatOptions<'_> {}
136
137impl Hash for FormatOptions<'_> {
138    fn hash<H: Hasher>(&self, state: &mut H) {
139        self.safe.hash(state);
140        self.null.hash(state);
141        self.date_format.hash(state);
142        self.datetime_format.hash(state);
143        self.timestamp_format.hash(state);
144        self.timestamp_tz_format.hash(state);
145        self.time_format.hash(state);
146        self.duration_format.hash(state);
147        self.types_info.hash(state);
148        self.quoted_strings.hash(state);
149        self.formatter_factory
150            .map(|f| f as *const dyn ArrayFormatterFactory)
151            .hash(state);
152    }
153}
154
155impl<'a> FormatOptions<'a> {
156    /// Creates a new set of format options
157    pub const fn new() -> Self {
158        Self {
159            safe: true,
160            null: "",
161            date_format: None,
162            datetime_format: None,
163            timestamp_format: None,
164            timestamp_tz_format: None,
165            time_format: None,
166            duration_format: DurationFormat::ISO8601,
167            types_info: false,
168            quoted_strings: false,
169            formatter_factory: None,
170        }
171    }
172
173    /// If set to `true` any formatting errors will be written to the output
174    /// instead of being converted into a [`std::fmt::Error`]
175    pub const fn with_display_error(mut self, safe: bool) -> Self {
176        self.safe = safe;
177        self
178    }
179
180    /// Overrides the string used to represent a null
181    ///
182    /// Defaults to `""`
183    pub const fn with_null(self, null: &'a str) -> Self {
184        Self { null, ..self }
185    }
186
187    /// Overrides the format used for [`DataType::Date32`] columns
188    pub const fn with_date_format(self, date_format: Option<&'a str>) -> Self {
189        Self {
190            date_format,
191            ..self
192        }
193    }
194
195    /// Overrides the format used for [`DataType::Date64`] columns
196    pub const fn with_datetime_format(self, datetime_format: Option<&'a str>) -> Self {
197        Self {
198            datetime_format,
199            ..self
200        }
201    }
202
203    /// Overrides the format used for [`DataType::Timestamp`] columns without a timezone
204    pub const fn with_timestamp_format(self, timestamp_format: Option<&'a str>) -> Self {
205        Self {
206            timestamp_format,
207            ..self
208        }
209    }
210
211    /// Overrides the format used for [`DataType::Timestamp`] columns with a timezone
212    pub const fn with_timestamp_tz_format(self, timestamp_tz_format: Option<&'a str>) -> Self {
213        Self {
214            timestamp_tz_format,
215            ..self
216        }
217    }
218
219    /// Overrides the format used for [`DataType::Time32`] and [`DataType::Time64`] columns
220    pub const fn with_time_format(self, time_format: Option<&'a str>) -> Self {
221        Self {
222            time_format,
223            ..self
224        }
225    }
226
227    /// Overrides the format used for duration columns
228    ///
229    /// Defaults to [`DurationFormat::ISO8601`]
230    pub const fn with_duration_format(self, duration_format: DurationFormat) -> Self {
231        Self {
232            duration_format,
233            ..self
234        }
235    }
236
237    /// Overrides if types should be shown
238    ///
239    /// Defaults to [`false`]
240    pub const fn with_types_info(self, types_info: bool) -> Self {
241        Self { types_info, ..self }
242    }
243
244    /// Sets whether string values should be quoted
245    ///
246    /// When `true`, strings are formatted using [`Debug`]-style with double quotes and escaping.
247    /// Defaults to `false`
248    pub const fn with_quoted_strings(self, quoted_strings: bool) -> Self {
249        Self {
250            quoted_strings,
251            ..self
252        }
253    }
254
255    /// Overrides the [`ArrayFormatterFactory`] used to instantiate custom [`ArrayFormatter`]s.
256    ///
257    /// Using [`None`] causes pretty-printers to use the default [`ArrayFormatter`]s.
258    pub const fn with_formatter_factory(
259        self,
260        formatter_factory: Option<&'a dyn ArrayFormatterFactory>,
261    ) -> Self {
262        Self {
263            formatter_factory,
264            ..self
265        }
266    }
267
268    /// Returns whether formatting errors should be written to the output instead of being converted
269    /// into a [`std::fmt::Error`].
270    pub const fn safe(&self) -> bool {
271        self.safe
272    }
273
274    /// Returns the string used for displaying nulls.
275    pub const fn null(&self) -> &'a str {
276        self.null
277    }
278
279    /// Returns the format used for [`DataType::Date32`] columns.
280    pub const fn date_format(&self) -> TimeFormat<'a> {
281        self.date_format
282    }
283
284    /// Returns the format used for [`DataType::Date64`] columns.
285    pub const fn datetime_format(&self) -> TimeFormat<'a> {
286        self.datetime_format
287    }
288
289    /// Returns the format used for [`DataType::Timestamp`] columns without a timezone.
290    pub const fn timestamp_format(&self) -> TimeFormat<'a> {
291        self.timestamp_format
292    }
293
294    /// Returns the format used for [`DataType::Timestamp`] columns with a timezone.
295    pub const fn timestamp_tz_format(&self) -> TimeFormat<'a> {
296        self.timestamp_tz_format
297    }
298
299    /// Returns the format used for [`DataType::Time32`] and [`DataType::Time64`] columns.
300    pub const fn time_format(&self) -> TimeFormat<'a> {
301        self.time_format
302    }
303
304    /// Returns the [`DurationFormat`] used for duration columns.
305    pub const fn duration_format(&self) -> DurationFormat {
306        self.duration_format
307    }
308
309    /// Returns true if type info should be included in a visual representation of batches.
310    pub const fn types_info(&self) -> bool {
311        self.types_info
312    }
313
314    /// Returns whether string values should be quoted.
315    pub const fn quoted_strings(&self) -> bool {
316        self.quoted_strings
317    }
318
319    /// Returns the [`ArrayFormatterFactory`] used to instantiate custom [`ArrayFormatter`]s.
320    pub const fn formatter_factory(&self) -> Option<&'a dyn ArrayFormatterFactory> {
321        self.formatter_factory
322    }
323}
324
325/// Allows creating a new [`ArrayFormatter`] for a given [`Array`] and an optional [`Field`].
326///
327/// # Example
328///
329/// The example below shows how to create a custom formatter for a custom type `my_money`. Note that
330/// this example requires the `prettyprint` feature.
331///
332/// ```rust
333/// # #[cfg(feature = "prettyprint")]{
334/// use std::fmt::Write;
335/// use arrow_array::{cast::AsArray, Array, Int32Array};
336/// use arrow_cast::display::{ArrayFormatter, ArrayFormatterFactory, DisplayIndex, FormatOptions, FormatResult};
337/// use arrow_cast::pretty::pretty_format_batches_with_options;
338/// use arrow_schema::{ArrowError, Field};
339///
340/// /// A custom formatter factory that can create a formatter for the special type `my_money`.
341/// ///
342/// /// This struct could have access to some kind of extension type registry that can lookup the
343/// /// correct formatter for an extension type on-demand.
344/// #[derive(Debug)]
345/// struct MyFormatters {}
346///
347/// impl ArrayFormatterFactory for MyFormatters {
348///     fn create_array_formatter<'formatter>(
349///         &self,
350///         array: &'formatter dyn Array,
351///         options: &FormatOptions<'formatter>,
352///         field: Option<&'formatter Field>,
353///     ) -> Result<Option<ArrayFormatter<'formatter>>, ArrowError> {
354///         // check if this is the money type
355///         if field
356///             .map(|f| f.extension_type_name() == Some("my_money"))
357///             .unwrap_or(false)
358///         {
359///             // We assume that my_money always is an Int32.
360///             let array = array.as_primitive();
361///             let display_index = Box::new(MyMoneyFormatter { array, options: options.clone() });
362///             return Ok(Some(ArrayFormatter::new(display_index, options.safe())));
363///         }
364///
365///         Ok(None) // None indicates that the default formatter should be used.
366///     }
367/// }
368///
369/// /// A formatter for the type `my_money` that wraps a specific array and has access to the
370/// /// formatting options.
371/// struct MyMoneyFormatter<'a> {
372///     array: &'a Int32Array,
373///     options: FormatOptions<'a>,
374/// }
375///
376/// impl<'a> DisplayIndex for MyMoneyFormatter<'a> {
377///     fn write(&self, idx: usize, f: &mut dyn Write) -> FormatResult {
378///         match self.array.is_valid(idx) {
379///             true => write!(f, "{} €", self.array.value(idx))?,
380///             false => write!(f, "{}", self.options.null())?,
381///         }
382///
383///         Ok(())
384///     }
385/// }
386///
387/// // Usually, here you would provide your record batches.
388/// let my_batches = vec![];
389///
390/// // Call the pretty printer with the custom formatter factory.
391/// pretty_format_batches_with_options(
392///        &my_batches,
393///        &FormatOptions::new().with_formatter_factory(Some(&MyFormatters {}))
394/// );
395/// # }
396/// ```
397pub trait ArrayFormatterFactory: Debug + Send + Sync {
398    /// Creates a new [`ArrayFormatter`] for the given [`Array`] and an optional [`Field`]. If the
399    /// default implementation should be used, return [`None`].
400    ///
401    /// The field shall be used to look up metadata about the `array` while `options` provide
402    /// information on formatting, for example, dates and times which should be considered by an
403    /// implementor.
404    fn create_array_formatter<'formatter>(
405        &self,
406        array: &'formatter dyn Array,
407        options: &FormatOptions<'formatter>,
408        field: Option<&'formatter Field>,
409    ) -> Result<Option<ArrayFormatter<'formatter>>, ArrowError>;
410}
411
412/// Used to create a new [`ArrayFormatter`] from the given `array`, while also checking whether
413/// there is an override available in the [`ArrayFormatterFactory`].
414pub(crate) fn make_array_formatter<'a>(
415    array: &'a dyn Array,
416    options: &FormatOptions<'a>,
417    field: Option<&'a Field>,
418) -> Result<ArrayFormatter<'a>, ArrowError> {
419    match options.formatter_factory() {
420        None => ArrayFormatter::try_new(array, options),
421        Some(formatters) => formatters
422            .create_array_formatter(array, options, field)
423            .transpose()
424            .unwrap_or_else(|| ArrayFormatter::try_new(array, options)),
425    }
426}
427
428/// Implements [`Display`] for a specific array value
429pub struct ValueFormatter<'a> {
430    idx: usize,
431    formatter: &'a ArrayFormatter<'a>,
432}
433
434impl ValueFormatter<'_> {
435    /// Writes this value to the provided [`Write`]
436    ///
437    /// Note: this ignores [`FormatOptions::with_display_error`] and
438    /// will return an error on formatting issue
439    pub fn write(&self, s: &mut dyn Write) -> Result<(), ArrowError> {
440        match self.formatter.format.write(self.idx, s) {
441            Ok(()) => Ok(()),
442            Err(FormatError::Arrow(e)) => Err(e),
443            Err(FormatError::Format(_)) => Err(ArrowError::CastError("Format error".to_string())),
444        }
445    }
446
447    /// Fallibly converts this to a string
448    pub fn try_to_string(&self) -> Result<String, ArrowError> {
449        let mut s = String::new();
450        self.write(&mut s)?;
451        Ok(s)
452    }
453}
454
455impl Display for ValueFormatter<'_> {
456    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
457        match self.formatter.format.write(self.idx, f) {
458            Ok(()) => Ok(()),
459            Err(FormatError::Arrow(e)) if self.formatter.safe => {
460                write!(f, "ERROR: {e}")
461            }
462            Err(_) => Err(std::fmt::Error),
463        }
464    }
465}
466
467/// A string formatter for an [`Array`]
468///
469/// This can be used with [`std::write`] to write type-erased `dyn Array`
470///
471/// ```
472/// # use std::fmt::{Display, Formatter, Write};
473/// # use arrow_array::{Array, ArrayRef, Int32Array};
474/// # use arrow_cast::display::{ArrayFormatter, FormatOptions};
475/// # use arrow_schema::ArrowError;
476/// struct MyContainer {
477///     values: ArrayRef,
478/// }
479///
480/// impl Display for MyContainer {
481///     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
482///         let options = FormatOptions::default();
483///         let formatter = ArrayFormatter::try_new(self.values.as_ref(), &options)
484///             .map_err(|_| std::fmt::Error)?;
485///
486///         let mut iter = 0..self.values.len();
487///         if let Some(idx) = iter.next() {
488///             write!(f, "{}", formatter.value(idx))?;
489///         }
490///         for idx in iter {
491///             write!(f, ", {}", formatter.value(idx))?;
492///         }
493///         Ok(())
494///     }
495/// }
496/// ```
497///
498/// [`ValueFormatter::write`] can also be used to get a semantic error, instead of the
499/// opaque [`std::fmt::Error`]
500///
501/// ```
502/// # use std::fmt::Write;
503/// # use arrow_array::Array;
504/// # use arrow_cast::display::{ArrayFormatter, FormatOptions};
505/// # use arrow_schema::ArrowError;
506/// fn format_array(
507///     f: &mut dyn Write,
508///     array: &dyn Array,
509///     options: &FormatOptions,
510/// ) -> Result<(), ArrowError> {
511///     let formatter = ArrayFormatter::try_new(array, options)?;
512///     for i in 0..array.len() {
513///         formatter.value(i).write(f)?
514///     }
515///     Ok(())
516/// }
517/// ```
518///
519pub struct ArrayFormatter<'a> {
520    format: Box<dyn DisplayIndex + 'a>,
521    safe: bool,
522}
523
524impl<'a> ArrayFormatter<'a> {
525    /// Returns an [`ArrayFormatter`] using the provided formatter.
526    pub fn new(format: Box<dyn DisplayIndex + 'a>, safe: bool) -> Self {
527        Self { format, safe }
528    }
529
530    /// Returns an [`ArrayFormatter`] that can be used to format `array`
531    ///
532    /// This returns an error if an array of the given data type cannot be formatted
533    pub fn try_new(array: &'a dyn Array, options: &FormatOptions<'a>) -> Result<Self, ArrowError> {
534        Ok(Self::new(
535            make_default_display_index(array, options)?,
536            options.safe,
537        ))
538    }
539
540    /// Returns a [`ValueFormatter`] that implements [`Display`] for
541    /// the value of the array at `idx`
542    pub fn value(&self, idx: usize) -> ValueFormatter<'_> {
543        ValueFormatter {
544            formatter: self,
545            idx,
546        }
547    }
548}
549
550fn make_default_display_index<'a>(
551    array: &'a dyn Array,
552    options: &FormatOptions<'a>,
553) -> Result<Box<dyn DisplayIndex + 'a>, ArrowError> {
554    downcast_primitive_array! {
555        array => array_format(array, options),
556        DataType::Null => array_format(as_null_array(array), options),
557        DataType::Boolean => array_format(as_boolean_array(array), options),
558        DataType::Utf8 => array_format(array.as_string::<i32>(), options),
559        DataType::LargeUtf8 => array_format(array.as_string::<i64>(), options),
560        DataType::Utf8View => array_format(array.as_string_view(), options),
561        DataType::Binary => array_format(array.as_binary::<i32>(), options),
562        DataType::BinaryView => array_format(array.as_binary_view(), options),
563        DataType::LargeBinary => array_format(array.as_binary::<i64>(), options),
564        DataType::FixedSizeBinary(_) => {
565            let a = array.as_any().downcast_ref::<FixedSizeBinaryArray>().unwrap();
566            array_format(a, options)
567        }
568        DataType::Dictionary(_, _) => downcast_dictionary_array! {
569            array => array_format(array, options),
570            _ => unreachable!()
571        }
572        DataType::List(_) => array_format(as_generic_list_array::<i32>(array), options),
573        DataType::LargeList(_) => array_format(as_generic_list_array::<i64>(array), options),
574        DataType::ListView(_) => array_format(array.as_list_view::<i32>(), options),
575        DataType::LargeListView(_) => array_format(array.as_list_view::<i64>(), options),
576        DataType::FixedSizeList(_, _) => {
577            let a = array.as_any().downcast_ref::<FixedSizeListArray>().unwrap();
578            array_format(a, options)
579        }
580        DataType::Struct(_) => array_format(as_struct_array(array), options),
581        DataType::Map(_, _) => array_format(as_map_array(array), options),
582        DataType::Union(_, _) => array_format(as_union_array(array), options),
583        DataType::RunEndEncoded(_, _) => downcast_run_array! {
584            array => array_format(array, options),
585            _ => unreachable!()
586        },
587        d => Err(ArrowError::NotYetImplemented(format!("formatting {d} is not yet supported"))),
588    }
589}
590
591/// Either an [`ArrowError`] or [`std::fmt::Error`]
592pub enum FormatError {
593    /// An error occurred while formatting the array
594    Format(std::fmt::Error),
595    /// An Arrow error occurred while formatting the array.
596    Arrow(ArrowError),
597}
598
599/// The result of formatting an array element via [`DisplayIndex::write`].
600pub type FormatResult = Result<(), FormatError>;
601
602impl From<std::fmt::Error> for FormatError {
603    fn from(value: std::fmt::Error) -> Self {
604        Self::Format(value)
605    }
606}
607
608impl From<ArrowError> for FormatError {
609    fn from(value: ArrowError) -> Self {
610        Self::Arrow(value)
611    }
612}
613
614/// [`Display`] but accepting an index
615pub trait DisplayIndex {
616    /// Write the value of the underlying array at `idx` to `f`.
617    fn write(&self, idx: usize, f: &mut dyn Write) -> FormatResult;
618}
619
620/// [`DisplayIndex`] with additional state
621trait DisplayIndexState<'a> {
622    type State;
623
624    fn prepare(&self, options: &FormatOptions<'a>) -> Result<Self::State, ArrowError>;
625
626    fn write(&self, state: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult;
627}
628
629impl<'a, T: DisplayIndex> DisplayIndexState<'a> for T {
630    type State = ();
631
632    fn prepare(&self, _options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
633        Ok(())
634    }
635
636    fn write(&self, (): &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult {
637        DisplayIndex::write(self, idx, f)
638    }
639}
640
641struct ArrayFormat<'a, F: DisplayIndexState<'a>> {
642    state: F::State,
643    array: F,
644    null: &'a str,
645}
646
647fn array_format<'a, F>(
648    array: F,
649    options: &FormatOptions<'a>,
650) -> Result<Box<dyn DisplayIndex + 'a>, ArrowError>
651where
652    F: DisplayIndexState<'a> + Array + 'a,
653{
654    let state = array.prepare(options)?;
655    Ok(Box::new(ArrayFormat {
656        state,
657        array,
658        null: options.null,
659    }))
660}
661
662impl<'a, F: DisplayIndexState<'a> + Array> DisplayIndex for ArrayFormat<'a, F> {
663    fn write(&self, idx: usize, f: &mut dyn Write) -> FormatResult {
664        if self.array.is_null(idx) {
665            if !self.null.is_empty() {
666                f.write_str(self.null)?
667            }
668            return Ok(());
669        }
670        DisplayIndexState::write(&self.array, &self.state, idx, f)
671    }
672}
673
674impl DisplayIndex for &BooleanArray {
675    fn write(&self, idx: usize, f: &mut dyn Write) -> FormatResult {
676        write!(f, "{}", self.value(idx))?;
677        Ok(())
678    }
679}
680
681impl<'a> DisplayIndexState<'a> for &'a NullArray {
682    type State = &'a str;
683
684    fn prepare(&self, options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
685        Ok(options.null)
686    }
687
688    fn write(&self, state: &Self::State, _idx: usize, f: &mut dyn Write) -> FormatResult {
689        f.write_str(state)?;
690        Ok(())
691    }
692}
693
694macro_rules! primitive_display {
695    ($($t:ty),+) => {
696        $(impl<'a> DisplayIndex for &'a PrimitiveArray<$t>
697        {
698            fn write(&self, idx: usize, f: &mut dyn Write) -> FormatResult {
699                let value = self.value(idx);
700                let mut buffer = [0u8; <$t as ArrowPrimitiveType>::Native::FORMATTED_SIZE];
701                let b = lexical_core::write(value, &mut buffer);
702                // Lexical core produces valid UTF-8
703                let s = unsafe { std::str::from_utf8_unchecked(b) };
704                f.write_str(s)?;
705                Ok(())
706            }
707        })+
708    };
709}
710
711macro_rules! primitive_display_float {
712    ($($t:ty),+) => {
713        $(impl<'a> DisplayIndex for &'a PrimitiveArray<$t>
714        {
715            fn write(&self, idx: usize, f: &mut dyn Write) -> FormatResult {
716                let value = self.value(idx);
717                let mut buffer = ryu::Buffer::new();
718                f.write_str(buffer.format(value))?;
719                Ok(())
720            }
721        })+
722    };
723}
724
725primitive_display!(Int8Type, Int16Type, Int32Type, Int64Type);
726primitive_display!(UInt8Type, UInt16Type, UInt32Type, UInt64Type);
727primitive_display_float!(Float32Type, Float64Type);
728
729impl DisplayIndex for &PrimitiveArray<Float16Type> {
730    fn write(&self, idx: usize, f: &mut dyn Write) -> FormatResult {
731        write!(f, "{}", self.value(idx))?;
732        Ok(())
733    }
734}
735
736macro_rules! decimal_display {
737    ($($t:ty),+) => {
738        $(impl<'a> DisplayIndexState<'a> for &'a PrimitiveArray<$t> {
739            type State = (u8, i8);
740
741            fn prepare(&self, _options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
742                Ok((self.precision(), self.scale()))
743            }
744
745            fn write(&self, s: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult {
746                write!(f, "{}", <$t>::format_decimal(self.values()[idx], s.0, s.1))?;
747                Ok(())
748            }
749        })+
750    };
751}
752
753decimal_display!(Decimal32Type, Decimal64Type, Decimal128Type, Decimal256Type);
754
755fn write_timestamp(
756    f: &mut dyn Write,
757    naive: NaiveDateTime,
758    timezone: Option<Tz>,
759    format: &CompiledTimeFormat<'_>,
760) -> FormatResult {
761    match timezone {
762        Some(tz) => {
763            let date = Utc.from_utc_datetime(&naive).with_timezone(&tz);
764            match format {
765                CompiledTimeFormat::Custom(items) => {
766                    write!(f, "{}", date.format_with_items(items.0.iter()))?
767                }
768                CompiledTimeFormat::Default => {
769                    write!(f, "{}", date.to_rfc3339_opts(SecondsFormat::AutoSi, true))?
770                }
771            }
772        }
773        None => match format {
774            CompiledTimeFormat::Custom(items) => {
775                write!(f, "{}", naive.format_with_items(items.0.iter()))?
776            }
777            CompiledTimeFormat::Default => write!(f, "{naive:?}")?,
778        },
779    }
780    Ok(())
781}
782
783macro_rules! timestamp_display {
784    ($($t:ty),+) => {
785        $(impl<'a> DisplayIndexState<'a> for &'a PrimitiveArray<$t> {
786            type State = (Option<Tz>, CompiledTimeFormat<'a>);
787
788            fn prepare(&self, options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
789                match self.data_type() {
790                    DataType::Timestamp(_, Some(tz)) => Ok((Some(tz.parse()?), CompiledTimeFormat::new(options.timestamp_tz_format))),
791                    DataType::Timestamp(_, None) => Ok((None, CompiledTimeFormat::new(options.timestamp_format))),
792                    _ => unreachable!(),
793                }
794            }
795
796            fn write(&self, s: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult {
797                let value = self.value(idx);
798                let naive = as_datetime::<$t>(value).ok_or_else(|| {
799                    ArrowError::CastError(format!(
800                        "Failed to convert {} to datetime for {}",
801                        value,
802                        self.data_type()
803                    ))
804                })?;
805
806                write_timestamp(f, naive, s.0, &s.1)
807            }
808        })+
809    };
810}
811
812timestamp_display!(
813    TimestampSecondType,
814    TimestampMillisecondType,
815    TimestampMicrosecondType,
816    TimestampNanosecondType
817);
818
819macro_rules! temporal_display {
820    ($convert:ident, $format:ident, $t:ty) => {
821        impl<'a> DisplayIndexState<'a> for &'a PrimitiveArray<$t> {
822            type State = CompiledTimeFormat<'a>;
823
824            fn prepare(&self, options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
825                Ok(CompiledTimeFormat::new(options.$format))
826            }
827
828            fn write(&self, fmt: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult {
829                let value = self.value(idx);
830                let naive = $convert(value).ok_or_else(|| {
831                    ArrowError::CastError(format!(
832                        "Failed to convert {} to temporal for {}",
833                        value,
834                        self.data_type()
835                    ))
836                })?;
837
838                match fmt {
839                    CompiledTimeFormat::Custom(items) => {
840                        write!(f, "{}", naive.format_with_items(items.0.iter()))?
841                    }
842                    CompiledTimeFormat::Default => write!(f, "{naive:?}")?,
843                }
844                Ok(())
845            }
846        }
847    };
848}
849
850#[inline]
851fn date32_to_date(value: i32) -> Option<NaiveDate> {
852    Some(date32_to_datetime(value)?.date())
853}
854
855temporal_display!(date32_to_date, date_format, Date32Type);
856temporal_display!(date64_to_datetime, datetime_format, Date64Type);
857temporal_display!(time32s_to_time, time_format, Time32SecondType);
858temporal_display!(time32ms_to_time, time_format, Time32MillisecondType);
859temporal_display!(time64us_to_time, time_format, Time64MicrosecondType);
860temporal_display!(time64ns_to_time, time_format, Time64NanosecondType);
861
862/// Derive [`DisplayIndexState`] for `PrimitiveArray<$t>`
863///
864/// Arguments
865/// * `$convert` - function to convert the value to an `Duration`
866/// * `$t` - [`ArrowPrimitiveType`] of the array
867/// * `$scale` - scale of the duration (passed to `duration_fmt`)
868macro_rules! duration_display {
869    ($convert:ident, $t:ty, $scale:tt) => {
870        impl<'a> DisplayIndexState<'a> for &'a PrimitiveArray<$t> {
871            type State = DurationFormat;
872
873            fn prepare(&self, options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
874                Ok(options.duration_format)
875            }
876
877            fn write(&self, fmt: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult {
878                let v = self.value(idx);
879                match fmt {
880                    DurationFormat::ISO8601 => write!(f, "{}", $convert(v))?,
881                    DurationFormat::Pretty => duration_fmt!(f, v, $scale)?,
882                }
883                Ok(())
884            }
885        }
886    };
887}
888
889/// Similar to [`duration_display`] but `$convert` returns an `Option`
890macro_rules! duration_option_display {
891    ($convert:ident, $t:ty, $scale:tt) => {
892        impl<'a> DisplayIndexState<'a> for &'a PrimitiveArray<$t> {
893            type State = DurationFormat;
894
895            fn prepare(&self, options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
896                Ok(options.duration_format)
897            }
898
899            fn write(&self, fmt: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult {
900                let v = self.value(idx);
901                match fmt {
902                    DurationFormat::ISO8601 => match $convert(v) {
903                        Some(td) => write!(f, "{}", td)?,
904                        None => write!(f, "<invalid>")?,
905                    },
906                    DurationFormat::Pretty => match $convert(v) {
907                        Some(_) => duration_fmt!(f, v, $scale)?,
908                        None => write!(f, "<invalid>")?,
909                    },
910                }
911                Ok(())
912            }
913        }
914    };
915}
916
917macro_rules! duration_fmt {
918    ($f:ident, $v:expr, 0) => {{
919        let secs = $v;
920        let mins = secs / 60;
921        let hours = mins / 60;
922        let days = hours / 24;
923
924        let secs = secs - (mins * 60);
925        let mins = mins - (hours * 60);
926        let hours = hours - (days * 24);
927        write!($f, "{days} days {hours} hours {mins} mins {secs} secs")
928    }};
929    ($f:ident, $v:expr, $scale:tt) => {{
930        let subsec = $v;
931        let secs = subsec / 10_i64.pow($scale);
932        let mins = secs / 60;
933        let hours = mins / 60;
934        let days = hours / 24;
935
936        let subsec = subsec - (secs * 10_i64.pow($scale));
937        let secs = secs - (mins * 60);
938        let mins = mins - (hours * 60);
939        let hours = hours - (days * 24);
940        match subsec.is_negative() {
941            true => {
942                write!(
943                    $f,
944                    concat!("{} days {} hours {} mins -{}.{:0", $scale, "} secs"),
945                    days,
946                    hours,
947                    mins,
948                    secs.abs(),
949                    subsec.abs()
950                )
951            }
952            false => {
953                write!(
954                    $f,
955                    concat!("{} days {} hours {} mins {}.{:0", $scale, "} secs"),
956                    days, hours, mins, secs, subsec
957                )
958            }
959        }
960    }};
961}
962
963duration_option_display!(try_duration_s_to_duration, DurationSecondType, 0);
964duration_option_display!(try_duration_ms_to_duration, DurationMillisecondType, 3);
965duration_display!(duration_us_to_duration, DurationMicrosecondType, 6);
966duration_display!(duration_ns_to_duration, DurationNanosecondType, 9);
967
968impl DisplayIndex for &PrimitiveArray<IntervalYearMonthType> {
969    fn write(&self, idx: usize, f: &mut dyn Write) -> FormatResult {
970        let interval = self.value(idx) as f64;
971        let years = (interval / 12_f64).floor();
972        let month = interval - (years * 12_f64);
973
974        write!(f, "{years} years {month} mons",)?;
975        Ok(())
976    }
977}
978
979impl DisplayIndex for &PrimitiveArray<IntervalDayTimeType> {
980    fn write(&self, idx: usize, f: &mut dyn Write) -> FormatResult {
981        let value = self.value(idx);
982
983        if value.is_zero() {
984            write!(f, "0 secs")?;
985            return Ok(());
986        }
987
988        let mut prefix = "";
989
990        if value.days != 0 {
991            write!(f, "{prefix}{} days", value.days)?;
992            prefix = " ";
993        }
994
995        if value.milliseconds != 0 {
996            let millis_fmt = MillisecondsFormatter {
997                milliseconds: value.milliseconds,
998                prefix,
999            };
1000
1001            f.write_fmt(format_args!("{millis_fmt}"))?;
1002        }
1003
1004        Ok(())
1005    }
1006}
1007
1008impl DisplayIndex for &PrimitiveArray<IntervalMonthDayNanoType> {
1009    fn write(&self, idx: usize, f: &mut dyn Write) -> FormatResult {
1010        let value = self.value(idx);
1011
1012        if value.is_zero() {
1013            write!(f, "0 secs")?;
1014            return Ok(());
1015        }
1016
1017        let mut prefix = "";
1018
1019        if value.months != 0 {
1020            write!(f, "{prefix}{} mons", value.months)?;
1021            prefix = " ";
1022        }
1023
1024        if value.days != 0 {
1025            write!(f, "{prefix}{} days", value.days)?;
1026            prefix = " ";
1027        }
1028
1029        if value.nanoseconds != 0 {
1030            let nano_fmt = NanosecondsFormatter {
1031                nanoseconds: value.nanoseconds,
1032                prefix,
1033            };
1034            f.write_fmt(format_args!("{nano_fmt}"))?;
1035        }
1036
1037        Ok(())
1038    }
1039}
1040
1041struct NanosecondsFormatter<'a> {
1042    nanoseconds: i64,
1043    prefix: &'a str,
1044}
1045
1046impl Display for NanosecondsFormatter<'_> {
1047    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1048        let mut prefix = self.prefix;
1049
1050        let secs = self.nanoseconds / 1_000_000_000;
1051        let mins = secs / 60;
1052        let hours = mins / 60;
1053
1054        let secs = secs - (mins * 60);
1055        let mins = mins - (hours * 60);
1056
1057        let nanoseconds = self.nanoseconds % 1_000_000_000;
1058
1059        if hours != 0 {
1060            write!(f, "{prefix}{hours} hours")?;
1061            prefix = " ";
1062        }
1063
1064        if mins != 0 {
1065            write!(f, "{prefix}{mins} mins")?;
1066            prefix = " ";
1067        }
1068
1069        if secs != 0 || nanoseconds != 0 {
1070            let secs_sign = if secs < 0 || nanoseconds < 0 { "-" } else { "" };
1071            write!(
1072                f,
1073                "{prefix}{}{}.{:09} secs",
1074                secs_sign,
1075                secs.abs(),
1076                nanoseconds.abs()
1077            )?;
1078        }
1079
1080        Ok(())
1081    }
1082}
1083
1084struct MillisecondsFormatter<'a> {
1085    milliseconds: i32,
1086    prefix: &'a str,
1087}
1088
1089impl Display for MillisecondsFormatter<'_> {
1090    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1091        let mut prefix = self.prefix;
1092
1093        let secs = self.milliseconds / 1_000;
1094        let mins = secs / 60;
1095        let hours = mins / 60;
1096
1097        let secs = secs - (mins * 60);
1098        let mins = mins - (hours * 60);
1099
1100        let milliseconds = self.milliseconds % 1_000;
1101
1102        if hours != 0 {
1103            write!(f, "{prefix}{hours} hours")?;
1104            prefix = " ";
1105        }
1106
1107        if mins != 0 {
1108            write!(f, "{prefix}{mins} mins")?;
1109            prefix = " ";
1110        }
1111
1112        if secs != 0 || milliseconds != 0 {
1113            let secs_sign = if secs < 0 || milliseconds < 0 {
1114                "-"
1115            } else {
1116                ""
1117            };
1118
1119            write!(
1120                f,
1121                "{prefix}{}{}.{:03} secs",
1122                secs_sign,
1123                secs.abs(),
1124                milliseconds.abs()
1125            )?;
1126        }
1127
1128        Ok(())
1129    }
1130}
1131
1132impl<'a, O: OffsetSizeTrait> DisplayIndexState<'a> for &'a GenericStringArray<O> {
1133    type State = bool;
1134
1135    fn prepare(&self, options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
1136        Ok(options.quoted_strings())
1137    }
1138
1139    fn write(&self, state: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult {
1140        let value = self.value(idx);
1141        if *state {
1142            write!(f, "{:?}", value)?;
1143        } else {
1144            write!(f, "{}", value)?;
1145        }
1146        Ok(())
1147    }
1148}
1149
1150impl<'a> DisplayIndexState<'a> for &'a StringViewArray {
1151    type State = bool;
1152
1153    fn prepare(&self, options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
1154        Ok(options.quoted_strings())
1155    }
1156
1157    fn write(&self, state: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult {
1158        let value = self.value(idx);
1159        if *state {
1160            write!(f, "{:?}", value)?;
1161        } else {
1162            write!(f, "{}", value)?;
1163        }
1164        Ok(())
1165    }
1166}
1167
1168impl<O: OffsetSizeTrait> DisplayIndex for &GenericBinaryArray<O> {
1169    fn write(&self, idx: usize, f: &mut dyn Write) -> FormatResult {
1170        let v = self.value(idx);
1171        for byte in v {
1172            write!(f, "{byte:02x}")?;
1173        }
1174        Ok(())
1175    }
1176}
1177
1178impl DisplayIndex for &BinaryViewArray {
1179    fn write(&self, idx: usize, f: &mut dyn Write) -> FormatResult {
1180        let v = self.value(idx);
1181        for byte in v {
1182            write!(f, "{byte:02x}")?;
1183        }
1184        Ok(())
1185    }
1186}
1187
1188impl DisplayIndex for &FixedSizeBinaryArray {
1189    fn write(&self, idx: usize, f: &mut dyn Write) -> FormatResult {
1190        let v = self.value(idx);
1191        for byte in v {
1192            write!(f, "{byte:02x}")?;
1193        }
1194        Ok(())
1195    }
1196}
1197
1198impl<'a, K: ArrowDictionaryKeyType> DisplayIndexState<'a> for &'a DictionaryArray<K> {
1199    type State = Box<dyn DisplayIndex + 'a>;
1200
1201    fn prepare(&self, options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
1202        make_default_display_index(self.values().as_ref(), options)
1203    }
1204
1205    fn write(&self, s: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult {
1206        let value_idx = self.keys().values()[idx].as_usize();
1207        s.as_ref().write(value_idx, f)
1208    }
1209}
1210
1211impl<'a, K: RunEndIndexType> DisplayIndexState<'a> for &'a RunArray<K> {
1212    type State = ArrayFormatter<'a>;
1213
1214    fn prepare(&self, options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
1215        let field = match (*self).data_type() {
1216            DataType::RunEndEncoded(_, values_field) => values_field,
1217            _ => unreachable!(),
1218        };
1219        make_array_formatter(self.values().as_ref(), options, Some(field))
1220    }
1221
1222    fn write(&self, s: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult {
1223        let value_idx = self.get_physical_index(idx);
1224        write!(f, "{}", s.value(value_idx))?;
1225        Ok(())
1226    }
1227}
1228
1229fn write_list(
1230    f: &mut dyn Write,
1231    mut range: Range<usize>,
1232    values: &ArrayFormatter<'_>,
1233) -> FormatResult {
1234    f.write_char('[')?;
1235    if let Some(idx) = range.next() {
1236        write!(f, "{}", values.value(idx))?;
1237    }
1238    for idx in range {
1239        write!(f, ", {}", values.value(idx))?;
1240    }
1241    f.write_char(']')?;
1242    Ok(())
1243}
1244
1245impl<'a, O: OffsetSizeTrait> DisplayIndexState<'a> for &'a GenericListArray<O> {
1246    type State = ArrayFormatter<'a>;
1247
1248    fn prepare(&self, options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
1249        let field = match (*self).data_type() {
1250            DataType::List(f) => f,
1251            DataType::LargeList(f) => f,
1252            _ => unreachable!(),
1253        };
1254        make_array_formatter(self.values().as_ref(), options, Some(field.as_ref()))
1255    }
1256
1257    fn write(&self, s: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult {
1258        let offsets = self.value_offsets();
1259        let end = offsets[idx + 1].as_usize();
1260        let start = offsets[idx].as_usize();
1261        write_list(f, start..end, s)
1262    }
1263}
1264
1265impl<'a, O: OffsetSizeTrait> DisplayIndexState<'a> for &'a GenericListViewArray<O> {
1266    type State = ArrayFormatter<'a>;
1267
1268    fn prepare(&self, options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
1269        let field = match (*self).data_type() {
1270            DataType::ListView(f) => f,
1271            DataType::LargeListView(f) => f,
1272            _ => unreachable!(),
1273        };
1274        make_array_formatter(self.values().as_ref(), options, Some(field.as_ref()))
1275    }
1276
1277    fn write(&self, s: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult {
1278        let offsets = self.value_offsets();
1279        let sizes = self.value_sizes();
1280        let start = offsets[idx].as_usize();
1281        let end = start + sizes[idx].as_usize();
1282        write_list(f, start..end, s)
1283    }
1284}
1285
1286impl<'a> DisplayIndexState<'a> for &'a FixedSizeListArray {
1287    type State = (usize, ArrayFormatter<'a>);
1288
1289    fn prepare(&self, options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
1290        let field = match (*self).data_type() {
1291            DataType::FixedSizeList(f, _) => f,
1292            _ => unreachable!(),
1293        };
1294        let formatter =
1295            make_array_formatter(self.values().as_ref(), options, Some(field.as_ref()))?;
1296        let length = self.value_length();
1297        Ok((length as usize, formatter))
1298    }
1299
1300    fn write(&self, s: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult {
1301        let start = idx * s.0;
1302        let end = start + s.0;
1303        write_list(f, start..end, &s.1)
1304    }
1305}
1306
1307/// Pairs an [`ArrayFormatter`] with its field name
1308type FieldDisplay<'a> = (&'a str, ArrayFormatter<'a>);
1309
1310impl<'a> DisplayIndexState<'a> for &'a StructArray {
1311    type State = Vec<FieldDisplay<'a>>;
1312
1313    fn prepare(&self, options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
1314        let fields = match (*self).data_type() {
1315            DataType::Struct(f) => f,
1316            _ => unreachable!(),
1317        };
1318
1319        self.columns()
1320            .iter()
1321            .zip(fields)
1322            .map(|(a, f)| {
1323                let format = make_array_formatter(a.as_ref(), options, Some(f))?;
1324                Ok((f.name().as_str(), format))
1325            })
1326            .collect()
1327    }
1328
1329    fn write(&self, s: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult {
1330        let mut iter = s.iter();
1331        f.write_char('{')?;
1332        if let Some((name, display)) = iter.next() {
1333            write!(f, "{name}: {}", display.value(idx))?;
1334        }
1335        for (name, display) in iter {
1336            write!(f, ", {name}: {}", display.value(idx))?;
1337        }
1338        f.write_char('}')?;
1339        Ok(())
1340    }
1341}
1342
1343impl<'a> DisplayIndexState<'a> for &'a MapArray {
1344    type State = (ArrayFormatter<'a>, ArrayFormatter<'a>);
1345
1346    fn prepare(&self, options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
1347        let (key_field, value_field) = (*self).entries_fields();
1348
1349        let keys = make_array_formatter(self.keys().as_ref(), options, Some(key_field))?;
1350        let values = make_array_formatter(self.values().as_ref(), options, Some(value_field))?;
1351        Ok((keys, values))
1352    }
1353
1354    fn write(&self, s: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult {
1355        let offsets = self.value_offsets();
1356        let end = offsets[idx + 1].as_usize();
1357        let start = offsets[idx].as_usize();
1358        let mut iter = start..end;
1359
1360        f.write_char('{')?;
1361        if let Some(idx) = iter.next() {
1362            write!(f, "{}: {}", s.0.value(idx), s.1.value(idx))?;
1363        }
1364
1365        for idx in iter {
1366            write!(f, ", {}", s.0.value(idx))?;
1367            write!(f, ": {}", s.1.value(idx))?;
1368        }
1369
1370        f.write_char('}')?;
1371        Ok(())
1372    }
1373}
1374
1375impl<'a> DisplayIndexState<'a> for &'a UnionArray {
1376    type State = (Vec<Option<FieldDisplay<'a>>>, UnionMode);
1377
1378    fn prepare(&self, options: &FormatOptions<'a>) -> Result<Self::State, ArrowError> {
1379        let (fields, mode) = match (*self).data_type() {
1380            DataType::Union(fields, mode) => (fields, mode),
1381            _ => unreachable!(),
1382        };
1383
1384        let max_id = fields.iter().map(|(id, _)| id).max().unwrap_or_default() as usize;
1385        let mut out: Vec<Option<FieldDisplay>> = (0..max_id + 1).map(|_| None).collect();
1386        for (i, field) in fields.iter() {
1387            let formatter = make_array_formatter(self.child(i).as_ref(), options, Some(field))?;
1388            out[i as usize] = Some((field.name().as_str(), formatter))
1389        }
1390        Ok((out, *mode))
1391    }
1392
1393    fn write(&self, s: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult {
1394        let id = self.type_id(idx);
1395        let idx = match s.1 {
1396            UnionMode::Dense => self.value_offset(idx),
1397            UnionMode::Sparse => idx,
1398        };
1399        let (name, field) = s.0[id as usize].as_ref().unwrap();
1400
1401        write!(f, "{{{name}={}}}", field.value(idx))?;
1402        Ok(())
1403    }
1404}
1405
1406/// Get the value at the given row in an array as a String.
1407///
1408/// Note this function is quite inefficient and is unlikely to be
1409/// suitable for converting large arrays or record batches.
1410///
1411/// Please see [`ArrayFormatter`] for a more performant interface
1412pub fn array_value_to_string(column: &dyn Array, row: usize) -> Result<String, ArrowError> {
1413    let options = FormatOptions::default().with_display_error(true);
1414    let formatter = ArrayFormatter::try_new(column, &options)?;
1415    Ok(formatter.value(row).to_string())
1416}
1417
1418/// Converts numeric type to a `String`
1419pub fn lexical_to_string<N: lexical_core::ToLexical>(n: N) -> String {
1420    let mut buf = Vec::<u8>::with_capacity(N::FORMATTED_SIZE_DECIMAL);
1421    unsafe {
1422        // JUSTIFICATION
1423        //  Benefit
1424        //      Allows using the faster serializer lexical core and convert to string
1425        //  Soundness
1426        //      Length of buf is set as written length afterwards. lexical_core
1427        //      creates a valid string, so doesn't need to be checked.
1428        let slice = std::slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.capacity());
1429        let len = lexical_core::write(n, slice).len();
1430        buf.set_len(len);
1431        String::from_utf8_unchecked(buf)
1432    }
1433}
1434
1435#[cfg(test)]
1436mod tests {
1437    use super::*;
1438    use arrow_array::builder::StringRunBuilder;
1439
1440    /// Test to verify options can be constant. See #4580
1441    const TEST_CONST_OPTIONS: FormatOptions<'static> = FormatOptions::new()
1442        .with_date_format(Some("foo"))
1443        .with_timestamp_format(Some("404"));
1444
1445    #[test]
1446    fn test_const_options() {
1447        assert_eq!(TEST_CONST_OPTIONS.date_format, Some("foo"));
1448    }
1449
1450    /// See https://github.com/apache/arrow-rs/issues/8875
1451    #[test]
1452    fn test_options_send_sync() {
1453        fn assert_send_sync<T>()
1454        where
1455            T: Send + Sync,
1456        {
1457            // nothing – the compiler does the work
1458        }
1459
1460        assert_send_sync::<FormatOptions<'static>>();
1461    }
1462
1463    #[test]
1464    fn test_map_array_to_string() {
1465        let keys = vec!["a", "b", "c", "d", "e", "f", "g", "h"];
1466        let values_data = UInt32Array::from(vec![0u32, 10, 20, 30, 40, 50, 60, 70]);
1467
1468        // Construct a buffer for value offsets, for the nested array:
1469        //  [[a, b, c], [d, e, f], [g, h]]
1470        let entry_offsets = [0, 3, 6, 8];
1471
1472        let map_array =
1473            MapArray::new_from_strings(keys.clone().into_iter(), &values_data, &entry_offsets)
1474                .unwrap();
1475        assert_eq!(
1476            "{d: 30, e: 40, f: 50}",
1477            array_value_to_string(&map_array, 1).unwrap()
1478        );
1479    }
1480
1481    fn format_array(array: &dyn Array, fmt: &FormatOptions) -> Vec<String> {
1482        let fmt = ArrayFormatter::try_new(array, fmt).unwrap();
1483        (0..array.len()).map(|x| fmt.value(x).to_string()).collect()
1484    }
1485
1486    #[test]
1487    fn test_temporal_custom_format() {
1488        let options = FormatOptions::new()
1489            .with_date_format(Some("%Y-%m-%d"))
1490            .with_datetime_format(Some("%Y-%m-%d %H:%M:%S"))
1491            .with_time_format(Some("%H:%M:%S"))
1492            .with_timestamp_format(Some("%Y-%m-%d %H:%M:%S"))
1493            .with_timestamp_tz_format(Some("%Y-%m-%d %H:%M:%S %:z"));
1494
1495        let date32 = Date32Array::from(vec![0]);
1496        assert_eq!(format_array(&date32, &options), ["1970-01-01"]);
1497
1498        let date64 = Date64Array::from(vec![0]);
1499        assert_eq!(format_array(&date64, &options), ["1970-01-01 00:00:00"]);
1500
1501        let time = Time32SecondArray::from(vec![3661]);
1502        assert_eq!(format_array(&time, &options), ["01:01:01"]);
1503
1504        let timestamp = TimestampSecondArray::from(vec![0]);
1505        assert_eq!(format_array(&timestamp, &options), ["1970-01-01 00:00:00"]);
1506
1507        let timestamp_tz = TimestampSecondArray::from(vec![0]).with_timezone("+08:00");
1508        assert_eq!(
1509            format_array(&timestamp_tz, &options),
1510            ["1970-01-01 08:00:00 +08:00"]
1511        );
1512
1513        let invalid_options = FormatOptions::new().with_datetime_format(Some("%"));
1514        let formatter = ArrayFormatter::try_new(&date64, &invalid_options).unwrap();
1515        assert!(formatter.value(0).try_to_string().is_err());
1516    }
1517
1518    #[test]
1519    fn test_array_value_to_string_duration() {
1520        let iso_fmt = FormatOptions::new();
1521        let pretty_fmt = FormatOptions::new().with_duration_format(DurationFormat::Pretty);
1522
1523        let array = DurationNanosecondArray::from(vec![
1524            1,
1525            -1,
1526            1000,
1527            -1000,
1528            (45 * 60 * 60 * 24 + 14 * 60 * 60 + 2 * 60 + 34) * 1_000_000_000 + 123456789,
1529            -(45 * 60 * 60 * 24 + 14 * 60 * 60 + 2 * 60 + 34) * 1_000_000_000 - 123456789,
1530        ]);
1531        let iso = format_array(&array, &iso_fmt);
1532        let pretty = format_array(&array, &pretty_fmt);
1533
1534        assert_eq!(iso[0], "PT0.000000001S");
1535        assert_eq!(pretty[0], "0 days 0 hours 0 mins 0.000000001 secs");
1536        assert_eq!(iso[1], "-PT0.000000001S");
1537        assert_eq!(pretty[1], "0 days 0 hours 0 mins -0.000000001 secs");
1538        assert_eq!(iso[2], "PT0.000001S");
1539        assert_eq!(pretty[2], "0 days 0 hours 0 mins 0.000001000 secs");
1540        assert_eq!(iso[3], "-PT0.000001S");
1541        assert_eq!(pretty[3], "0 days 0 hours 0 mins -0.000001000 secs");
1542        assert_eq!(iso[4], "PT3938554.123456789S");
1543        assert_eq!(pretty[4], "45 days 14 hours 2 mins 34.123456789 secs");
1544        assert_eq!(iso[5], "-PT3938554.123456789S");
1545        assert_eq!(pretty[5], "-45 days -14 hours -2 mins -34.123456789 secs");
1546
1547        let array = DurationMicrosecondArray::from(vec![
1548            1,
1549            -1,
1550            1000,
1551            -1000,
1552            (45 * 60 * 60 * 24 + 14 * 60 * 60 + 2 * 60 + 34) * 1_000_000 + 123456,
1553            -(45 * 60 * 60 * 24 + 14 * 60 * 60 + 2 * 60 + 34) * 1_000_000 - 123456,
1554        ]);
1555        let iso = format_array(&array, &iso_fmt);
1556        let pretty = format_array(&array, &pretty_fmt);
1557
1558        assert_eq!(iso[0], "PT0.000001S");
1559        assert_eq!(pretty[0], "0 days 0 hours 0 mins 0.000001 secs");
1560        assert_eq!(iso[1], "-PT0.000001S");
1561        assert_eq!(pretty[1], "0 days 0 hours 0 mins -0.000001 secs");
1562        assert_eq!(iso[2], "PT0.001S");
1563        assert_eq!(pretty[2], "0 days 0 hours 0 mins 0.001000 secs");
1564        assert_eq!(iso[3], "-PT0.001S");
1565        assert_eq!(pretty[3], "0 days 0 hours 0 mins -0.001000 secs");
1566        assert_eq!(iso[4], "PT3938554.123456S");
1567        assert_eq!(pretty[4], "45 days 14 hours 2 mins 34.123456 secs");
1568        assert_eq!(iso[5], "-PT3938554.123456S");
1569        assert_eq!(pretty[5], "-45 days -14 hours -2 mins -34.123456 secs");
1570
1571        let array = DurationMillisecondArray::from(vec![
1572            1,
1573            -1,
1574            1000,
1575            -1000,
1576            (45 * 60 * 60 * 24 + 14 * 60 * 60 + 2 * 60 + 34) * 1_000 + 123,
1577            -(45 * 60 * 60 * 24 + 14 * 60 * 60 + 2 * 60 + 34) * 1_000 - 123,
1578        ]);
1579        let iso = format_array(&array, &iso_fmt);
1580        let pretty = format_array(&array, &pretty_fmt);
1581
1582        assert_eq!(iso[0], "PT0.001S");
1583        assert_eq!(pretty[0], "0 days 0 hours 0 mins 0.001 secs");
1584        assert_eq!(iso[1], "-PT0.001S");
1585        assert_eq!(pretty[1], "0 days 0 hours 0 mins -0.001 secs");
1586        assert_eq!(iso[2], "PT1S");
1587        assert_eq!(pretty[2], "0 days 0 hours 0 mins 1.000 secs");
1588        assert_eq!(iso[3], "-PT1S");
1589        assert_eq!(pretty[3], "0 days 0 hours 0 mins -1.000 secs");
1590        assert_eq!(iso[4], "PT3938554.123S");
1591        assert_eq!(pretty[4], "45 days 14 hours 2 mins 34.123 secs");
1592        assert_eq!(iso[5], "-PT3938554.123S");
1593        assert_eq!(pretty[5], "-45 days -14 hours -2 mins -34.123 secs");
1594
1595        let array = DurationSecondArray::from(vec![
1596            1,
1597            -1,
1598            1000,
1599            -1000,
1600            45 * 60 * 60 * 24 + 14 * 60 * 60 + 2 * 60 + 34,
1601            -45 * 60 * 60 * 24 - 14 * 60 * 60 - 2 * 60 - 34,
1602        ]);
1603        let iso = format_array(&array, &iso_fmt);
1604        let pretty = format_array(&array, &pretty_fmt);
1605
1606        assert_eq!(iso[0], "PT1S");
1607        assert_eq!(pretty[0], "0 days 0 hours 0 mins 1 secs");
1608        assert_eq!(iso[1], "-PT1S");
1609        assert_eq!(pretty[1], "0 days 0 hours 0 mins -1 secs");
1610        assert_eq!(iso[2], "PT1000S");
1611        assert_eq!(pretty[2], "0 days 0 hours 16 mins 40 secs");
1612        assert_eq!(iso[3], "-PT1000S");
1613        assert_eq!(pretty[3], "0 days 0 hours -16 mins -40 secs");
1614        assert_eq!(iso[4], "PT3938554S");
1615        assert_eq!(pretty[4], "45 days 14 hours 2 mins 34 secs");
1616        assert_eq!(iso[5], "-PT3938554S");
1617        assert_eq!(pretty[5], "-45 days -14 hours -2 mins -34 secs");
1618    }
1619
1620    #[test]
1621    fn test_null() {
1622        let array = NullArray::new(2);
1623        let options = FormatOptions::new().with_null("NULL");
1624        let formatted = format_array(&array, &options);
1625        assert_eq!(formatted, &["NULL".to_string(), "NULL".to_string()])
1626    }
1627
1628    #[test]
1629    fn test_string_run_arry_to_string() {
1630        let mut builder = StringRunBuilder::<Int32Type>::new();
1631
1632        builder.append_value("input_value");
1633        builder.append_value("input_value");
1634        builder.append_value("input_value");
1635        builder.append_value("input_value1");
1636
1637        let map_array = builder.finish();
1638        assert_eq!("input_value", array_value_to_string(&map_array, 1).unwrap());
1639        assert_eq!(
1640            "input_value1",
1641            array_value_to_string(&map_array, 3).unwrap()
1642        );
1643    }
1644
1645    #[test]
1646    fn test_list_view_to_string() {
1647        let list_view = ListViewArray::from_iter_primitive::<Int32Type, _, _>(vec![
1648            Some(vec![Some(1), Some(2), Some(3)]),
1649            None,
1650            Some(vec![Some(4), None, Some(6)]),
1651            Some(vec![]),
1652        ]);
1653
1654        assert_eq!("[1, 2, 3]", array_value_to_string(&list_view, 0).unwrap());
1655        assert_eq!("", array_value_to_string(&list_view, 1).unwrap());
1656        assert_eq!("[4, , 6]", array_value_to_string(&list_view, 2).unwrap());
1657        assert_eq!("[]", array_value_to_string(&list_view, 3).unwrap());
1658    }
1659
1660    #[test]
1661    fn test_large_list_view_to_string() {
1662        let list_view = LargeListViewArray::from_iter_primitive::<Int32Type, _, _>(vec![
1663            Some(vec![Some(1), Some(2), Some(3)]),
1664            None,
1665            Some(vec![Some(4), None, Some(6)]),
1666            Some(vec![]),
1667        ]);
1668
1669        assert_eq!("[1, 2, 3]", array_value_to_string(&list_view, 0).unwrap());
1670        assert_eq!("", array_value_to_string(&list_view, 1).unwrap());
1671        assert_eq!("[4, , 6]", array_value_to_string(&list_view, 2).unwrap());
1672        assert_eq!("[]", array_value_to_string(&list_view, 3).unwrap());
1673    }
1674}