Skip to main content

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