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