1use core::fmt;
11use std::fmt::{Display, Formatter};
12use std::sync::Arc;
13
14use itertools::Itertools;
15use vortex_utils::aliases::hash_set::HashSet;
16
17use crate::{DType, FieldName};
18
19#[derive(Clone, Debug, PartialEq, Eq, Hash)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub enum Field {
23 Name(FieldName),
25 ElementType,
28}
29
30impl Field {
31 pub fn as_name(&self) -> Option<&str> {
33 match self {
34 Field::Name(name) => Some(name),
35 Field::ElementType => None,
36 }
37 }
38
39 pub fn is_named(&self) -> bool {
41 matches!(self, Field::Name(_))
42 }
43}
44
45impl From<&str> for Field {
46 fn from(value: &str) -> Self {
47 Field::Name(value.into())
48 }
49}
50
51impl From<Arc<str>> for Field {
52 fn from(value: Arc<str>) -> Self {
53 Self::Name(value)
54 }
55}
56
57impl Display for Field {
58 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
59 match self {
60 Field::Name(name) => write!(f, "${name}"),
61 Field::ElementType => write!(f, "[]"),
62 }
63 }
64}
65
66#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)]
82#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
83pub struct FieldPath(Vec<Field>);
84
85impl FieldPath {
86 pub fn root() -> Self {
88 Self::default()
89 }
90
91 pub fn from_name<F: Into<Field>>(name: F) -> Self {
93 Self(vec![name.into()])
94 }
95
96 pub fn parts(&self) -> &[Field] {
98 &self.0
99 }
100
101 pub fn is_root(&self) -> bool {
103 self.0.is_empty()
104 }
105
106 pub fn push<F: Into<Field>>(mut self, field: F) -> Self {
108 self.0.push(field.into());
109 self
110 }
111
112 pub fn starts_with_field(&self, field: &Field) -> bool {
115 assert!(matches!(field, Field::Name(_)));
116 let first = self.0.first();
117 assert!(matches!(first, Some(Field::Name(_))));
118 first.is_some_and(|f| f == field)
119 }
120
121 pub fn step_into(mut self) -> Option<Self> {
123 if self.0.is_empty() {
124 return None;
125 }
126 self.0 = self.0.into_iter().skip(1).collect();
127 Some(self)
128 }
129
130 pub fn resolve(&self, mut dtype: DType) -> Option<DType> {
167 for field in &self.0 {
168 dtype = match (dtype, field) {
169 (DType::Struct(fields, _), Field::Name(name)) => fields.field(name)?,
170 (DType::List(element_dtype, _), Field::ElementType) => {
171 element_dtype.as_ref().clone()
172 }
173 (..) => return None,
174 }
175 }
176
177 Some(dtype)
178 }
179
180 pub fn exists_in(&self, dtype: DType) -> bool {
182 self.resolve(dtype).is_some()
184 }
185}
186
187impl FromIterator<Field> for FieldPath {
188 fn from_iter<T: IntoIterator<Item = Field>>(iter: T) -> Self {
189 FieldPath(iter.into_iter().collect())
190 }
191}
192
193impl From<Field> for FieldPath {
194 fn from(value: Field) -> Self {
195 FieldPath(vec![value])
196 }
197}
198
199impl From<Vec<Field>> for FieldPath {
200 fn from(value: Vec<Field>) -> Self {
201 FieldPath(value)
202 }
203}
204
205impl Display for FieldPath {
206 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
207 Display::fmt(&self.0.iter().format("."), f)
208 }
209}
210
211#[derive(Default, Clone, Debug)]
212pub struct FieldPathSet {
214 set: HashSet<FieldPath>,
217}
218
219impl FieldPathSet {
220 pub fn contains(&self, path: &FieldPath) -> bool {
222 self.set.contains(path)
223 }
224}
225
226impl FromIterator<FieldPath> for FieldPathSet {
227 fn from_iter<T: IntoIterator<Item = FieldPath>>(iter: T) -> Self {
228 let set = HashSet::from_iter(iter);
229 Self { set }
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236 use crate::Nullability::*;
237 use crate::{DType, PType, StructFields};
238
239 #[test]
240 fn test_field_path() {
241 let path = FieldPath::from_name("A").push("B").push("C");
242 assert_eq!(path.to_string(), "$A.$B.$C");
243
244 let fields = vec!["A", "B", "C"]
245 .into_iter()
246 .map(Field::from)
247 .collect_vec();
248 assert_eq!(path.parts(), &fields);
249
250 let vec_path = FieldPath::from(fields);
251 assert_eq!(vec_path.to_string(), "$A.$B.$C");
252 assert_eq!(path, vec_path);
253 }
254
255 #[test]
256 fn nested_field_single_level() {
257 let a_type = DType::Primitive(PType::I32, NonNullable);
258 let dtype = DType::struct_(
259 [("a", a_type.clone()), ("b", DType::Bool(Nullable))],
260 NonNullable,
261 );
262 let path = FieldPath::from_name("a");
263 assert_eq!(a_type, path.resolve(dtype.clone()).unwrap());
264 assert!(path.exists_in(dtype));
265 }
266
267 #[test]
268 fn nested_field_two_level() {
269 let inner = DType::struct_(
270 [
271 ("inner_a", DType::Primitive(PType::U8, NonNullable)),
272 ("inner_b", DType::Bool(Nullable)),
273 ],
274 NonNullable,
275 );
276
277 let outer = DType::Struct(
278 StructFields::from_iter([("outer_a", DType::Bool(NonNullable)), ("outer_b", inner)]),
279 NonNullable,
280 );
281
282 let path = FieldPath::from_name("outer_b").push("inner_a");
283 let dtype = path.resolve(outer.clone()).unwrap();
284
285 assert_eq!(dtype, DType::Primitive(PType::U8, NonNullable));
286 assert!(path.exists_in(outer));
287 }
288
289 #[test]
290 fn nested_field_deep_nested() {
291 let level1 = DType::struct_(
292 [(
293 "a",
294 DType::struct_(
295 [(
296 "b",
297 DType::list(
298 DType::struct_(
299 [("c", DType::Primitive(PType::F64, Nullable))],
300 NonNullable,
301 ),
302 Nullable,
303 ),
304 )],
305 NonNullable,
306 ),
307 )],
308 NonNullable,
309 );
310
311 let path = FieldPath::from_name("a")
312 .push("b")
313 .push(Field::ElementType)
314 .push("c");
315 let dtype = path.resolve(level1.clone()).unwrap();
316
317 assert_eq!(dtype, DType::Primitive(PType::F64, Nullable));
318 assert!(path.exists_in(level1.clone()));
319
320 let path = FieldPath::from_name("a")
321 .push("b")
322 .push("c")
323 .push(Field::ElementType);
324 assert!(path.resolve(level1.clone()).is_none());
325 assert!(!path.exists_in(level1.clone()));
326
327 let path = FieldPath::from_name("a")
328 .push(Field::ElementType)
329 .push("b")
330 .push("c");
331 assert!(path.resolve(level1.clone()).is_none());
332 assert!(!path.exists_in(level1.clone()));
333
334 let path = FieldPath::from_name(Field::ElementType)
335 .push("a")
336 .push("b")
337 .push("c");
338 assert!(path.resolve(level1.clone()).is_none());
339 assert!(!path.exists_in(level1));
340 }
341
342 #[test]
343 fn nested_field_not_found() {
344 let dtype = DType::struct_([("a", DType::Bool(NonNullable))], NonNullable);
345 let path = FieldPath::from_name("b");
346 assert!(path.resolve(dtype.clone()).is_none());
347 assert!(!path.exists_in(dtype.clone()));
348
349 let path = FieldPath::from(Field::ElementType);
350 assert!(path.resolve(dtype.clone()).is_none());
351 assert!(!path.exists_in(dtype));
352 }
353
354 #[test]
355 fn nested_field_non_struct_intermediate() {
356 let dtype = DType::struct_(
357 [("a", DType::Primitive(PType::I32, NonNullable))],
358 NonNullable,
359 );
360 let path = FieldPath::from_name("a").push("b");
361 assert!(path.resolve(dtype.clone()).is_none());
362 assert!(!path.exists_in(dtype.clone()));
363
364 let path = FieldPath::from_name("a").push(Field::ElementType);
365 assert!(path.resolve(dtype.clone()).is_none());
366 assert!(!path.exists_in(dtype));
367 }
368}