arrow_cast/
display.rs

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