Skip to main content

arrow_cast/
display.rs

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