Skip to main content

arrow_schema/
field.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::error::ArrowError;
19use std::cmp::Ordering;
20use std::hash::{Hash, Hasher};
21use std::sync::Arc;
22
23use crate::datatype::DataType;
24#[cfg(feature = "canonical_extension_types")]
25use crate::extension::CanonicalExtensionType;
26use crate::schema::SchemaBuilder;
27use crate::{
28    Fields, Metadata, UnionFields, UnionMode,
29    extension::{EXTENSION_TYPE_METADATA_KEY, EXTENSION_TYPE_NAME_KEY, ExtensionType},
30};
31
32/// A reference counted [`Field`]
33pub type FieldRef = Arc<Field>;
34
35/// Describes a single column in a [`Schema`](super::Schema).
36///
37/// A [`Schema`](super::Schema) is an ordered collection of
38/// [`Field`] objects. Fields contain:
39/// * `name`: the name of the field
40/// * `data_type`: the type of the field
41/// * `nullable`: if the field is nullable
42/// * `metadata`: a map of key-value pairs containing additional custom metadata
43///
44/// Arrow Extension types, are encoded in `Field`s metadata. See
45/// [`Self::try_extension_type`] to retrieve the [`ExtensionType`], if any.
46#[derive(Clone)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48pub struct Field {
49    name: String,
50    data_type: DataType,
51    nullable: bool,
52    #[deprecated(
53        since = "54.0.0",
54        note = "The ability to preserve dictionary IDs will be removed. With it, all fields related to it."
55    )]
56    dict_id: i64,
57    dict_is_ordered: bool,
58    /// A map of key-value pairs containing additional custom meta data.
59    metadata: Metadata,
60}
61
62impl std::fmt::Debug for Field {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        #![expect(deprecated)] // Must still print dict_id, if set
65        let Self {
66            name,
67            data_type,
68            nullable,
69            dict_id,
70            dict_is_ordered,
71            metadata,
72        } = self;
73
74        let mut s = f.debug_struct("Field");
75
76        if name != "item" {
77            // Keep it short when debug-formatting `DataType::List`
78            s.field("name", name);
79        }
80
81        s.field("data_type", data_type);
82
83        if *nullable {
84            s.field("nullable", nullable);
85        }
86
87        if *dict_id != 0 {
88            s.field("dict_id", dict_id);
89        }
90
91        if *dict_is_ordered {
92            s.field("dict_is_ordered", dict_is_ordered);
93        }
94
95        if !metadata.is_empty() {
96            s.field("metadata", metadata);
97        }
98        s.finish()
99    }
100}
101
102// Auto-derive `PartialEq` traits will pull `dict_id` and `dict_is_ordered`
103// into comparison. However, these properties are only used in IPC context
104// for matching dictionary encoded data. They are not necessary to be same
105// to consider schema equality. For example, in C++ `Field` implementation,
106// it doesn't contain these dictionary properties too.
107impl PartialEq for Field {
108    fn eq(&self, other: &Self) -> bool {
109        self.name == other.name
110            && self.data_type == other.data_type
111            && self.nullable == other.nullable
112            && self.metadata == other.metadata
113    }
114}
115
116impl Eq for Field {}
117
118impl PartialOrd for Field {
119    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
120        Some(self.cmp(other))
121    }
122}
123
124impl Ord for Field {
125    fn cmp(&self, other: &Self) -> Ordering {
126        self.name
127            .cmp(other.name())
128            .then_with(|| self.data_type.cmp(other.data_type()))
129            .then_with(|| self.nullable.cmp(&other.nullable))
130            .then_with(|| self.metadata.cmp(&other.metadata))
131    }
132}
133
134impl Hash for Field {
135    fn hash<H: Hasher>(&self, state: &mut H) {
136        self.name.hash(state);
137        self.data_type.hash(state);
138        self.nullable.hash(state);
139        // `Metadata` iterates in deterministic (sorted) key order
140        self.metadata.hash(state);
141    }
142}
143
144impl AsRef<Field> for Field {
145    fn as_ref(&self) -> &Field {
146        self
147    }
148}
149
150impl Field {
151    /// Default list member field name
152    pub const LIST_FIELD_DEFAULT_NAME: &'static str = "item";
153    /// Default field name for the entries field for Map
154    ///
155    /// See [Arrow Spec](https://github.com/apache/arrow/blob/b19c4761b558ade94ae05743062d92aacedef10e/format/Schema.fbs#L127-L138))
156    pub const MAP_ENTRIES_FIELD_DEFAULT_NAME: &'static str = "entries";
157    /// Default field name for the key field for Map
158    ///
159    /// See [Arrow Spec](https://github.com/apache/arrow/blob/b19c4761b558ade94ae05743062d92aacedef10e/format/Schema.fbs#L127-L138))
160    pub const MAP_KEY_FIELD_DEFAULT_NAME: &'static str = "key";
161    /// Default field name for the value field for Map
162    ///
163    /// See [Arrow Spec](https://github.com/apache/arrow/blob/b19c4761b558ade94ae05743062d92aacedef10e/format/Schema.fbs#L127-L138))
164    pub const MAP_VALUE_FIELD_DEFAULT_NAME: &'static str = "value";
165    /// Default field name for the run-ends field for RunEndEncoded
166    ///
167    /// See [Arrow Spec](https://arrow.apache.org/docs/format/Columnar.html#run-end-encoded-layout)
168    pub const REE_RUN_ENDS_FIELD_DEFAULT_NAME: &'static str = "run_ends";
169    /// Default field name for the values field for RunEndEncoded
170    ///
171    /// See [Arrow Spec](https://arrow.apache.org/docs/format/Columnar.html#run-end-encoded-layout)
172    pub const REE_VALUES_FIELD_DEFAULT_NAME: &'static str = "values";
173
174    /// Creates a new field with the given name, data type, and nullability
175    ///
176    /// # Example
177    /// ```
178    /// # use arrow_schema::{Field, DataType};
179    /// Field::new("field_name", DataType::Int32, true);
180    /// ```
181    pub fn new(name: impl Into<String>, data_type: DataType, nullable: bool) -> Self {
182        #[expect(deprecated)]
183        Field {
184            name: name.into(),
185            data_type,
186            nullable,
187            dict_id: 0,
188            dict_is_ordered: false,
189            metadata: Default::default(),
190        }
191    }
192
193    /// Creates a new `Field` suitable for [`DataType::List`] and
194    /// [`DataType::LargeList`]
195    ///
196    /// While not required, this method follows the convention of naming the
197    /// `Field` `"item"`.
198    ///
199    /// # Example
200    /// ```
201    /// # use arrow_schema::{Field, DataType};
202    /// assert_eq!(
203    ///   Field::new("item", DataType::Int32, true),
204    ///   Field::new_list_field(DataType::Int32, true)
205    /// );
206    /// ```
207    pub fn new_list_field(data_type: DataType, nullable: bool) -> Self {
208        Self::new(Self::LIST_FIELD_DEFAULT_NAME, data_type, nullable)
209    }
210
211    /// Creates a new field that has additional dictionary information
212    #[deprecated(
213        since = "54.0.0",
214        note = "The ability to preserve dictionary IDs will be removed. With the dict_id field disappearing this function signature will change by removing the dict_id parameter."
215    )]
216    pub fn new_dict(
217        name: impl Into<String>,
218        data_type: DataType,
219        nullable: bool,
220        dict_id: i64,
221        dict_is_ordered: bool,
222    ) -> Self {
223        #[expect(deprecated)]
224        Field {
225            name: name.into(),
226            data_type,
227            nullable,
228            dict_id,
229            dict_is_ordered,
230            metadata: Default::default(),
231        }
232    }
233
234    /// Create a new [`Field`] with [`DataType::Dictionary`]
235    ///
236    /// Use [`Self::new_dict`] for more advanced dictionary options
237    ///
238    /// # Panics
239    ///
240    /// Panics if [`!key.is_dictionary_key_type`][DataType::is_dictionary_key_type]
241    pub fn new_dictionary(
242        name: impl Into<String>,
243        key: DataType,
244        value: DataType,
245        nullable: bool,
246    ) -> Self {
247        assert!(
248            key.is_dictionary_key_type(),
249            "{key} is not a valid dictionary key"
250        );
251        let data_type = DataType::Dictionary(Box::new(key), Box::new(value));
252        Self::new(name, data_type, nullable)
253    }
254
255    /// Create a new [`Field`] with [`DataType::Struct`]
256    ///
257    /// - `name`: the name of the [`DataType::Struct`] field
258    /// - `fields`: the description of each struct element
259    /// - `nullable`: if the [`DataType::Struct`] array is nullable
260    pub fn new_struct(name: impl Into<String>, fields: impl Into<Fields>, nullable: bool) -> Self {
261        Self::new(name, DataType::Struct(fields.into()), nullable)
262    }
263
264    /// Create a new [`Field`] with [`DataType::List`]
265    ///
266    /// - `name`: the name of the [`DataType::List`] field
267    /// - `value`: the description of each list element
268    /// - `nullable`: if the [`DataType::List`] array is nullable
269    pub fn new_list(name: impl Into<String>, value: impl Into<FieldRef>, nullable: bool) -> Self {
270        Self::new(name, DataType::List(value.into()), nullable)
271    }
272
273    /// Create a new [`Field`] with [`DataType::LargeList`]
274    ///
275    /// - `name`: the name of the [`DataType::LargeList`] field
276    /// - `value`: the description of each list element
277    /// - `nullable`: if the [`DataType::LargeList`] array is nullable
278    pub fn new_large_list(
279        name: impl Into<String>,
280        value: impl Into<FieldRef>,
281        nullable: bool,
282    ) -> Self {
283        Self::new(name, DataType::LargeList(value.into()), nullable)
284    }
285
286    /// Create a new [`Field`] with [`DataType::FixedSizeList`]
287    ///
288    /// - `name`: the name of the [`DataType::FixedSizeList`] field
289    /// - `value`: the description of each list element
290    /// - `size`: the size of the fixed size list
291    /// - `nullable`: if the [`DataType::FixedSizeList`] array is nullable
292    pub fn new_fixed_size_list(
293        name: impl Into<String>,
294        value: impl Into<FieldRef>,
295        size: i32,
296        nullable: bool,
297    ) -> Self {
298        Self::new(name, DataType::FixedSizeList(value.into(), size), nullable)
299    }
300
301    /// Create a new [`Field`] with [`DataType::Map`]
302    ///
303    /// - `name`: the name of the [`DataType::Map`] field
304    /// - `entries`: the name of the inner [`DataType::Struct`] field
305    /// - `keys`: the map keys
306    /// - `values`: the map values
307    /// - `sorted`: if the [`DataType::Map`] array is sorted
308    /// - `nullable`: if the [`DataType::Map`] array is nullable
309    pub fn new_map(
310        name: impl Into<String>,
311        entries: impl Into<String>,
312        keys: impl Into<FieldRef>,
313        values: impl Into<FieldRef>,
314        sorted: bool,
315        nullable: bool,
316    ) -> Self {
317        let data_type = DataType::Map(
318            Arc::new(Field::new(
319                entries.into(),
320                DataType::Struct(Fields::from([keys.into(), values.into()])),
321                false, // The inner map field is always non-nullable (#1697),
322            )),
323            sorted,
324        );
325        Self::new(name, data_type, nullable)
326    }
327
328    /// Create a new [`Field`] with [`DataType::Union`]
329    ///
330    /// - `name`: the name of the [`DataType::Union`] field
331    /// - `type_ids`: the union type ids
332    /// - `fields`: the union fields
333    /// - `mode`: the union mode
334    ///
335    /// # Panics
336    ///
337    /// Panics if:
338    /// - any type ID is negative
339    /// - type IDs contain duplicates
340    /// - the number of type IDs does not equal the number of fields
341    pub fn new_union<S, F, T>(name: S, type_ids: T, fields: F, mode: UnionMode) -> Self
342    where
343        S: Into<String>,
344        F: IntoIterator,
345        F::Item: Into<FieldRef>,
346        T: IntoIterator<Item = i8>,
347    {
348        Self::new(
349            name,
350            DataType::Union(
351                UnionFields::try_new(type_ids, fields).expect("Invalid UnionField"),
352                mode,
353            ),
354            false, // Unions cannot be nullable
355        )
356    }
357
358    /// Sets the `Field`'s optional custom metadata.
359    #[inline]
360    pub fn set_metadata(&mut self, metadata: impl Into<Metadata>) {
361        self.metadata = metadata.into();
362    }
363
364    /// Sets the metadata of this `Field` to be `metadata` and returns self
365    pub fn with_metadata(mut self, metadata: impl Into<Metadata>) -> Self {
366        self.set_metadata(metadata);
367        self
368    }
369
370    /// Returns the immutable reference to the `Field`'s optional custom metadata.
371    #[inline]
372    pub const fn metadata(&self) -> &Metadata {
373        &self.metadata
374    }
375
376    /// Returns a mutable reference to the `Field`'s optional custom metadata.
377    #[inline]
378    pub fn metadata_mut(&mut self) -> &mut Metadata {
379        &mut self.metadata
380    }
381
382    /// Returns an immutable reference to the `Field`'s name.
383    #[inline]
384    pub const fn name(&self) -> &String {
385        &self.name
386    }
387
388    /// Set the name of this [`Field`]
389    #[inline]
390    pub fn set_name(&mut self, name: impl Into<String>) {
391        self.name = name.into();
392    }
393
394    /// Set the name of the [`Field`] and returns self.
395    ///
396    /// ```
397    /// # use arrow_schema::*;
398    /// let field = Field::new("c1", DataType::Int64, false)
399    ///    .with_name("c2");
400    ///
401    /// assert_eq!(field.name(), "c2");
402    /// ```
403    pub fn with_name(mut self, name: impl Into<String>) -> Self {
404        self.set_name(name);
405        self
406    }
407
408    /// Returns an immutable reference to the [`Field`]'s  [`DataType`].
409    #[inline]
410    pub const fn data_type(&self) -> &DataType {
411        &self.data_type
412    }
413
414    /// Set [`DataType`] of the [`Field`]
415    ///
416    /// ```
417    /// # use arrow_schema::*;
418    /// let mut field = Field::new("c1", DataType::Int64, false);
419    /// field.set_data_type(DataType::Utf8);
420    ///
421    /// assert_eq!(field.data_type(), &DataType::Utf8);
422    /// ```
423    #[inline]
424    pub fn set_data_type(&mut self, data_type: DataType) {
425        self.data_type = data_type;
426    }
427
428    /// Set [`DataType`] of the [`Field`] and returns self.
429    ///
430    /// ```
431    /// # use arrow_schema::*;
432    /// let field = Field::new("c1", DataType::Int64, false)
433    ///    .with_data_type(DataType::Utf8);
434    ///
435    /// assert_eq!(field.data_type(), &DataType::Utf8);
436    /// ```
437    pub fn with_data_type(mut self, data_type: DataType) -> Self {
438        self.set_data_type(data_type);
439        self
440    }
441
442    /// Returns the extension type name of this [`Field`], if set.
443    ///
444    /// This returns the value of [`EXTENSION_TYPE_NAME_KEY`], if set in
445    /// [`Field::metadata`]. If the key is missing, there is no extension type
446    /// name and this returns `None`.
447    ///
448    /// # Example
449    ///
450    /// ```
451    /// # use arrow_schema::{DataType, extension::EXTENSION_TYPE_NAME_KEY, Field};
452    ///
453    /// let field = Field::new("", DataType::Null, false);
454    /// assert_eq!(field.extension_type_name(), None);
455    ///
456    /// let field = Field::new("", DataType::Null, false)
457    ///     .with_metadata([(EXTENSION_TYPE_NAME_KEY, "example")]);
458    /// assert_eq!(field.extension_type_name(), Some("example"));
459    /// ```
460    pub fn extension_type_name(&self) -> Option<&str> {
461        self.metadata()
462            .get(EXTENSION_TYPE_NAME_KEY)
463            .map(String::as_ref)
464    }
465
466    /// Returns the extension type metadata of this [`Field`], if set.
467    ///
468    /// This returns the value of [`EXTENSION_TYPE_METADATA_KEY`], if set in
469    /// [`Field::metadata`]. If the key is missing, there is no extension type
470    /// metadata and this returns `None`.
471    ///
472    /// # Example
473    ///
474    /// ```
475    /// # use arrow_schema::{DataType, extension::EXTENSION_TYPE_METADATA_KEY, Field};
476    ///
477    /// let field = Field::new("", DataType::Null, false);
478    /// assert_eq!(field.extension_type_metadata(), None);
479    ///
480    /// let field = Field::new("", DataType::Null, false)
481    ///     .with_metadata([(EXTENSION_TYPE_METADATA_KEY, "example")]);
482    /// assert_eq!(field.extension_type_metadata(), Some("example"));
483    /// ```
484    pub fn extension_type_metadata(&self) -> Option<&str> {
485        self.metadata()
486            .get(EXTENSION_TYPE_METADATA_KEY)
487            .map(String::as_ref)
488    }
489
490    /// Returns `true` if this [`Field`] has the given [`ExtensionType`] name
491    /// and can be successfully validated as that extension type.
492    ///
493    /// This first checks the extension type name and only calls
494    /// [`ExtensionType::validate`] when the name matches.
495    ///
496    /// This is useful when you only need a boolean validity check and do not
497    /// need to retrieve the extension type instance.
498    #[inline]
499    pub fn has_valid_extension_type<E: ExtensionType>(&self) -> bool {
500        if self.extension_type_name() != Some(E::NAME) {
501            return false;
502        }
503
504        let ext_metadata = self
505            .metadata()
506            .get(EXTENSION_TYPE_METADATA_KEY)
507            .map(|s| s.as_str());
508
509        E::deserialize_metadata(ext_metadata)
510            .and_then(|metadata| E::validate(self.data_type(), metadata))
511            .is_ok()
512    }
513
514    /// Returns an instance of the given [`ExtensionType`] of this [`Field`],
515    /// if set in the [`Field::metadata`].
516    ///
517    /// Note that using `try_extension_type` with an extension type that does
518    /// not match the name in the metadata will return an `ArrowError` which can
519    /// be slow due to string allocations. If you only want to check if a
520    /// [`Field`] has a specific [`ExtensionType`], first check
521    /// [`Field::extension_type_name`], or use [`Field::has_valid_extension_type`]
522    /// to also validate metadata and data type.
523    ///
524    /// # Errors
525    ///
526    /// Returns an error if
527    /// - this field does not have the name of this extension type
528    ///   ([`ExtensionType::NAME`]) in the [`Field::metadata`] (mismatch or
529    ///   missing)
530    /// - the deserialization of the metadata
531    ///   ([`ExtensionType::deserialize_metadata`]) fails
532    /// - the construction of the extension type ([`ExtensionType::try_new`])
533    ///   fail (for example when the [`Field::data_type`] is not supported by
534    ///   the extension type ([`ExtensionType::supports_data_type`]))
535    ///
536    /// # Example: Check and retrieve an extension type
537    /// You can use this to check if a [`Field`] has a specific
538    /// [`ExtensionType`] and retrieve it:
539    /// ```
540    /// # use arrow_schema::{DataType, Field, ArrowError};
541    /// # use arrow_schema::extension::ExtensionType;
542    /// # struct MyExtensionType;
543    /// # impl ExtensionType for MyExtensionType {
544    /// # const NAME: &'static str = "my_extension";
545    /// # type Metadata = String;
546    /// # fn supports_data_type(&self, data_type: &DataType) -> Result<(), ArrowError> { Ok(()) }
547    /// # fn try_new(data_type: &DataType, metadata: Self::Metadata) -> Result<Self, ArrowError> { Ok(Self) }
548    /// # fn serialize_metadata(&self) -> Option<String> { unimplemented!() }
549    /// # fn deserialize_metadata(s: Option<&str>) -> Result<Self::Metadata, ArrowError> { unimplemented!() }
550    /// # fn metadata(&self) -> &<Self as ExtensionType>::Metadata { todo!() }
551    /// # }
552    /// # fn get_field() -> Field { Field::new("field", DataType::Null, false) }
553    /// let field = get_field();
554    /// if let Ok(extension_type) = field.try_extension_type::<MyExtensionType>() {
555    ///   // do something with extension_type
556    /// }
557    /// ```
558    pub fn try_extension_type<E: ExtensionType>(&self) -> Result<E, ArrowError> {
559        E::try_new_from_field_metadata(self.data_type(), self.metadata())
560    }
561
562    /// Returns an instance of the given [`ExtensionType`] of this [`Field`],
563    /// panics if this [`Field`] does not have this extension type.
564    ///
565    /// # Panics
566    ///
567    /// This calls [`Field::try_extension_type`] and panics when it returns an
568    /// error.
569    pub fn extension_type<E: ExtensionType>(&self) -> E {
570        self.try_extension_type::<E>()
571            .unwrap_or_else(|e| panic!("{e}"))
572    }
573
574    /// Updates the metadata of this [`Field`] with the [`ExtensionType::NAME`]
575    /// and [`ExtensionType::metadata`] of the given [`ExtensionType`], if the
576    /// given extension type supports the [`Field::data_type`] of this field
577    /// ([`ExtensionType::supports_data_type`]).
578    ///
579    /// If the given extension type defines no metadata, a previously set
580    /// value of [`EXTENSION_TYPE_METADATA_KEY`] is cleared.
581    ///
582    /// # Error
583    ///
584    /// This functions returns an error if the data type of this field does not
585    /// match any of the supported storage types of the given extension type.
586    pub fn try_with_extension_type<E: ExtensionType>(
587        &mut self,
588        extension_type: E,
589    ) -> Result<(), ArrowError> {
590        // Make sure the data type of this field is supported
591        extension_type.supports_data_type(&self.data_type)?;
592
593        self.metadata
594            .insert(EXTENSION_TYPE_NAME_KEY.to_owned(), E::NAME.to_owned());
595        match extension_type.serialize_metadata() {
596            Some(metadata) => self
597                .metadata
598                .insert(EXTENSION_TYPE_METADATA_KEY.to_owned(), metadata),
599            // If this extension type has no metadata, we make sure to
600            // clear previously set metadata.
601            None => self.metadata.remove(EXTENSION_TYPE_METADATA_KEY),
602        };
603
604        Ok(())
605    }
606
607    /// Updates the metadata of this [`Field`] with the [`ExtensionType::NAME`]
608    /// and [`ExtensionType::metadata`] of the given [`ExtensionType`].
609    ///
610    /// # Panics
611    ///
612    /// This calls [`Field::try_with_extension_type`] and panics when it
613    /// returns an error.
614    pub fn with_extension_type<E: ExtensionType>(mut self, extension_type: E) -> Self {
615        self.try_with_extension_type(extension_type)
616            .unwrap_or_else(|e| panic!("{e}"));
617        self
618    }
619
620    /// Returns the [`CanonicalExtensionType`] of this [`Field`], if set.
621    ///
622    /// # Error
623    ///
624    /// Returns an error if
625    /// - this field does not have a canonical extension type (mismatch or missing)
626    /// - the canonical extension is not supported
627    /// - the construction of the extension type fails
628    #[cfg(feature = "canonical_extension_types")]
629    pub fn try_canonical_extension_type(&self) -> Result<CanonicalExtensionType, ArrowError> {
630        CanonicalExtensionType::try_from(self)
631    }
632
633    /// Indicates whether this [`Field`] supports null values.
634    ///
635    /// If true, the field *may* contain null values.
636    #[inline]
637    pub const fn is_nullable(&self) -> bool {
638        self.nullable
639    }
640
641    /// Set the `nullable` of this [`Field`].
642    ///
643    /// ```
644    /// # use arrow_schema::*;
645    /// let mut field = Field::new("c1", DataType::Int64, false);
646    /// field.set_nullable(true);
647    ///
648    /// assert_eq!(field.is_nullable(), true);
649    /// ```
650    #[inline]
651    pub fn set_nullable(&mut self, nullable: bool) {
652        self.nullable = nullable;
653    }
654
655    /// Set `nullable` of the [`Field`] and returns self.
656    ///
657    /// ```
658    /// # use arrow_schema::*;
659    /// let field = Field::new("c1", DataType::Int64, false)
660    ///    .with_nullable(true);
661    ///
662    /// assert_eq!(field.is_nullable(), true);
663    /// ```
664    pub fn with_nullable(mut self, nullable: bool) -> Self {
665        self.set_nullable(nullable);
666        self
667    }
668
669    /// Returns a (flattened) [`Vec`] containing all child [`Field`]s
670    /// within `self` contained within this field (including `self`)
671    pub(crate) fn fields(&self) -> Vec<&Field> {
672        let mut collected_fields = vec![self];
673        collected_fields.append(&mut Field::_fields(&self.data_type));
674
675        collected_fields
676    }
677
678    fn _fields(dt: &DataType) -> Vec<&Field> {
679        match dt {
680            DataType::Struct(fields) => fields.iter().flat_map(|f| f.fields()).collect(),
681            DataType::Union(fields, _) => fields.iter().flat_map(|(_, f)| f.fields()).collect(),
682            DataType::List(field)
683            | DataType::LargeList(field)
684            | DataType::ListView(field)
685            | DataType::LargeListView(field)
686            | DataType::FixedSizeList(field, _)
687            | DataType::Map(field, _) => field.fields(),
688            DataType::Dictionary(_, value_field) => Field::_fields(value_field.as_ref()),
689            DataType::RunEndEncoded(_, field) => field.fields(),
690            _ => vec![],
691        }
692    }
693
694    /// Returns a vector containing all (potentially nested) `Field` instances selected by the
695    /// dictionary ID they use
696    #[inline]
697    #[deprecated(
698        since = "54.0.0",
699        note = "The ability to preserve dictionary IDs will be removed. With it, all fields related to it."
700    )]
701    pub(crate) fn fields_with_dict_id(&self, id: i64) -> Vec<&Field> {
702        self.fields()
703            .into_iter()
704            .filter(|&field| {
705                #[expect(deprecated)]
706                let matching_dict_id = field.dict_id == id;
707                matches!(field.data_type(), DataType::Dictionary(_, _)) && matching_dict_id
708            })
709            .collect()
710    }
711
712    /// Returns the dictionary ID, if this is a dictionary type.
713    #[inline]
714    #[deprecated(
715        since = "54.0.0",
716        note = "The ability to preserve dictionary IDs will be removed. With it, all fields related to it."
717    )]
718    pub const fn dict_id(&self) -> Option<i64> {
719        match self.data_type {
720            #[expect(deprecated)]
721            DataType::Dictionary(_, _) => Some(self.dict_id),
722            _ => None,
723        }
724    }
725
726    /// Returns whether this `Field`'s dictionary is ordered, if this is a dictionary type.
727    ///
728    /// # Example
729    /// ```
730    /// # use arrow_schema::{DataType, Field};
731    /// // non dictionaries do not have a dict is ordered flat
732    /// let field = Field::new("c1", DataType::Int64, false);
733    /// assert_eq!(field.dict_is_ordered(), None);
734    /// // by default dictionary is not ordered
735    /// let field = Field::new("c1", DataType::Dictionary(Box::new(DataType::Int64), Box::new(DataType::Utf8)), false);
736    /// assert_eq!(field.dict_is_ordered(), Some(false));
737    /// let field = field.with_dict_is_ordered(true);
738    /// assert_eq!(field.dict_is_ordered(), Some(true));
739    /// ```
740    #[inline]
741    pub const fn dict_is_ordered(&self) -> Option<bool> {
742        match self.data_type {
743            DataType::Dictionary(_, _) => Some(self.dict_is_ordered),
744            _ => None,
745        }
746    }
747
748    /// Set the is ordered field for this `Field`, if it is a dictionary.
749    ///
750    /// Does nothing if this is not a dictionary type.
751    ///
752    /// See [`Field::dict_is_ordered`] for more information.
753    pub fn with_dict_is_ordered(mut self, dict_is_ordered: bool) -> Self {
754        if matches!(self.data_type, DataType::Dictionary(_, _)) {
755            self.dict_is_ordered = dict_is_ordered;
756        }
757        self
758    }
759
760    /// Merge this field into self if it is compatible.
761    ///
762    /// Struct fields are merged recursively.
763    ///
764    /// NOTE: `self` may be updated to a partial / unexpected state in case of merge failure.
765    ///
766    /// Example:
767    ///
768    /// ```
769    /// # use arrow_schema::*;
770    /// let mut field = Field::new("c1", DataType::Int64, false);
771    /// assert!(field.try_merge(&Field::new("c1", DataType::Int64, true)).is_ok());
772    /// assert!(field.is_nullable());
773    /// ```
774    pub fn try_merge(&mut self, from: &Field) -> Result<(), ArrowError> {
775        if from.dict_is_ordered != self.dict_is_ordered {
776            return Err(ArrowError::SchemaError(format!(
777                "Fail to merge schema field '{}' because from dict_is_ordered = {} does not match {}",
778                self.name, from.dict_is_ordered, self.dict_is_ordered
779            )));
780        }
781        // merge metadata
782        match (self.metadata().is_empty(), from.metadata().is_empty()) {
783            (false, false) => {
784                let mut merged = self.metadata().clone();
785                for (key, from_value) in from.metadata() {
786                    if let Some(self_value) = self.metadata.get(key) {
787                        if self_value != from_value {
788                            return Err(ArrowError::SchemaError(format!(
789                                "Fail to merge field '{}' due to conflicting metadata data value for key {}.
790                                    From value = {} does not match {}", self.name, key, from_value, self_value),
791                            ));
792                        }
793                    } else {
794                        merged.insert(key.clone(), from_value.clone());
795                    }
796                }
797                self.set_metadata(merged);
798            }
799            (true, false) => {
800                self.set_metadata(from.metadata().clone());
801            }
802            _ => {}
803        }
804        match &mut self.data_type {
805            DataType::Struct(nested_fields) => match &from.data_type {
806                DataType::Struct(from_nested_fields) => {
807                    let mut builder = SchemaBuilder::new();
808                    nested_fields
809                        .iter()
810                        .chain(from_nested_fields)
811                        .try_for_each(|f| builder.try_merge(f))?;
812                    *nested_fields = builder.finish().fields;
813                }
814                DataType::Null => {
815                    self.nullable = true;
816                }
817                _ => {
818                    return Err(ArrowError::SchemaError(format!(
819                        "Fail to merge schema field '{}' because the from data_type = {} is not DataType::Struct",
820                        self.name, from.data_type
821                    )));
822                }
823            },
824            DataType::Union(nested_fields, mode) => match &from.data_type {
825                DataType::Union(from_nested_fields, from_mode) => {
826                    if mode != from_mode {
827                        return Err(ArrowError::SchemaError(format!(
828                            "Fail to merge schema field '{}' because the from union mode = {:?} does not match {:?}",
829                            self.name, from_mode, mode
830                        )));
831                    }
832                    nested_fields.try_merge(from_nested_fields)?;
833                }
834                DataType::Null => {
835                    self.nullable = true;
836                }
837                _ => {
838                    return Err(ArrowError::SchemaError(format!(
839                        "Fail to merge schema field '{}' because the from data_type = {} is not DataType::Union",
840                        self.name, from.data_type
841                    )));
842                }
843            },
844            DataType::List(field) => match &from.data_type {
845                DataType::List(from_field) => {
846                    let mut f = (**field).clone();
847                    f.try_merge(from_field)?;
848                    (*field) = Arc::new(f);
849                }
850                DataType::Null => {
851                    self.nullable = true;
852                }
853                _ => {
854                    return Err(ArrowError::SchemaError(format!(
855                        "Fail to merge schema field '{}' because the from data_type = {} is not DataType::List",
856                        self.name, from.data_type
857                    )));
858                }
859            },
860            DataType::LargeList(field) => match &from.data_type {
861                DataType::LargeList(from_field) => {
862                    let mut f = (**field).clone();
863                    f.try_merge(from_field)?;
864                    (*field) = Arc::new(f);
865                }
866                DataType::Null => {
867                    self.nullable = true;
868                }
869                _ => {
870                    return Err(ArrowError::SchemaError(format!(
871                        "Fail to merge schema field '{}' because the from data_type = {} is not DataType::LargeList",
872                        self.name, from.data_type
873                    )));
874                }
875            },
876            DataType::Null => {
877                self.nullable = true;
878                self.data_type = from.data_type.clone();
879            }
880            DataType::Boolean
881            | DataType::Int8
882            | DataType::Int16
883            | DataType::Int32
884            | DataType::Int64
885            | DataType::UInt8
886            | DataType::UInt16
887            | DataType::UInt32
888            | DataType::UInt64
889            | DataType::Float16
890            | DataType::Float32
891            | DataType::Float64
892            | DataType::Timestamp(_, _)
893            | DataType::Date32
894            | DataType::Date64
895            | DataType::Time32(_)
896            | DataType::Time64(_)
897            | DataType::Duration(_)
898            | DataType::Binary
899            | DataType::LargeBinary
900            | DataType::BinaryView
901            | DataType::Interval(_)
902            | DataType::LargeListView(_)
903            | DataType::ListView(_)
904            | DataType::Map(_, _)
905            | DataType::Dictionary(_, _)
906            | DataType::RunEndEncoded(_, _)
907            | DataType::FixedSizeList(_, _)
908            | DataType::FixedSizeBinary(_)
909            | DataType::Utf8
910            | DataType::LargeUtf8
911            | DataType::Utf8View
912            | DataType::Decimal32(_, _)
913            | DataType::Decimal64(_, _)
914            | DataType::Decimal128(_, _)
915            | DataType::Decimal256(_, _) => {
916                if from.data_type == DataType::Null {
917                    self.nullable = true;
918                } else if self.data_type != from.data_type {
919                    return Err(ArrowError::SchemaError(format!(
920                        "Fail to merge schema field '{}' because the from data_type = {} does not equal {}",
921                        self.name, from.data_type, self.data_type
922                    )));
923                }
924            }
925        }
926        self.nullable |= from.nullable;
927
928        Ok(())
929    }
930
931    /// Check to see if `self` is a superset of `other` field. Superset is defined as:
932    ///
933    /// * if nullability doesn't match, self needs to be nullable
934    /// * self.metadata is a superset of other.metadata
935    /// * all other fields are equal
936    pub fn contains(&self, other: &Field) -> bool {
937        self.name == other.name
938        && self.data_type.contains(&other.data_type)
939        && self.dict_is_ordered == other.dict_is_ordered
940        // self need to be nullable or both of them are not nullable
941        && (self.nullable || !other.nullable)
942        // make sure self.metadata is a superset of other.metadata
943        && other.metadata.iter().all(|(k, v1)| {
944            self.metadata.get(k).is_some_and(|v2| v1 == v2)
945        })
946    }
947
948    /// Return size of this instance in bytes.
949    ///
950    /// Includes the size of `Self`.
951    pub fn size(&self) -> usize {
952        std::mem::size_of_val(self) - std::mem::size_of_val(&self.data_type)
953            + self.data_type.size()
954            + self.name.capacity()
955            + (std::mem::size_of::<(String, String)>() * self.metadata.len())
956            + self
957                .metadata
958                .iter()
959                .map(|(k, v)| k.capacity() + v.capacity())
960                .sum::<usize>()
961    }
962}
963
964impl std::fmt::Display for Field {
965    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
966        #![expect(deprecated)] // Must still print dict_id, if set
967        let Self {
968            name,
969            data_type,
970            nullable,
971            dict_id,
972            dict_is_ordered,
973            metadata,
974        } = self;
975        let maybe_nullable = if *nullable { "nullable " } else { "" };
976        let metadata_str = if metadata.is_empty() {
977            String::new()
978        } else {
979            format!(", metadata: {metadata:?}")
980        };
981        let dict_id_str = if dict_id == &0 {
982            String::new()
983        } else {
984            format!(", dict_id: {dict_id}")
985        };
986        let dict_is_ordered_str = if *dict_is_ordered {
987            ", dict_is_ordered"
988        } else {
989            ""
990        };
991        write!(
992            f,
993            "Field {{ {name:?}: {maybe_nullable}{data_type}{dict_id_str}{dict_is_ordered_str}{metadata_str} }}"
994        )
995    }
996}
997
998#[cfg(test)]
999mod test {
1000    use super::*;
1001    use std::collections::HashMap;
1002    use std::collections::hash_map::DefaultHasher;
1003
1004    #[derive(Debug, Clone, Copy)]
1005    struct TestExtensionType;
1006
1007    impl ExtensionType for TestExtensionType {
1008        const NAME: &'static str = "test.extension";
1009        type Metadata = ();
1010
1011        fn metadata(&self) -> &Self::Metadata {
1012            &()
1013        }
1014
1015        fn serialize_metadata(&self) -> Option<String> {
1016            None
1017        }
1018
1019        fn deserialize_metadata(metadata: Option<&str>) -> Result<Self::Metadata, ArrowError> {
1020            metadata.map_or(Ok(()), |_| {
1021                Err(ArrowError::InvalidArgumentError(
1022                    "TestExtensionType expects no metadata".to_owned(),
1023                ))
1024            })
1025        }
1026
1027        fn supports_data_type(&self, _data_type: &DataType) -> Result<(), ArrowError> {
1028            Ok(())
1029        }
1030
1031        fn try_new(_data_type: &DataType, _metadata: Self::Metadata) -> Result<Self, ArrowError> {
1032            Ok(Self)
1033        }
1034    }
1035
1036    #[test]
1037    fn test_has_valid_extension_type() {
1038        let no_extension = Field::new("f", DataType::Null, false);
1039        assert!(!no_extension.has_valid_extension_type::<TestExtensionType>());
1040
1041        let matching_name = Field::new("f", DataType::Null, false)
1042            .with_metadata([(EXTENSION_TYPE_NAME_KEY, TestExtensionType::NAME)]);
1043        assert!(matching_name.has_valid_extension_type::<TestExtensionType>());
1044
1045        let matching_name_with_invalid_metadata = Field::new("f", DataType::Null, false)
1046            .with_metadata([
1047                (EXTENSION_TYPE_NAME_KEY, TestExtensionType::NAME),
1048                (EXTENSION_TYPE_METADATA_KEY, "invalid"),
1049            ]);
1050        assert!(
1051            !matching_name_with_invalid_metadata.has_valid_extension_type::<TestExtensionType>()
1052        );
1053
1054        let different_name = Field::new("f", DataType::Null, false)
1055            .with_metadata([(EXTENSION_TYPE_NAME_KEY, "some.other_extension")]);
1056        assert!(!different_name.has_valid_extension_type::<TestExtensionType>());
1057    }
1058
1059    #[test]
1060    fn test_new_with_string() {
1061        // Fields should allow owned Strings to support reuse
1062        let s = "c1";
1063        Field::new(s, DataType::Int64, false);
1064    }
1065
1066    #[test]
1067    fn test_new_dict_with_string() {
1068        // Fields should allow owned Strings to support reuse
1069        let s = "c1";
1070        #[expect(deprecated)]
1071        Field::new_dict(s, DataType::Int64, false, 4, false);
1072    }
1073
1074    #[test]
1075    #[cfg_attr(miri, ignore)] // fork is not supported
1076    fn test_debug_format_field() {
1077        // Make sure the `Debug` formatting of `Field` is readable and not too long
1078        insta::assert_debug_snapshot!(Field::new("item", DataType::UInt8, false), @r"
1079        Field {
1080            data_type: UInt8,
1081        }
1082        ");
1083        insta::assert_debug_snapshot!(Field::new("column", DataType::LargeUtf8, true), @r#"
1084        Field {
1085            name: "column",
1086            data_type: LargeUtf8,
1087            nullable: true,
1088        }
1089        "#);
1090    }
1091
1092    #[test]
1093    fn test_merge_union_modes() {
1094        for mode in [UnionMode::Sparse, UnionMode::Dense] {
1095            let mut field = Field::new_union(
1096                "u",
1097                [0],
1098                [Field::new("number", DataType::Int32, false)],
1099                mode,
1100            );
1101            let other_mode = match mode {
1102                UnionMode::Sparse => UnionMode::Dense,
1103                UnionMode::Dense => UnionMode::Sparse,
1104            };
1105            let incompatible = Field::new_union(
1106                "u",
1107                [0],
1108                [Field::new("number", DataType::Int32, false)],
1109                other_mode,
1110            );
1111            assert!(matches!(
1112                field.try_merge(&incompatible),
1113                Err(ArrowError::SchemaError(_))
1114            ));
1115
1116            let compatible =
1117                Field::new_union("u", [1], [Field::new("text", DataType::Utf8, false)], mode);
1118            field.try_merge(&compatible).unwrap();
1119            let expected = Field::new_union(
1120                "u",
1121                [0, 1],
1122                [
1123                    Field::new("number", DataType::Int32, false),
1124                    Field::new("text", DataType::Utf8, false),
1125                ],
1126                mode,
1127            );
1128            assert_eq!(field, expected);
1129        }
1130    }
1131
1132    #[test]
1133    fn test_merge_incompatible_types() {
1134        let mut field = Field::new("c1", DataType::Int64, false);
1135        let result = field
1136            .try_merge(&Field::new("c1", DataType::Float32, true))
1137            .expect_err("should fail")
1138            .to_string();
1139        assert_eq!(
1140            "Schema error: Fail to merge schema field 'c1' because the from data_type = Float32 does not equal Int64",
1141            result
1142        );
1143    }
1144
1145    #[test]
1146    fn test_merge_with_null() {
1147        let mut field1 = Field::new("c1", DataType::Null, true);
1148        field1
1149            .try_merge(&Field::new("c1", DataType::Float32, false))
1150            .expect("should widen type to nullable float");
1151        assert_eq!(Field::new("c1", DataType::Float32, true), field1);
1152
1153        let mut field2 = Field::new("c2", DataType::Utf8, false);
1154        field2
1155            .try_merge(&Field::new("c2", DataType::Null, true))
1156            .expect("should widen type to nullable utf8");
1157        assert_eq!(Field::new("c2", DataType::Utf8, true), field2);
1158    }
1159
1160    #[test]
1161    fn test_merge_with_nested_null() {
1162        let mut struct1 = Field::new(
1163            "s1",
1164            DataType::Struct(Fields::from(vec![Field::new(
1165                "inner",
1166                DataType::Float32,
1167                false,
1168            )])),
1169            false,
1170        );
1171
1172        let struct2 = Field::new(
1173            "s2",
1174            DataType::Struct(Fields::from(vec![Field::new(
1175                "inner",
1176                DataType::Null,
1177                false,
1178            )])),
1179            true,
1180        );
1181
1182        struct1
1183            .try_merge(&struct2)
1184            .expect("should widen inner field's type to nullable float");
1185        assert_eq!(
1186            Field::new(
1187                "s1",
1188                DataType::Struct(Fields::from(vec![Field::new(
1189                    "inner",
1190                    DataType::Float32,
1191                    true,
1192                )])),
1193                true,
1194            ),
1195            struct1
1196        );
1197
1198        let mut list1 = Field::new(
1199            "l1",
1200            DataType::List(Field::new("inner", DataType::Float32, false).into()),
1201            false,
1202        );
1203
1204        let list2 = Field::new(
1205            "l2",
1206            DataType::List(Field::new("inner", DataType::Null, false).into()),
1207            true,
1208        );
1209
1210        list1
1211            .try_merge(&list2)
1212            .expect("should widen inner field's type to nullable float");
1213        assert_eq!(
1214            Field::new(
1215                "l1",
1216                DataType::List(Field::new("inner", DataType::Float32, true).into()),
1217                true,
1218            ),
1219            list1
1220        );
1221
1222        let mut large_list1 = Field::new(
1223            "ll1",
1224            DataType::LargeList(Field::new("inner", DataType::Float32, false).into()),
1225            false,
1226        );
1227
1228        let large_list2 = Field::new(
1229            "ll2",
1230            DataType::LargeList(Field::new("inner", DataType::Null, false).into()),
1231            true,
1232        );
1233
1234        large_list1
1235            .try_merge(&large_list2)
1236            .expect("should widen inner field's type to nullable float");
1237        assert_eq!(
1238            Field::new(
1239                "ll1",
1240                DataType::LargeList(Field::new("inner", DataType::Float32, true).into()),
1241                true,
1242            ),
1243            large_list1
1244        );
1245    }
1246
1247    #[test]
1248    fn test_fields_with_dict_id() {
1249        #[expect(deprecated)]
1250        let dict1 = Field::new_dict(
1251            "dict1",
1252            DataType::Dictionary(DataType::Utf8.into(), DataType::Int32.into()),
1253            false,
1254            10,
1255            false,
1256        );
1257        #[expect(deprecated)]
1258        let dict2 = Field::new_dict(
1259            "dict2",
1260            DataType::Dictionary(DataType::Int32.into(), DataType::Int8.into()),
1261            false,
1262            20,
1263            false,
1264        );
1265
1266        let field = Field::new(
1267            "struct<dict1, list[struct<dict2, list[struct<dict1]>]>",
1268            DataType::Struct(Fields::from(vec![
1269                dict1.clone(),
1270                Field::new(
1271                    "list[struct<dict1, list[struct<dict2>]>]",
1272                    DataType::List(Arc::new(Field::new(
1273                        "struct<dict1, list[struct<dict2>]>",
1274                        DataType::Struct(Fields::from(vec![
1275                            dict1.clone(),
1276                            Field::new(
1277                                "list[struct<dict2>]",
1278                                DataType::List(Arc::new(Field::new(
1279                                    "struct<dict2>",
1280                                    DataType::Struct(vec![dict2.clone()].into()),
1281                                    false,
1282                                ))),
1283                                false,
1284                            ),
1285                        ])),
1286                        false,
1287                    ))),
1288                    false,
1289                ),
1290            ])),
1291            false,
1292        );
1293
1294        #[expect(deprecated)]
1295        for field in field.fields_with_dict_id(10) {
1296            assert_eq!(dict1, *field);
1297        }
1298        #[expect(deprecated)]
1299        for field in field.fields_with_dict_id(20) {
1300            assert_eq!(dict2, *field);
1301        }
1302    }
1303
1304    fn get_field_hash(field: &Field) -> u64 {
1305        let mut s = DefaultHasher::new();
1306        field.hash(&mut s);
1307        s.finish()
1308    }
1309
1310    #[test]
1311    fn test_field_comparison_case() {
1312        // dictionary-encoding properties not used for field comparison
1313        #[expect(deprecated)]
1314        let dict1 = Field::new_dict(
1315            "dict1",
1316            DataType::Dictionary(DataType::Utf8.into(), DataType::Int32.into()),
1317            false,
1318            10,
1319            false,
1320        );
1321        #[expect(deprecated)]
1322        let dict2 = Field::new_dict(
1323            "dict1",
1324            DataType::Dictionary(DataType::Utf8.into(), DataType::Int32.into()),
1325            false,
1326            20,
1327            false,
1328        );
1329
1330        assert_eq!(dict1, dict2);
1331        assert_eq!(get_field_hash(&dict1), get_field_hash(&dict2));
1332
1333        #[expect(deprecated)]
1334        let dict1 = Field::new_dict(
1335            "dict0",
1336            DataType::Dictionary(DataType::Utf8.into(), DataType::Int32.into()),
1337            false,
1338            10,
1339            false,
1340        );
1341
1342        assert_ne!(dict1, dict2);
1343        assert_ne!(get_field_hash(&dict1), get_field_hash(&dict2));
1344    }
1345
1346    #[test]
1347    fn test_field_comparison_metadata() {
1348        let f1 = Field::new("x", DataType::Binary, false).with_metadata(HashMap::from([
1349            (String::from("k1"), String::from("v1")),
1350            (String::from("k2"), String::from("v2")),
1351        ]));
1352        let f2 = Field::new("x", DataType::Binary, false).with_metadata(HashMap::from([
1353            (String::from("k1"), String::from("v1")),
1354            (String::from("k3"), String::from("v3")),
1355        ]));
1356        let f3 = Field::new("x", DataType::Binary, false).with_metadata(HashMap::from([
1357            (String::from("k1"), String::from("v1")),
1358            (String::from("k3"), String::from("v4")),
1359        ]));
1360
1361        assert!(f1.cmp(&f2).is_lt());
1362        assert!(f2.cmp(&f3).is_lt());
1363        assert!(f1.cmp(&f3).is_lt());
1364    }
1365
1366    #[test]
1367    #[expect(clippy::needless_borrows_for_generic_args)] // intentional to exercise various references
1368    fn test_field_as_ref() {
1369        let field = || Field::new("x", DataType::Binary, false);
1370
1371        // AsRef can be used in a function accepting a field.
1372        // However, this case actually works a bit better when function takes `&Field`
1373        fn accept_ref(_: impl AsRef<Field>) {}
1374
1375        accept_ref(field());
1376        accept_ref(&field());
1377        accept_ref(&&field());
1378        accept_ref(Arc::new(field()));
1379        accept_ref(&Arc::new(field()));
1380        accept_ref(&&Arc::new(field()));
1381
1382        // AsRef can be used in a function accepting a collection of fields in any form,
1383        // such as &[Field], or &[Arc<Field>]
1384        fn accept_refs(_: impl IntoIterator<Item: AsRef<Field>>) {}
1385
1386        accept_refs(vec![field()]);
1387        accept_refs(vec![&field()]);
1388        accept_refs(vec![Arc::new(field())]);
1389        accept_refs(vec![&Arc::new(field())]);
1390        accept_refs(&vec![field()]);
1391        accept_refs(&vec![&field()]);
1392        accept_refs(&vec![Arc::new(field())]);
1393        accept_refs(&vec![&Arc::new(field())]);
1394    }
1395
1396    #[test]
1397    fn test_contains_reflexivity() {
1398        let mut field = Field::new("field1", DataType::Float16, false);
1399        field.set_metadata(HashMap::from([
1400            (String::from("k0"), String::from("v0")),
1401            (String::from("k1"), String::from("v1")),
1402        ]));
1403        assert!(field.contains(&field))
1404    }
1405
1406    #[test]
1407    fn test_contains_transitivity() {
1408        let child_field = Field::new("child1", DataType::Float16, false);
1409
1410        let mut field1 = Field::new(
1411            "field1",
1412            DataType::Struct(Fields::from(vec![child_field])),
1413            false,
1414        );
1415        field1.set_metadata(HashMap::from([(String::from("k1"), String::from("v1"))]));
1416
1417        let mut field2 = Field::new("field1", DataType::Struct(Fields::default()), true);
1418        field2.set_metadata(HashMap::from([(String::from("k2"), String::from("v2"))]));
1419        field2.try_merge(&field1).unwrap();
1420
1421        let mut field3 = Field::new("field1", DataType::Struct(Fields::default()), false);
1422        field3.set_metadata(HashMap::from([(String::from("k3"), String::from("v3"))]));
1423        field3.try_merge(&field2).unwrap();
1424
1425        assert!(field2.contains(&field1));
1426        assert!(field3.contains(&field2));
1427        assert!(field3.contains(&field1));
1428
1429        assert!(!field1.contains(&field2));
1430        assert!(!field1.contains(&field3));
1431        assert!(!field2.contains(&field3));
1432    }
1433
1434    #[test]
1435    fn test_contains_nullable() {
1436        let field1 = Field::new("field1", DataType::Boolean, true);
1437        let field2 = Field::new("field1", DataType::Boolean, false);
1438        assert!(field1.contains(&field2));
1439        assert!(!field2.contains(&field1));
1440    }
1441
1442    #[test]
1443    fn test_contains_must_have_same_fields() {
1444        let child_field1 = Field::new("child1", DataType::Float16, false);
1445        let child_field2 = Field::new("child2", DataType::Float16, false);
1446
1447        let field1 = Field::new(
1448            "field1",
1449            DataType::Struct(vec![child_field1.clone()].into()),
1450            true,
1451        );
1452        let field2 = Field::new(
1453            "field1",
1454            DataType::Struct(vec![child_field1, child_field2].into()),
1455            true,
1456        );
1457
1458        assert!(!field1.contains(&field2));
1459        assert!(!field2.contains(&field1));
1460
1461        // UnionFields with different type ID
1462        let field1 = Field::new(
1463            "field1",
1464            DataType::Union(
1465                UnionFields::try_new(
1466                    vec![1, 2],
1467                    vec![
1468                        Field::new("field1", DataType::UInt8, true),
1469                        Field::new("field3", DataType::Utf8, false),
1470                    ],
1471                )
1472                .unwrap(),
1473                UnionMode::Dense,
1474            ),
1475            true,
1476        );
1477        let field2 = Field::new(
1478            "field1",
1479            DataType::Union(
1480                UnionFields::try_new(
1481                    vec![1, 3],
1482                    vec![
1483                        Field::new("field1", DataType::UInt8, false),
1484                        Field::new("field3", DataType::Utf8, false),
1485                    ],
1486                )
1487                .unwrap(),
1488                UnionMode::Dense,
1489            ),
1490            true,
1491        );
1492        assert!(!field1.contains(&field2));
1493
1494        // UnionFields with same type ID
1495        let field1 = Field::new(
1496            "field1",
1497            DataType::Union(
1498                UnionFields::try_new(
1499                    vec![1, 2],
1500                    vec![
1501                        Field::new("field1", DataType::UInt8, true),
1502                        Field::new("field3", DataType::Utf8, false),
1503                    ],
1504                )
1505                .unwrap(),
1506                UnionMode::Dense,
1507            ),
1508            true,
1509        );
1510        let field2 = Field::new(
1511            "field1",
1512            DataType::Union(
1513                UnionFields::try_new(
1514                    vec![1, 2],
1515                    vec![
1516                        Field::new("field1", DataType::UInt8, false),
1517                        Field::new("field3", DataType::Utf8, false),
1518                    ],
1519                )
1520                .unwrap(),
1521                UnionMode::Dense,
1522            ),
1523            true,
1524        );
1525        assert!(field1.contains(&field2));
1526    }
1527
1528    #[cfg(feature = "serde")]
1529    fn assert_binary_serde_round_trip(field: Field) {
1530        let serialized = postcard::to_stdvec(&field).unwrap();
1531        let deserialized: Field = postcard::from_bytes(&serialized).unwrap();
1532        assert_eq!(field, deserialized)
1533    }
1534
1535    #[cfg(feature = "serde")]
1536    #[test]
1537    fn test_field_without_metadata_serde() {
1538        let field = Field::new("name", DataType::Boolean, true);
1539        assert_binary_serde_round_trip(field)
1540    }
1541
1542    #[cfg(feature = "serde")]
1543    #[test]
1544    fn test_field_with_empty_metadata_serde() {
1545        let field = Field::new("name", DataType::Boolean, false).with_metadata(HashMap::new());
1546
1547        assert_binary_serde_round_trip(field)
1548    }
1549
1550    #[cfg(feature = "serde")]
1551    #[test]
1552    fn test_field_with_nonempty_metadata_serde() {
1553        let mut metadata = HashMap::new();
1554        metadata.insert("hi".to_owned(), String::new());
1555        let field = Field::new("name", DataType::Boolean, false).with_metadata(metadata);
1556
1557        assert_binary_serde_round_trip(field)
1558    }
1559
1560    #[test]
1561    fn test_merge_compound_with_null() {
1562        // Struct + Null
1563        let mut field = Field::new(
1564            "s",
1565            DataType::Struct(Fields::from(vec![Field::new("a", DataType::Int32, false)])),
1566            false,
1567        );
1568        field
1569            .try_merge(&Field::new("s", DataType::Null, true))
1570            .expect("Struct should merge with Null");
1571        assert!(field.is_nullable());
1572        assert!(matches!(field.data_type(), DataType::Struct(_)));
1573
1574        // List + Null
1575        let mut field = Field::new(
1576            "l",
1577            DataType::List(Field::new("item", DataType::Utf8, false).into()),
1578            false,
1579        );
1580        field
1581            .try_merge(&Field::new("l", DataType::Null, true))
1582            .expect("List should merge with Null");
1583        assert!(field.is_nullable());
1584        assert!(matches!(field.data_type(), DataType::List(_)));
1585
1586        // LargeList + Null
1587        let mut field = Field::new(
1588            "ll",
1589            DataType::LargeList(Field::new("item", DataType::Utf8, false).into()),
1590            false,
1591        );
1592        field
1593            .try_merge(&Field::new("ll", DataType::Null, true))
1594            .expect("LargeList should merge with Null");
1595        assert!(field.is_nullable());
1596        assert!(matches!(field.data_type(), DataType::LargeList(_)));
1597
1598        // Union + Null
1599        let mut field = Field::new(
1600            "u",
1601            DataType::Union(
1602                UnionFields::try_new(vec![0], vec![Field::new("f", DataType::Int32, false)])
1603                    .unwrap(),
1604                UnionMode::Dense,
1605            ),
1606            false,
1607        );
1608        field
1609            .try_merge(&Field::new("u", DataType::Null, true))
1610            .expect("Union should merge with Null");
1611        assert!(matches!(field.data_type(), DataType::Union(_, _)));
1612    }
1613}