typed_arrow_dyn/view/
views.rs

1use std::{marker::PhantomData, sync::Arc};
2
3use arrow_array::{
4    Array, ArrayRef, FixedSizeListArray, LargeListArray, ListArray, MapArray, StructArray,
5    UnionArray,
6};
7use arrow_schema::{FieldRef, Fields, UnionFields, UnionMode};
8
9use super::{
10    cell::{DynCellRef, view_cell_with_projector},
11    path::Path,
12    projection::{FieldProjector, StructProjection},
13};
14use crate::DynViewError;
15
16/// View over a struct column.
17pub struct DynStructView<'a> {
18    pub(super) array: &'a StructArray,
19    pub(super) fields: Fields,
20    pub(super) row: usize,
21    pub(super) base_path: Path,
22    pub(super) projection: Option<Arc<StructProjection>>,
23}
24
25impl<'a> DynStructView<'a> {
26    /// Number of child fields.
27    pub fn len(&self) -> usize {
28        self.fields.len()
29    }
30
31    /// Returns true if the struct has no fields.
32    pub fn is_empty(&self) -> bool {
33        self.fields.is_empty()
34    }
35
36    /// Retrieve the value of a struct field by index.
37    pub fn get(&'a self, index: usize) -> Result<Option<DynCellRef<'a>>, DynViewError> {
38        let field = self
39            .fields
40            .get(index)
41            .ok_or_else(|| DynViewError::ColumnOutOfBounds {
42                column: index,
43                width: self.fields.len(),
44            })?;
45        let (source_index, projector) = if let Some(projection) = &self.projection {
46            let child = projection
47                .children
48                .get(index)
49                .ok_or(DynViewError::ColumnOutOfBounds {
50                    column: index,
51                    width: projection.children.len(),
52                })?;
53            (child.source_index, Some(&child.projector))
54        } else {
55            (index, None)
56        };
57        let child = self.array.column(source_index);
58        let path = self.base_path.push_field(field.name());
59        view_cell_with_projector(&path, field.as_ref(), projector, child.as_ref(), self.row)
60    }
61
62    /// Retrieve a struct field by name.
63    pub fn get_by_name(
64        &'a self,
65        name: &str,
66    ) -> Option<Result<Option<DynCellRef<'a>>, DynViewError>> {
67        self.fields
68            .iter()
69            .position(|f| f.name() == name)
70            .map(move |idx| self.get(idx))
71    }
72}
73
74/// View over `List<T>` / `LargeList<T>` values.
75#[derive(Debug, Clone)]
76pub struct DynListView<'a> {
77    pub(super) values: ArrayRef,
78    pub(super) item_field: FieldRef,
79    pub(super) start: usize,
80    pub(super) end: usize,
81    pub(super) base_path: Path,
82    pub(super) item_projector: Option<FieldProjector>,
83    pub(super) _marker: PhantomData<&'a ()>,
84}
85
86impl<'a> DynListView<'a> {
87    pub(super) fn new_list(
88        array: &'a ListArray,
89        item_field: FieldRef,
90        base_path: Path,
91        row: usize,
92        item_projector: Option<FieldProjector>,
93    ) -> Result<Self, DynViewError> {
94        let offsets = array.value_offsets();
95        let start = offsets[row] as usize;
96        let end = offsets[row + 1] as usize;
97        Ok(Self {
98            values: array.values().clone(),
99            item_field,
100            start,
101            end,
102            base_path,
103            item_projector,
104            _marker: PhantomData,
105        })
106    }
107
108    pub(super) fn new_large_list(
109        array: &'a LargeListArray,
110        item_field: FieldRef,
111        base_path: Path,
112        row: usize,
113        item_projector: Option<FieldProjector>,
114    ) -> Result<Self, DynViewError> {
115        let offsets = array.value_offsets();
116        let start = offsets[row] as usize;
117        let end = offsets[row + 1] as usize;
118        Ok(Self {
119            values: array.values().clone(),
120            item_field,
121            start,
122            end,
123            base_path,
124            item_projector,
125            _marker: PhantomData,
126        })
127    }
128
129    /// Number of elements in the list.
130    pub fn len(&self) -> usize {
131        self.end - self.start
132    }
133
134    /// Returns true when the list contains no elements.
135    pub fn is_empty(&self) -> bool {
136        self.len() == 0
137    }
138
139    /// Retrieve the list element at `index`.
140    pub fn get(&'a self, index: usize) -> Result<Option<DynCellRef<'a>>, DynViewError> {
141        if index >= self.len() {
142            return Err(DynViewError::RowOutOfBounds {
143                row: index,
144                len: self.len(),
145            });
146        }
147        let absolute = self.start + index;
148        let path = self.base_path.push_index(index);
149        let projector = self.item_projector.as_ref();
150        view_cell_with_projector(
151            &path,
152            self.item_field.as_ref(),
153            projector,
154            self.values.as_ref(),
155            absolute,
156        )
157    }
158}
159
160/// View over a fixed-size list.
161#[derive(Debug, Clone)]
162pub struct DynFixedSizeListView<'a> {
163    pub(super) values: ArrayRef,
164    pub(super) item_field: FieldRef,
165    pub(super) start: usize,
166    pub(super) len: usize,
167    pub(super) base_path: Path,
168    pub(super) item_projector: Option<FieldProjector>,
169    pub(super) _marker: PhantomData<&'a ()>,
170}
171
172impl<'a> DynFixedSizeListView<'a> {
173    pub(super) fn new(
174        array: &'a FixedSizeListArray,
175        item_field: FieldRef,
176        len: usize,
177        base_path: Path,
178        row: usize,
179        item_projector: Option<FieldProjector>,
180    ) -> Result<Self, DynViewError> {
181        let start = row * len;
182        Ok(Self {
183            values: array.values().clone(),
184            item_field,
185            start,
186            len,
187            base_path,
188            item_projector,
189            _marker: PhantomData,
190        })
191    }
192
193    /// Number of items (constant for all rows).
194    pub fn len(&self) -> usize {
195        self.len
196    }
197
198    /// Returns true when the list contains no items.
199    pub fn is_empty(&self) -> bool {
200        self.len == 0
201    }
202
203    /// Retrieve the element at `index`.
204    pub fn get(&'a self, index: usize) -> Result<Option<DynCellRef<'a>>, DynViewError> {
205        if index >= self.len {
206            return Err(DynViewError::RowOutOfBounds {
207                row: index,
208                len: self.len,
209            });
210        }
211        let absolute = self.start + index;
212        let path = self.base_path.push_index(index);
213        let projector = self.item_projector.as_ref();
214        view_cell_with_projector(
215            &path,
216            self.item_field.as_ref(),
217            projector,
218            self.values.as_ref(),
219            absolute,
220        )
221    }
222}
223
224/// View over a map column.
225#[derive(Debug, Clone)]
226pub struct DynMapView<'a> {
227    pub(super) array: &'a MapArray,
228    pub(super) start: usize,
229    pub(super) end: usize,
230    pub(super) base_path: Path,
231    pub(super) fields: Fields,
232    pub(super) projection: Option<Arc<StructProjection>>,
233}
234
235impl<'a> DynMapView<'a> {
236    pub(super) fn new(
237        array: &'a MapArray,
238        base_path: Path,
239        row: usize,
240    ) -> Result<Self, DynViewError> {
241        let entry_fields = array
242            .entries()
243            .as_any()
244            .downcast_ref::<StructArray>()
245            .map(|struct_arr| struct_arr.fields().clone())
246            .ok_or_else(|| DynViewError::Invalid {
247                column: 0,
248                path: base_path.path.clone(),
249                message: "map entries must be struct".to_string(),
250            })?;
251        Self::with_projection(array, entry_fields, base_path, row, None)
252    }
253
254    pub(super) fn with_projection(
255        array: &'a MapArray,
256        entry_fields: Fields,
257        base_path: Path,
258        row: usize,
259        projection: Option<Arc<StructProjection>>,
260    ) -> Result<Self, DynViewError> {
261        let offsets = array.value_offsets();
262        let start = offsets[row] as usize;
263        let end = offsets[row + 1] as usize;
264        Ok(Self {
265            array,
266            start,
267            end,
268            base_path,
269            fields: entry_fields,
270            projection,
271        })
272    }
273
274    /// Number of key/value pairs in the map entry.
275    pub fn len(&self) -> usize {
276        self.end - self.start
277    }
278
279    /// Returns true if the entry has no items.
280    pub fn is_empty(&self) -> bool {
281        self.len() == 0
282    }
283
284    /// Return the key/value pair at `index`.
285    pub fn get(
286        &'a self,
287        index: usize,
288    ) -> Result<(DynCellRef<'a>, Option<DynCellRef<'a>>), DynViewError> {
289        if index >= self.len() {
290            return Err(DynViewError::RowOutOfBounds {
291                row: index,
292                len: self.len(),
293            });
294        }
295        let entries = self.array.entries();
296        let struct_entry = entries
297            .as_any()
298            .downcast_ref::<StructArray>()
299            .ok_or_else(|| DynViewError::Invalid {
300                column: self.base_path.column,
301                path: self.base_path.path.clone(),
302                message: "map entries must be struct arrays".to_string(),
303            })?;
304
305        let (key_source, key_projector) = if let Some(proj) = &self.projection {
306            let child = proj.children.first().ok_or_else(|| DynViewError::Invalid {
307                column: self.base_path.column,
308                path: self.base_path.path.clone(),
309                message: "map projection missing key child".to_string(),
310            })?;
311            (child.source_index, Some(&child.projector))
312        } else {
313            (0, None)
314        };
315        let (value_source, value_projector) = if let Some(proj) = &self.projection {
316            let child = proj.children.get(1).ok_or_else(|| DynViewError::Invalid {
317                column: self.base_path.column,
318                path: self.base_path.path.clone(),
319                message: "map projection missing value child".to_string(),
320            })?;
321            (child.source_index, Some(&child.projector))
322        } else {
323            (1, None)
324        };
325        let keys = struct_entry.column(key_source);
326        let values = struct_entry.column(value_source);
327        let key_field = Arc::clone(self.fields.first().ok_or_else(|| DynViewError::Invalid {
328            column: self.base_path.column,
329            path: self.base_path.path.clone(),
330            message: "map schema missing key field".to_string(),
331        })?);
332        let value_field = Arc::clone(self.fields.get(1).ok_or_else(|| DynViewError::Invalid {
333            column: self.base_path.column,
334            path: self.base_path.path.clone(),
335            message: "map schema missing value field".to_string(),
336        })?);
337
338        let absolute = self.start + index;
339        let key_path = self.base_path.push_index(index).push_key();
340        let key = view_cell_with_projector(
341            &key_path,
342            key_field.as_ref(),
343            key_projector,
344            keys.as_ref(),
345            absolute,
346        )?
347        .ok_or_else(|| DynViewError::Invalid {
348            column: key_path.column,
349            path: key_path.path.clone(),
350            message: "map keys may not be null".to_string(),
351        })?;
352
353        let value_path = self.base_path.push_index(index).push_value();
354        let value = view_cell_with_projector(
355            &value_path,
356            value_field.as_ref(),
357            value_projector,
358            values.as_ref(),
359            absolute,
360        )?;
361
362        Ok((key, value))
363    }
364}
365
366/// View over a union value.
367#[derive(Debug, Clone)]
368pub struct DynUnionView<'a> {
369    pub(super) array: &'a UnionArray,
370    pub(super) fields: UnionFields,
371    pub(super) mode: UnionMode,
372    pub(super) row: usize,
373    pub(super) base_path: Path,
374}
375
376impl<'a> DynUnionView<'a> {
377    pub(super) fn new(
378        array: &'a UnionArray,
379        fields: UnionFields,
380        mode: UnionMode,
381        base_path: Path,
382        row: usize,
383    ) -> Result<Self, DynViewError> {
384        if row >= array.len() {
385            return Err(DynViewError::RowOutOfBounds {
386                row,
387                len: array.len(),
388            });
389        }
390        Ok(Self {
391            array,
392            fields,
393            mode,
394            row,
395            base_path,
396        })
397    }
398
399    /// Active type id for this row.
400    pub fn type_id(&self) -> i8 {
401        self.array.type_id(self.row)
402    }
403
404    /// Active variant metadata.
405    fn variant_field(&self) -> Result<(i8, FieldRef), DynViewError> {
406        let tag = self.type_id();
407        self.fields
408            .iter()
409            .find_map(|(t, field)| {
410                if t == tag {
411                    Some((t, Arc::clone(field)))
412                } else {
413                    None
414                }
415            })
416            .ok_or_else(|| DynViewError::Invalid {
417                column: self.base_path.column,
418                path: self.base_path.path.clone(),
419                message: format!("unknown union type id {tag}"),
420            })
421    }
422
423    /// Returns the name of the active variant, if present.
424    pub fn variant_name(&self) -> Option<&str> {
425        let tag = self.type_id();
426        self.fields
427            .iter()
428            .find(|(t, _)| *t == tag)
429            .map(|(_, field)| field.name().as_str())
430    }
431
432    /// Retrieve the active value (or `None` if the variant payload is null).
433    pub fn value(&'a self) -> Result<Option<DynCellRef<'a>>, DynViewError> {
434        let (tag, field) = self.variant_field()?;
435        let child = self.array.child(tag);
436        let child_index = match self.mode {
437            UnionMode::Dense => self.array.value_offset(self.row),
438            UnionMode::Sparse => self.row,
439        };
440        let path = self.base_path.push_variant(field.name().as_str(), tag);
441        view_cell_with_projector(&path, field.as_ref(), None, child.as_ref(), child_index)
442    }
443}