Skip to main content

arrow_schema/
datatype_display.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: See the NOTICE file distributed with this work for additional information regarding copyright ownership
3// SPDX-FileContributor: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements
4
5use crate::DataType;
6use crate::Field;
7use crate::Metadata;
8use std::fmt;
9use std::fmt::Display;
10
11impl Display for DataType {
12    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
13        fn format_metadata(metadata: &Metadata) -> String {
14            format!("{}", FormatMetadata(metadata))
15        }
16
17        fn format_nullability(field: &crate::Field) -> &str {
18            if field.is_nullable() { "" } else { "non-null " }
19        }
20
21        fn format_field(field: &crate::Field) -> String {
22            let name = field.name();
23            let maybe_nullable = format_nullability(field);
24            let data_type = field.data_type();
25            let metadata_str = format_metadata(field.metadata());
26            format!("{name:?}: {maybe_nullable}{data_type}{metadata_str}")
27        }
28
29        // A lot of these can still be improved a lot.
30        // _Some_ of these can be parsed with `FromStr`, but not all (YET!).
31        // The goal is that the formatting should always be
32        // * Terse and teadable
33        // * Reversible (contain all necessary information to reverse it perfectly)
34
35        match &self {
36            Self::Null => write!(f, "Null"),
37            Self::Boolean => write!(f, "Boolean"),
38            Self::Int8 => write!(f, "Int8"),
39            Self::Int16 => write!(f, "Int16"),
40            Self::Int32 => write!(f, "Int32"),
41            Self::Int64 => write!(f, "Int64"),
42            Self::UInt8 => write!(f, "UInt8"),
43            Self::UInt16 => write!(f, "UInt16"),
44            Self::UInt32 => write!(f, "UInt32"),
45            Self::UInt64 => write!(f, "UInt64"),
46            Self::Float16 => write!(f, "Float16"),
47            Self::Float32 => write!(f, "Float32"),
48            Self::Float64 => write!(f, "Float64"),
49            Self::Timestamp(time_unit, timezone) => {
50                if let Some(timezone) = timezone {
51                    write!(f, "Timestamp({time_unit}, {timezone:?})")
52                } else {
53                    write!(f, "Timestamp({time_unit})")
54                }
55            }
56            Self::Date32 => write!(f, "Date32"),
57            Self::Date64 => write!(f, "Date64"),
58            Self::Time32(time_unit) => write!(f, "Time32({time_unit})"),
59            Self::Time64(time_unit) => write!(f, "Time64({time_unit})"),
60            Self::Duration(time_unit) => write!(f, "Duration({time_unit})"),
61            Self::Interval(interval_unit) => write!(f, "Interval({interval_unit:?})"),
62            Self::Binary => write!(f, "Binary"),
63            Self::FixedSizeBinary(bytes_per_value) => {
64                write!(f, "FixedSizeBinary({bytes_per_value:?})")
65            }
66            Self::LargeBinary => write!(f, "LargeBinary"),
67            Self::BinaryView => write!(f, "BinaryView"),
68            Self::Utf8 => write!(f, "Utf8"),
69            Self::LargeUtf8 => write!(f, "LargeUtf8"),
70            Self::Utf8View => write!(f, "Utf8View"),
71            Self::List(field)
72            | Self::LargeList(field)
73            | Self::ListView(field)
74            | Self::LargeListView(field) => {
75                let type_name = if matches!(self, Self::List(_)) {
76                    "List"
77                } else if matches!(self, Self::ListView(_)) {
78                    "ListView"
79                } else if matches!(self, Self::LargeList(_)) {
80                    "LargeList"
81                } else {
82                    "LargeListView"
83                };
84
85                let name = field.name();
86                let maybe_nullable = format_nullability(field);
87                let data_type = field.data_type();
88                let field_name_str = if name == "item" {
89                    String::default()
90                } else {
91                    format!(", field: '{name}'")
92                };
93                let metadata_str = format_metadata(field.metadata());
94
95                // e.g. `LargeList(non-null Uint32)
96                write!(
97                    f,
98                    "{type_name}({maybe_nullable}{data_type}{field_name_str}{metadata_str})"
99                )
100            }
101            Self::FixedSizeList(field, size) => {
102                let name = field.name();
103                let maybe_nullable = format_nullability(field);
104                let data_type = field.data_type();
105                let field_name_str = if name == "item" {
106                    String::default()
107                } else {
108                    format!(", field: '{name}'")
109                };
110                let metadata_str = format_metadata(field.metadata());
111
112                write!(
113                    f,
114                    "FixedSizeList({size} x {maybe_nullable}{data_type}{field_name_str}{metadata_str})",
115                )
116            }
117            Self::Struct(fields) => {
118                write!(f, "Struct(")?;
119                if !fields.is_empty() {
120                    let fields_str = fields
121                        .iter()
122                        .map(|field| format_field(field))
123                        .collect::<Vec<_>>()
124                        .join(", ");
125                    write!(f, "{fields_str}")?;
126                }
127                write!(f, ")")?;
128                Ok(())
129            }
130            Self::Union(union_fields, union_mode) => {
131                write!(f, "Union({union_mode:?}")?;
132                if !union_fields.is_empty() {
133                    write!(f, ", ")?;
134                    let fields_str = union_fields
135                        .iter()
136                        .map(|v| {
137                            let type_id = v.0;
138                            let field_str = format_field(v.1);
139                            format!("{type_id:?}: ({field_str})")
140                        })
141                        .collect::<Vec<_>>()
142                        .join(", ");
143                    write!(f, "{fields_str}")?;
144                }
145                write!(f, ")")?;
146                Ok(())
147            }
148            Self::Dictionary(data_type, data_type1) => {
149                write!(f, "Dictionary({data_type}, {data_type1})")
150            }
151            Self::Decimal32(precision, scale) => write!(f, "Decimal32({precision}, {scale})"),
152            Self::Decimal64(precision, scale) => write!(f, "Decimal64({precision}, {scale})"),
153            Self::Decimal128(precision, scale) => write!(f, "Decimal128({precision}, {scale})"),
154            Self::Decimal256(precision, scale) => write!(f, "Decimal256({precision}, {scale})"),
155            Self::Map(field, sorted) => {
156                write!(f, "Map(")?;
157                let map_field_str = format_field(field);
158                let keys_are_sorted = if *sorted { "sorted" } else { "unsorted" };
159
160                write!(f, "{map_field_str}, {keys_are_sorted})")?;
161                Ok(())
162            }
163            Self::RunEndEncoded(run_ends_field, values_field) => {
164                let default_names = run_ends_field.name() == Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME
165                    && values_field.name() == Field::REE_VALUES_FIELD_DEFAULT_NAME;
166                let no_metadata =
167                    run_ends_field.metadata().is_empty() && values_field.metadata().is_empty();
168                write!(f, "RunEndEncoded(")?;
169                if default_names && no_metadata {
170                    let re_null = format_nullability(run_ends_field);
171                    let v_null = format_nullability(values_field);
172                    write!(
173                        f,
174                        "{re_null}{}, {v_null}{})",
175                        run_ends_field.data_type(),
176                        values_field.data_type(),
177                    )?;
178                } else {
179                    let run_ends_str = format_field(run_ends_field);
180                    let values_str = format_field(values_field);
181                    write!(f, "{run_ends_str}, {values_str})")?;
182                }
183                Ok(())
184            }
185        }
186    }
187}
188
189/// Adapter to format [`Metadata`] consistently.
190struct FormatMetadata<'a>(&'a Metadata);
191
192impl fmt::Display for FormatMetadata<'_> {
193    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194        let metadata = self.0;
195        if metadata.is_empty() {
196            Ok(())
197        } else {
198            // `Metadata` iterates in sorted key order
199            write!(f, ", metadata: ")?;
200            f.debug_map().entries(metadata.iter()).finish()
201        }
202    }
203}
204
205#[cfg(test)]
206mod tests {
207
208    use std::collections::HashMap;
209    use std::sync::Arc;
210
211    use crate::Field;
212
213    use super::*;
214
215    #[test]
216    fn test_display_list() {
217        let list_data_type = DataType::List(Arc::new(Field::new_list_field(DataType::Int32, true)));
218        let list_data_type_string = list_data_type.to_string();
219        let expected_string = "List(Int32)";
220        assert_eq!(list_data_type_string, expected_string);
221    }
222
223    #[test]
224    fn test_display_list_view() {
225        let list_view_data_type =
226            DataType::ListView(Arc::new(Field::new("item", DataType::Int32, true)));
227        let list_view_data_type_string = list_view_data_type.to_string();
228        let expected_string = "ListView(Int32)";
229        assert_eq!(list_view_data_type_string, expected_string);
230    }
231
232    #[test]
233    fn test_display_list_with_named_field() {
234        let list_data_type = DataType::List(Arc::new(Field::new("foo", DataType::UInt64, false)));
235        let list_data_type_string = list_data_type.to_string();
236        let expected_string = "List(non-null UInt64, field: 'foo')";
237        assert_eq!(list_data_type_string, expected_string);
238    }
239
240    #[test]
241    fn test_display_list_view_with_named_field() {
242        let list_view_data_type =
243            DataType::ListView(Arc::new(Field::new("bar", DataType::UInt64, false)));
244        let list_view_data_type_string = list_view_data_type.to_string();
245        let expected_string = "ListView(non-null UInt64, field: 'bar')";
246        assert_eq!(list_view_data_type_string, expected_string);
247    }
248
249    #[test]
250    fn test_display_nested_list() {
251        let nested_data_type = DataType::List(Arc::new(Field::new_list_field(
252            DataType::List(Arc::new(Field::new_list_field(DataType::UInt64, false))),
253            false,
254        )));
255        let nested_data_type_string = nested_data_type.to_string();
256        let nested_expected_string = "List(non-null List(non-null UInt64))";
257        assert_eq!(nested_data_type_string, nested_expected_string);
258    }
259
260    #[test]
261    fn test_display_nested_list_view() {
262        let nested_view_data_type = DataType::ListView(Arc::new(Field::new_list_field(
263            DataType::ListView(Arc::new(Field::new_list_field(DataType::UInt64, false))),
264            false,
265        )));
266        let nested_view_data_type_string = nested_view_data_type.to_string();
267        let nested_view_expected_string = "ListView(non-null ListView(non-null UInt64))";
268        assert_eq!(nested_view_data_type_string, nested_view_expected_string);
269    }
270
271    #[test]
272    fn test_display_list_with_metadata() {
273        let mut field = Field::new_list_field(DataType::Int32, true);
274        let metadata = HashMap::from([("foo1".to_string(), "value1".to_string())]);
275        field.set_metadata(metadata);
276        let list_data_type = DataType::List(Arc::new(field));
277        let list_data_type_string = list_data_type.to_string();
278        let expected_string = "List(Int32, metadata: {\"foo1\": \"value1\"})";
279
280        assert_eq!(list_data_type_string, expected_string);
281    }
282
283    #[test]
284    fn test_display_list_view_with_metadata() {
285        let mut field = Field::new_list_field(DataType::Int32, true);
286        let metadata = HashMap::from([("foo2".to_string(), "value2".to_string())]);
287        field.set_metadata(metadata);
288        let list_view_data_type = DataType::ListView(Arc::new(field));
289        let list_view_data_type_string = list_view_data_type.to_string();
290        let expected_string = "ListView(Int32, metadata: {\"foo2\": \"value2\"})";
291        assert_eq!(list_view_data_type_string, expected_string);
292    }
293
294    #[test]
295    fn test_display_large_list() {
296        let large_list_data_type =
297            DataType::LargeList(Arc::new(Field::new_list_field(DataType::Int32, true)));
298        let large_list_data_type_string = large_list_data_type.to_string();
299        let expected_string = "LargeList(Int32)";
300        assert_eq!(large_list_data_type_string, expected_string);
301
302        // Test with named field
303        let large_list_named =
304            DataType::LargeList(Arc::new(Field::new("bar", DataType::UInt64, false)));
305        let large_list_named_string = large_list_named.to_string();
306        let expected_named_string = "LargeList(non-null UInt64, field: 'bar')";
307        assert_eq!(large_list_named_string, expected_named_string);
308
309        // Test with metadata
310        let mut field = Field::new_list_field(DataType::Int32, true);
311        let metadata = HashMap::from([("key1".to_string(), "value1".to_string())]);
312        field.set_metadata(metadata);
313        let large_list_metadata = DataType::LargeList(Arc::new(field));
314        let large_list_metadata_string = large_list_metadata.to_string();
315        let expected_metadata_string = "LargeList(Int32, metadata: {\"key1\": \"value1\"})";
316        assert_eq!(large_list_metadata_string, expected_metadata_string);
317    }
318
319    #[test]
320    fn test_display_large_list_view() {
321        let large_list_view_data_type =
322            DataType::LargeListView(Arc::new(Field::new("item", DataType::Int32, true)));
323        let large_list_view_data_type_string = large_list_view_data_type.to_string();
324        let expected_string = "LargeListView(Int32)";
325        assert_eq!(large_list_view_data_type_string, expected_string);
326
327        // Test with named field
328        let large_list_view_named =
329            DataType::LargeListView(Arc::new(Field::new("bar", DataType::UInt64, false)));
330        let large_list_view_named_string = large_list_view_named.to_string();
331        let expected_named_string = "LargeListView(non-null UInt64, field: 'bar')";
332        assert_eq!(large_list_view_named_string, expected_named_string);
333
334        // Test with metadata
335        let mut field = Field::new_list_field(DataType::Int32, true);
336        let metadata = HashMap::from([("key1".to_string(), "value1".to_string())]);
337        field.set_metadata(metadata);
338        let large_list_view_metadata = DataType::LargeListView(Arc::new(field));
339        let large_list_view_metadata_string = large_list_view_metadata.to_string();
340        let expected_metadata_string = "LargeListView(Int32, metadata: {\"key1\": \"value1\"})";
341        assert_eq!(large_list_view_metadata_string, expected_metadata_string);
342    }
343
344    #[test]
345    fn test_display_fixed_size_list() {
346        let fixed_size_list =
347            DataType::FixedSizeList(Arc::new(Field::new_list_field(DataType::Int32, true)), 5);
348        let fixed_size_list_string = fixed_size_list.to_string();
349        let expected_string = "FixedSizeList(5 x Int32)";
350        assert_eq!(fixed_size_list_string, expected_string);
351
352        // Test with named field
353        let fixed_size_named =
354            DataType::FixedSizeList(Arc::new(Field::new("baz", DataType::UInt64, false)), 3);
355        let fixed_size_named_string = fixed_size_named.to_string();
356        let expected_named_string = "FixedSizeList(3 x non-null UInt64, field: 'baz')";
357        assert_eq!(fixed_size_named_string, expected_named_string);
358
359        // Test with metadata
360        let mut field = Field::new_list_field(DataType::Int32, true);
361        let metadata = HashMap::from([("key2".to_string(), "value2".to_string())]);
362        field.set_metadata(metadata);
363        let fixed_size_metadata = DataType::FixedSizeList(Arc::new(field), 4);
364        let fixed_size_metadata_string = fixed_size_metadata.to_string();
365        let expected_metadata_string = "FixedSizeList(4 x Int32, metadata: {\"key2\": \"value2\"})";
366        assert_eq!(fixed_size_metadata_string, expected_metadata_string);
367    }
368
369    #[test]
370    fn test_display_struct() {
371        let fields = vec![
372            Field::new("a", DataType::Int32, false),
373            Field::new("b", DataType::Utf8, true),
374        ];
375        let struct_data_type = DataType::Struct(fields.into());
376        let struct_data_type_string = struct_data_type.to_string();
377        let expected_string = "Struct(\"a\": non-null Int32, \"b\": Utf8)";
378        assert_eq!(struct_data_type_string, expected_string);
379
380        // Test with metadata
381        let mut field_with_metadata = Field::new("b", DataType::Utf8, true);
382        let metadata = HashMap::from([
383            ("key".to_string(), "value".to_string()),
384            ("key2".to_string(), "value2".to_string()),
385        ]);
386        field_with_metadata.set_metadata(metadata);
387        let struct_fields_with_metadata =
388            vec![Field::new("a", DataType::Int32, false), field_with_metadata];
389        let struct_data_type_with_metadata = DataType::Struct(struct_fields_with_metadata.into());
390        let struct_data_type_with_metadata_string = struct_data_type_with_metadata.to_string();
391        let expected_string_with_metadata = "Struct(\"a\": non-null Int32, \"b\": Utf8, metadata: {\"key\": \"value\", \"key2\": \"value2\"})";
392        assert_eq!(
393            struct_data_type_with_metadata_string,
394            expected_string_with_metadata
395        );
396    }
397
398    #[test]
399    fn test_display_union() {
400        let fields = vec![
401            Field::new("a", DataType::Int32, false),
402            Field::new("b", DataType::Utf8, true),
403        ];
404        let type_ids = vec![0, 1];
405        let union_fields = type_ids
406            .into_iter()
407            .zip(fields.into_iter().map(Arc::new))
408            .collect();
409
410        let union_data_type = DataType::Union(union_fields, crate::UnionMode::Sparse);
411        let union_data_type_string = union_data_type.to_string();
412        let expected_string = "Union(Sparse, 0: (\"a\": non-null Int32), 1: (\"b\": Utf8))";
413        assert_eq!(union_data_type_string, expected_string);
414
415        // Test with metadata
416        let mut field_with_metadata = Field::new("b", DataType::Utf8, true);
417        let metadata = HashMap::from([("key".to_string(), "value".to_string())]);
418        field_with_metadata.set_metadata(metadata);
419        let union_fields_with_metadata = vec![
420            (0, Arc::new(Field::new("a", DataType::Int32, false))),
421            (1, Arc::new(field_with_metadata)),
422        ]
423        .into_iter()
424        .collect();
425        let union_data_type_with_metadata =
426            DataType::Union(union_fields_with_metadata, crate::UnionMode::Sparse);
427        let union_data_type_with_metadata_string = union_data_type_with_metadata.to_string();
428        let expected_string_with_metadata = "Union(Sparse, 0: (\"a\": non-null Int32), 1: (\"b\": Utf8, metadata: {\"key\": \"value\"}))";
429        assert_eq!(
430            union_data_type_with_metadata_string,
431            expected_string_with_metadata
432        );
433    }
434
435    #[test]
436    fn test_display_map() {
437        let entry_field = Field::new(
438            Field::MAP_ENTRIES_FIELD_DEFAULT_NAME,
439            DataType::Struct(
440                vec![
441                    Field::new(Field::MAP_KEY_FIELD_DEFAULT_NAME, DataType::Utf8, false),
442                    Field::new(Field::MAP_VALUE_FIELD_DEFAULT_NAME, DataType::Int32, true),
443                ]
444                .into(),
445            ),
446            false,
447        );
448        let map_data_type = DataType::Map(Arc::new(entry_field), true);
449        let map_data_type_string = map_data_type.to_string();
450        let expected_string =
451            "Map(\"entries\": non-null Struct(\"key\": non-null Utf8, \"value\": Int32), sorted)";
452        assert_eq!(map_data_type_string, expected_string);
453
454        // Test with metadata
455        let mut entry_field_with_metadata = Field::new(
456            Field::MAP_ENTRIES_FIELD_DEFAULT_NAME,
457            DataType::Struct(
458                vec![
459                    Field::new(Field::MAP_KEY_FIELD_DEFAULT_NAME, DataType::Utf8, false),
460                    Field::new(Field::MAP_VALUE_FIELD_DEFAULT_NAME, DataType::Int32, true),
461                ]
462                .into(),
463            ),
464            false,
465        );
466        let metadata = HashMap::from([("key".to_string(), "value".to_string())]);
467        entry_field_with_metadata.set_metadata(metadata);
468        let map_data_type_with_metadata = DataType::Map(Arc::new(entry_field_with_metadata), true);
469        let map_data_type_with_metadata_string = map_data_type_with_metadata.to_string();
470        let expected_string_with_metadata = "Map(\"entries\": non-null Struct(\"key\": non-null Utf8, \"value\": Int32), metadata: {\"key\": \"value\"}, sorted)";
471        assert_eq!(
472            map_data_type_with_metadata_string,
473            expected_string_with_metadata
474        );
475    }
476
477    #[test]
478    fn test_display_run_end_encoded() {
479        // Compact form: default field names
480        let run_ends_field = Arc::new(Field::new(
481            Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
482            DataType::UInt32,
483            false,
484        ));
485        let values_field = Arc::new(Field::new(
486            Field::REE_VALUES_FIELD_DEFAULT_NAME,
487            DataType::Int32,
488            true,
489        ));
490        let ree = DataType::RunEndEncoded(run_ends_field.clone(), values_field.clone());
491        assert_eq!(ree.to_string(), "RunEndEncoded(non-null UInt32, Int32)");
492
493        // Compact form: non-null values
494        let run_ends_field = Arc::new(Field::new(
495            Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
496            DataType::Int32,
497            false,
498        ));
499        let values_field_str = Arc::new(Field::new(
500            Field::REE_VALUES_FIELD_DEFAULT_NAME,
501            DataType::Utf8,
502            false,
503        ));
504        let ree2 = DataType::RunEndEncoded(run_ends_field, values_field_str);
505        assert_eq!(
506            ree2.to_string(),
507            "RunEndEncoded(non-null Int32, non-null Utf8)"
508        );
509
510        // Verbose form: metadata on values field triggers verbose form
511        let run_ends_field = Arc::new(Field::new(
512            Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
513            DataType::Int32,
514            false,
515        ));
516        let mut values_with_meta =
517            Field::new(Field::REE_VALUES_FIELD_DEFAULT_NAME, DataType::Utf8, true);
518        values_with_meta.set_metadata(HashMap::from([("k".to_string(), "v".to_string())]));
519        let ree_meta = DataType::RunEndEncoded(run_ends_field, Arc::new(values_with_meta));
520        assert_eq!(
521            ree_meta.to_string(),
522            "RunEndEncoded(\"run_ends\": non-null Int32, \"values\": Utf8, metadata: {\"k\": \"v\"})"
523        );
524
525        // Verbose form: non-default field name on values
526        let run_ends_field = Arc::new(Field::new(
527            Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
528            DataType::Int32,
529            false,
530        ));
531        let named_values = Arc::new(Field::new("named_values", DataType::Utf8, false));
532        let ree3 = DataType::RunEndEncoded(run_ends_field, named_values);
533        assert_eq!(
534            ree3.to_string(),
535            "RunEndEncoded(\"run_ends\": non-null Int32, \"named_values\": non-null Utf8)"
536        );
537
538        // Verbose form: non-default field name on run_ends
539        let custom_re = Arc::new(Field::new("re", DataType::Int32, false));
540        let values_field = Arc::new(Field::new(
541            Field::REE_VALUES_FIELD_DEFAULT_NAME,
542            DataType::Int32,
543            true,
544        ));
545        let ree4 = DataType::RunEndEncoded(custom_re, values_field);
546        assert_eq!(
547            ree4.to_string(),
548            "RunEndEncoded(\"re\": non-null Int32, \"values\": Int32)"
549        );
550
551        // Verbose form: metadata on both fields
552        let mut run_ends_with_meta = Field::new(
553            Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
554            DataType::Int32,
555            false,
556        );
557        run_ends_with_meta.set_metadata(HashMap::from([(
558            "source".to_string(),
559            "encoder".to_string(),
560        )]));
561        let mut values_with_meta2 =
562            Field::new(Field::REE_VALUES_FIELD_DEFAULT_NAME, DataType::Utf8, true);
563        values_with_meta2.set_metadata(HashMap::from([("locale".to_string(), "en".to_string())]));
564        let ree5 =
565            DataType::RunEndEncoded(Arc::new(run_ends_with_meta), Arc::new(values_with_meta2));
566        assert_eq!(
567            ree5.to_string(),
568            "RunEndEncoded(\"run_ends\": non-null Int32, metadata: {\"source\": \"encoder\"}, \"values\": Utf8, metadata: {\"locale\": \"en\"})"
569        );
570    }
571
572    #[test]
573    fn test_display_dictionary() {
574        let dict_data_type =
575            DataType::Dictionary(Box::new(DataType::Int8), Box::new(DataType::Utf8));
576        let dict_data_type_string = dict_data_type.to_string();
577        let expected_string = "Dictionary(Int8, Utf8)";
578        assert_eq!(dict_data_type_string, expected_string);
579
580        // Test with complex index and value types
581        let complex_dict_data_type = DataType::Dictionary(
582            Box::new(DataType::Int16),
583            Box::new(DataType::Struct(
584                vec![
585                    Field::new("a", DataType::Int32, false),
586                    Field::new("b", DataType::Utf8, true),
587                ]
588                .into(),
589            )),
590        );
591        let complex_dict_data_type_string = complex_dict_data_type.to_string();
592        let expected_complex_string =
593            "Dictionary(Int16, Struct(\"a\": non-null Int32, \"b\": Utf8))";
594        assert_eq!(complex_dict_data_type_string, expected_complex_string);
595    }
596
597    #[test]
598    fn test_display_interval() {
599        let interval_year_month = DataType::Interval(crate::IntervalUnit::YearMonth);
600        let interval_year_month_string = interval_year_month.to_string();
601        let expected_year_month_string = "Interval(YearMonth)";
602        assert_eq!(interval_year_month_string, expected_year_month_string);
603
604        let interval_day_time = DataType::Interval(crate::IntervalUnit::DayTime);
605        let interval_day_time_string = interval_day_time.to_string();
606        let expected_day_time_string = "Interval(DayTime)";
607        assert_eq!(interval_day_time_string, expected_day_time_string);
608
609        let interval_month_day_nano = DataType::Interval(crate::IntervalUnit::MonthDayNano);
610        let interval_month_day_nano_string = interval_month_day_nano.to_string();
611        let expected_month_day_nano_string = "Interval(MonthDayNano)";
612        assert_eq!(
613            interval_month_day_nano_string,
614            expected_month_day_nano_string
615        );
616    }
617
618    #[test]
619    fn test_display_timestamp() {
620        let timestamp_without_tz = DataType::Timestamp(crate::TimeUnit::Microsecond, None);
621        let timestamp_without_tz_string = timestamp_without_tz.to_string();
622        let expected_without_tz_string = "Timestamp(µs)";
623        assert_eq!(timestamp_without_tz_string, expected_without_tz_string);
624
625        let timestamp_with_tz =
626            DataType::Timestamp(crate::TimeUnit::Nanosecond, Some(Arc::from("UTC")));
627        let timestamp_with_tz_string = timestamp_with_tz.to_string();
628        let expected_with_tz_string = "Timestamp(ns, \"UTC\")";
629        assert_eq!(timestamp_with_tz_string, expected_with_tz_string);
630    }
631}