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