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