Skip to main content

arrow_cast/cast/
structs.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::cast::*;
19
20pub(crate) fn cast_struct_to_struct(
21    array: &StructArray,
22    from_fields: Fields,
23    to_fields: Fields,
24    cast_options: &CastOptions,
25) -> Result<ArrayRef, ArrowError> {
26    // Fast path: if field names are in the same order, we can just zip and cast
27    let fields_match_order = from_fields.len() == to_fields.len()
28        && from_fields
29            .iter()
30            .zip(to_fields.iter())
31            .all(|(f1, f2)| f1.name() == f2.name());
32
33    let fields = if fields_match_order {
34        // Fast path: cast columns in order if their names match
35        cast_struct_fields_in_order(array, to_fields.clone(), cast_options)?
36    } else {
37        let all_fields_match_by_name = to_fields.iter().all(|to_field| {
38            from_fields
39                .iter()
40                .any(|from_field| from_field.name() == to_field.name())
41        });
42
43        if all_fields_match_by_name {
44            // Slow path: match fields by name and reorder
45            cast_struct_fields_by_name(array, from_fields.clone(), to_fields.clone(), cast_options)?
46        } else {
47            // Fallback: cast field by field in order
48            cast_struct_fields_in_order(array, to_fields.clone(), cast_options)?
49        }
50    };
51
52    let array = StructArray::try_new(to_fields.clone(), fields, array.nulls().cloned())?;
53    Ok(Arc::new(array) as ArrayRef)
54}
55
56fn cast_struct_fields_by_name(
57    array: &StructArray,
58    from_fields: Fields,
59    to_fields: Fields,
60    cast_options: &CastOptions,
61) -> Result<Vec<ArrayRef>, ArrowError> {
62    to_fields
63        .iter()
64        .map(|to_field| {
65            let from_field_idx = from_fields
66                .iter()
67                .position(|from_field| from_field.name() == to_field.name())
68                .unwrap(); // safe because we checked above
69            let column = array.column(from_field_idx);
70            cast_with_options(column, to_field.data_type(), cast_options)
71        })
72        .collect::<Result<Vec<ArrayRef>, ArrowError>>()
73}
74
75fn cast_struct_fields_in_order(
76    array: &StructArray,
77    to_fields: Fields,
78    cast_options: &CastOptions,
79) -> Result<Vec<ArrayRef>, ArrowError> {
80    array
81        .columns()
82        .iter()
83        .zip(to_fields.iter())
84        .map(|(l, field)| cast_with_options(l, field.data_type(), cast_options))
85        .collect::<Result<Vec<ArrayRef>, ArrowError>>()
86}