1#[cfg(any(
2 feature = "dtype-datetime",
3 feature = "dtype-date",
4 feature = "dtype-duration",
5 feature = "dtype-time"
6))]
7use polars_compute::cast::cast_default;
8use polars_compute::cast::cast_unchecked;
9
10use crate::prelude::*;
11
12impl Series {
13 #[inline]
15 pub fn array_ref(&self, chunk_idx: usize) -> &ArrayRef {
16 &self.chunks()[chunk_idx] as &ArrayRef
17 }
18
19 pub fn to_arrow(&self, chunk_idx: usize, compat_level: CompatLevel) -> ArrayRef {
23 ToArrowConverter {
24 compat_level,
25 #[cfg(feature = "dtype-categorical")]
26 categorical_converter: {
27 let mut categorical_converter =
28 crate::series::categorical_to_arrow::CategoricalToArrowConverter {
29 converters: Default::default(),
30 persist_remap: false,
31 output_keys_only: false,
32 };
33
34 categorical_converter.initialize(self.dtype());
35
36 categorical_converter
37 },
38 }
39 .array_to_arrow(self.chunks().get(chunk_idx).unwrap().as_ref(), self.dtype())
40 }
41}
42
43pub struct ToArrowConverter {
44 pub compat_level: CompatLevel,
45 #[cfg(feature = "dtype-categorical")]
46 pub categorical_converter: crate::series::categorical_to_arrow::CategoricalToArrowConverter,
47}
48
49impl ToArrowConverter {
50 pub fn array_to_arrow(&mut self, array: &dyn Array, dtype: &DataType) -> Box<dyn Array> {
51 match dtype {
52 #[cfg(feature = "dtype-struct")]
54 DataType::Struct(fields) => {
55 use arrow::array::StructArray;
56
57 let arr: &StructArray = array.as_any().downcast_ref().unwrap();
58 let values = arr
59 .values()
60 .iter()
61 .zip(fields.iter())
62 .map(|(values, field)| self.array_to_arrow(values.as_ref(), field.dtype()))
63 .collect::<Vec<_>>();
64
65 StructArray::new(
66 ArrowDataType::Struct(
67 fields
68 .iter()
69 .map(|x| (x.name().clone(), x.dtype()))
70 .zip(values.iter().map(|x| x.dtype()))
71 .map(|((name, dtype), converted_arrow_dtype)| {
72 create_arrow_field(
73 name,
74 dtype,
75 converted_arrow_dtype,
76 self.compat_level,
77 )
78 })
79 .collect(),
80 ),
81 arr.len(),
82 values,
83 arr.validity().cloned(),
84 )
85 .boxed()
86 },
87 DataType::List(inner) => {
88 let arr: &ListArray<i64> = array.as_any().downcast_ref().unwrap();
89 let new_values = self.array_to_arrow(arr.values().as_ref(), inner);
90
91 let arr = ListArray::<i64>::new(
92 ArrowDataType::LargeList(Box::new(create_arrow_field(
93 LIST_VALUES_NAME,
94 inner.as_ref(),
95 new_values.dtype(),
96 self.compat_level,
97 ))),
98 arr.offsets().clone(),
99 new_values,
100 arr.validity().cloned(),
101 );
102 Box::new(arr)
103 },
104 #[cfg(feature = "dtype-array")]
105 DataType::Array(inner, width) => {
106 use arrow::array::FixedSizeListArray;
107
108 let arr: &FixedSizeListArray = array.as_any().downcast_ref().unwrap();
109 let new_values = self.array_to_arrow(arr.values().as_ref(), inner);
110
111 let arr = FixedSizeListArray::new(
112 ArrowDataType::FixedSizeList(
113 Box::new(create_arrow_field(
114 LIST_VALUES_NAME,
115 inner.as_ref(),
116 new_values.dtype(),
117 self.compat_level,
118 )),
119 *width,
120 ),
121 arr.len(),
122 new_values,
123 arr.validity().cloned(),
124 );
125 Box::new(arr)
126 },
127 #[cfg(feature = "dtype-categorical")]
128 DataType::Categorical(_, _) | DataType::Enum(_, _) => self
129 .categorical_converter
130 .array_to_arrow(array, dtype, self.compat_level),
131 #[cfg(feature = "dtype-date")]
132 DataType::Date => {
133 cast_default(array, &DataType::Date.to_arrow(self.compat_level)).unwrap()
134 },
135 #[cfg(feature = "dtype-datetime")]
136 DataType::Datetime(_, _) => {
137 cast_default(array, &dtype.to_arrow(self.compat_level)).unwrap()
138 },
139 #[cfg(feature = "dtype-duration")]
140 DataType::Duration(_) => {
141 cast_default(array, &dtype.to_arrow(self.compat_level)).unwrap()
142 },
143 #[cfg(feature = "dtype-time")]
144 DataType::Time => {
145 cast_default(array, &DataType::Time.to_arrow(self.compat_level)).unwrap()
146 },
147 #[cfg(feature = "dtype-decimal")]
148 DataType::Decimal(_, _) => array
149 .as_any()
150 .downcast_ref::<arrow::array::PrimitiveArray<i128>>()
151 .unwrap()
152 .clone()
153 .to(dtype.to_arrow(CompatLevel::newest()))
154 .to_boxed(),
155 #[cfg(feature = "object")]
156 DataType::Object(_) => {
157 use crate::chunked_array::object::builder::object_series_to_arrow_array;
158 object_series_to_arrow_array(&unsafe {
159 Series::from_chunks_and_dtype_unchecked(
160 PlSmallStr::EMPTY,
161 vec![array.to_boxed()],
162 dtype,
163 )
164 })
165 },
166 DataType::String => {
167 if self.compat_level.0 >= 1 {
168 array.to_boxed()
169 } else {
170 cast_unchecked(array, &ArrowDataType::LargeUtf8).unwrap()
171 }
172 },
173 DataType::Binary => {
174 if self.compat_level.0 >= 1 {
175 array.to_boxed()
176 } else {
177 cast_unchecked(array, &ArrowDataType::LargeBinary).unwrap()
178 }
179 },
180 _ => {
181 assert!(!dtype.is_logical());
182 array.to_boxed()
183 },
184 }
185 }
186}
187
188fn create_arrow_field(
189 name: PlSmallStr,
190 dtype: &DataType,
191 arrow_dtype: &ArrowDataType,
192 compat_level: CompatLevel,
193) -> ArrowField {
194 match (dtype, arrow_dtype) {
195 #[cfg(feature = "dtype-categorical")]
196 (DataType::Categorical(..) | DataType::Enum(..), ArrowDataType::Dictionary(_, _, _)) => {
197 dtype.to_arrow_field(name, compat_level)
199 },
200 _ => ArrowField::new(name, arrow_dtype.clone(), true),
201 }
202}