## Coverage Report

#### Created: 2025-11-22 06:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /Users/andrewlamb/Software/datafusion/datafusion/common/src/scalar/struct_builder.rs

    Line
    Count
    Source
    1
    // Licensed to the Apache Software Foundation (ASF) under one
    2
    // or more contributor license agreements.  See the NOTICE file
    3
    // distributed with this work for additional information
    4
    // regarding copyright ownership.  The ASF licenses this file
    5
    // to you under the Apache License, Version 2.0 (the
    6
    // "License"); you may not use this file except in compliance
    7
    // with the License.  You may obtain a copy of the License at
    8
    //
    9
    //   http://www.apache.org/licenses/LICENSE-2.0
    10
    //
    11
    // Unless required by applicable law or agreed to in writing,
    12
    // software distributed under the License is distributed on an
    13
    // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14
    // KIND, either express or implied.  See the License for the
    15
    // specific language governing permissions and limitations
    16
    // under the License.
    17
    18
    //! [`ScalarStructBuilder`] for building [`ScalarValue::Struct`]
    19
    20
    use crate::{Result, ScalarValue};
    21
    use arrow::array::{ArrayRef, StructArray};
    22
    use arrow::datatypes::{DataType, Field, FieldRef, Fields};
    23
    use std::sync::Arc;
    24
    25
    /// Builder for [`ScalarValue::Struct`].
    26
    ///
    27
    /// See examples on [`ScalarValue`]
    28
    #[derive(Debug, Default)]
    29
    pub struct ScalarStructBuilder {
    30
        fields: Vec<FieldRef>,
    31
        arrays: Vec<ArrayRef>,
    32
    }
    33
    34
    impl ScalarStructBuilder {
    35
        /// Create a new `ScalarStructBuilder`
    36
    0
        pub fn new() -> Self {
    37
    0
            Self::default()
    38
    0
        }
    39
    40
        /// Return a new [`ScalarValue::Struct`] with a single `null` value.
    41
        ///
    42
        /// Note this is different from a struct where each of the specified fields
    43
        /// are null (e.g. `{a: NULL}`)
    44
        ///
    45
        /// # Example
    46
        ///
    47
        /// ```rust
    48
        /// # use arrow::datatypes::{DataType, Field};
    49
        /// # use datafusion_common::scalar::ScalarStructBuilder;
    50
        /// let fields = vec![Field::new("a", DataType::Int32, false)];
    51
        /// let sv = ScalarStructBuilder::new_null(fields);
    52
        /// // Note this is `NULL`, not `{a: NULL}`
    53
        /// assert_eq!(format!("{sv}"), "NULL");
    54
        /// ```
    55
        ///
    56
        /// To create a struct where the *fields* are null, use `Self::new()` and
    57
        /// pass null values for each field:
    58
        ///
    59
        /// ```rust
    60
        /// # use arrow::datatypes::{DataType, Field};
    61
        /// # use datafusion_common::scalar::{ScalarStructBuilder, ScalarValue};
    62
        /// // make a nullable field
    63
        /// let field = Field::new("a", DataType::Int32, true);
    64
        /// // add a null value for the "a" field
    65
        /// let sv = ScalarStructBuilder::new()
    66
        ///     .with_scalar(field, ScalarValue::Int32(None))
    67
        ///     .build()
    68
        ///     .unwrap();
    69
        /// // value is not null, but field is
    70
        /// assert_eq!(format!("{sv}"), "{a:}");
    71
        /// ```
    72
    0
        pub fn new_null(fields: impl IntoFields) -> ScalarValue {
    73
    0
            DataType::Struct(fields.into()).try_into().unwrap()
    74
    0
        }
    75
    76
        /// Add the specified field and [`ArrayRef`] to the struct.
    77
        ///
    78
        /// Note the array should have a single row.
    79
    0
        pub fn with_array(mut self, field: impl IntoFieldRef, value: ArrayRef) -> Self {
    80
    0
            self.fields.push(field.into_field_ref());
    81
    0
            self.arrays.push(value);
    82
    0
            self
    83
    0
        }
    84
    85
        /// Add the specified field and `ScalarValue` to the struct.
    86
        #[expect(clippy::needless_pass_by_value)] // Skip for public API's compatibility
    87
    0
        pub fn with_scalar(self, field: impl IntoFieldRef, value: ScalarValue) -> Self {
    88
            // valid scalar value should not fail
    89
    0
            let array = value.to_array().unwrap();
    90
    0
            self.with_array(field, array)
    91
    0
        }
    92
    93
        /// Add a field with the specified name and value to the struct.
    94
        /// the field is created with the specified data type and as non nullable
    95
    0
        pub fn with_name_and_scalar(self, name: &str, value: ScalarValue) -> Self {
    96
    0
            let field = Field::new(name, value.data_type(), false);
    97
    0
            self.with_scalar(field, value)
    98
    0
        }
    99
    100
        /// Return a [`ScalarValue::Struct`] with the fields and values added so far
    101
        ///
    102
        /// # Errors
    103
        ///
    104
        /// If the [`StructArray`] cannot be created (for example if there is a
    105
        /// mismatch between field types and arrays) or the arrays do not have
    106
        /// exactly one element.
    107
    0
        pub fn build(self) -> Result<ScalarValue> {
    108
    0
            let Self { fields, arrays } = self;
    109
    110
    0
            let struct_array =
    111
    0
                StructArray::try_new_with_length(Fields::from(fields), arrays, None, 1)?;
    112
    0
            Ok(ScalarValue::Struct(Arc::new(struct_array)))
    113
    0
        }
    114
    }
    115
    116
    /// Trait for converting a type into a [`FieldRef`]
    117
    ///
    118
    /// Used to avoid having to call `clone()` on a `FieldRef` when adding a field to
    119
    /// a `ScalarStructBuilder`.
    120
    ///
    121
    /// TODO potentially upstream this to arrow-rs so that we can
    122
    /// use impl `Into<FieldRef>` instead
    123
    pub trait IntoFieldRef {
    124
        fn into_field_ref(self) -> FieldRef;
    125
    }
    126
    127
    impl IntoFieldRef for FieldRef {
    128
    0
        fn into_field_ref(self) -> FieldRef {
    129
    0
            self
    130
    0
        }
    131
    }
    132
    133
    impl IntoFieldRef for &FieldRef {
    134
    0
        fn into_field_ref(self) -> FieldRef {
    135
    0
            Arc::clone(self)
    136
    0
        }
    137
    }
    138
    139
    impl IntoFieldRef for Field {
    140
    0
        fn into_field_ref(self) -> FieldRef {
    141
    0
            FieldRef::new(self)
    142
    0
        }
    143
    }
    144
    145
    /// Trait for converting a type into a [`Fields`]
    146
    ///
    147
    /// This avoids to avoid having to call clone() on an Arc'd `Fields` when adding
    148
    /// a field to a `ScalarStructBuilder`
    149
    ///
    150
    /// TODO potentially upstream this to arrow-rs so that we can
    151
    /// use impl `Into<Fields>` instead
    152
    pub trait IntoFields {
    153
        fn into(self) -> Fields;
    154
    }
    155
    156
    impl IntoFields for Fields {
    157
    0
        fn into(self) -> Fields {
    158
    0
            self
    159
    0
        }
    160
    }
    161
    162
    impl IntoFields for &Fields {
    163
    0
        fn into(self) -> Fields {
    164
    0
            self.clone()
    165
    0
        }
    166
    }
    167
    168
    impl IntoFields for Vec<Field> {
    169
    0
        fn into(self) -> Fields {
    170
    0
            Fields::from(self)
    171
    0
        }
    172
    }
    173
    174
    #[cfg(test)]
    175
    mod tests {
    176
        use super::*;
    177
    178
        // Other cases are tested by doc tests
    179
        #[test]
    180
        fn test_empty_struct() {
    181
            let sv = ScalarStructBuilder::new().build().unwrap();
    182
            assert_eq!(format!("{sv}"), "{}");
    183
        }
    184
    }