parquet_variant/path.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.
17use std::{borrow::Cow, ops::Deref};
18
19/// Represents a qualified path to a potential subfield or index of a variant
20/// value.
21///
22/// Can be used with [`Variant::get_path`] to retrieve a specific subfield of
23/// a variant value.
24///
25/// [`Variant::get_path`]: crate::Variant::get_path
26///
27/// Create a [`VariantPath`] from a vector of [`VariantPathElement`], or
28/// from a single field name or index.
29///
30/// # Example: Simple paths
31/// ```rust
32/// # use parquet_variant::{VariantPath, VariantPathElement};
33/// // access the field "foo" in a variant object value
34/// let path = VariantPath::from("foo");
35/// // access the first element in a variant list vale
36/// let path = VariantPath::from(0);
37/// ```
38///
39/// # Example: Compound paths
40/// ```
41/// # use parquet_variant::{VariantPath, VariantPathElement};
42/// /// You can also create a path by joining elements together:
43/// // access the field "foo" and then the first element in a variant list value
44/// let path = VariantPath::from("foo").join(0);
45/// // this is the same as the previous one
46/// let path2 = VariantPath::from_iter(["foo".into(), 0.into()]);
47/// assert_eq!(path, path2);
48/// // you can also create a path from a vector of `VariantPathElement` directly
49/// let path3 = [
50/// VariantPathElement::field("foo"),
51/// VariantPathElement::index(0)
52/// ].into_iter().collect::<VariantPath>();
53/// assert_eq!(path, path3);
54/// ```
55///
56/// # Example: Accessing Compound paths
57/// ```
58/// # use parquet_variant::{VariantPath, VariantPathElement};
59/// /// You can access the paths using slices
60/// // access the field "foo" and then the first element in a variant list value
61/// let path = VariantPath::from("foo")
62/// .join("bar")
63/// .join("baz");
64/// assert_eq!(path[1], VariantPathElement::field("bar"));
65/// ```
66#[derive(Debug, Clone, PartialEq, Default)]
67pub struct VariantPath<'a>(Vec<VariantPathElement<'a>>);
68
69impl<'a> VariantPath<'a> {
70 /// Create a new `VariantPath` from a vector of `VariantPathElement`.
71 pub fn new(path: Vec<VariantPathElement<'a>>) -> Self {
72 Self(path)
73 }
74
75 /// Return the inner path elements.
76 pub fn path(&self) -> &Vec<VariantPathElement<'_>> {
77 &self.0
78 }
79
80 /// Return a new `VariantPath` with element appended
81 pub fn join(mut self, element: impl Into<VariantPathElement<'a>>) -> Self {
82 self.push(element);
83 self
84 }
85
86 /// Append a new element to the path
87 pub fn push(&mut self, element: impl Into<VariantPathElement<'a>>) {
88 self.0.push(element.into());
89 }
90}
91
92impl<'a> From<Vec<VariantPathElement<'a>>> for VariantPath<'a> {
93 fn from(value: Vec<VariantPathElement<'a>>) -> Self {
94 Self::new(value)
95 }
96}
97
98/// Create from &str
99impl<'a> From<&'a str> for VariantPath<'a> {
100 fn from(path: &'a str) -> Self {
101 VariantPath::new(vec![path.into()])
102 }
103}
104
105/// Create from usize
106impl<'a> From<usize> for VariantPath<'a> {
107 fn from(index: usize) -> Self {
108 VariantPath::new(vec![VariantPathElement::index(index)])
109 }
110}
111
112/// Create from iter
113impl<'a> FromIterator<VariantPathElement<'a>> for VariantPath<'a> {
114 fn from_iter<T: IntoIterator<Item = VariantPathElement<'a>>>(iter: T) -> Self {
115 VariantPath::new(Vec::from_iter(iter))
116 }
117}
118
119impl<'a> Deref for VariantPath<'a> {
120 type Target = [VariantPathElement<'a>];
121
122 fn deref(&self) -> &Self::Target {
123 &self.0
124 }
125}
126
127/// Element of a [`VariantPath`] that can be a field name or an index.
128///
129/// See [`VariantPath`] for more details and examples.
130#[derive(Debug, Clone, PartialEq)]
131pub enum VariantPathElement<'a> {
132 /// Access field with name `name`
133 Field { name: Cow<'a, str> },
134 /// Access the list element at `index`
135 Index { index: usize },
136}
137
138impl<'a> VariantPathElement<'a> {
139 pub fn field(name: impl Into<Cow<'a, str>>) -> VariantPathElement<'a> {
140 let name = name.into();
141 VariantPathElement::Field { name }
142 }
143
144 pub fn index(index: usize) -> VariantPathElement<'a> {
145 VariantPathElement::Index { index }
146 }
147}
148
149// Conversion utilities for `VariantPathElement` from string types
150impl<'a> From<Cow<'a, str>> for VariantPathElement<'a> {
151 fn from(name: Cow<'a, str>) -> Self {
152 VariantPathElement::field(name)
153 }
154}
155
156impl<'a> From<&'a str> for VariantPathElement<'a> {
157 fn from(name: &'a str) -> Self {
158 VariantPathElement::field(Cow::Borrowed(name))
159 }
160}
161
162impl<'a> From<String> for VariantPathElement<'a> {
163 fn from(name: String) -> Self {
164 VariantPathElement::field(Cow::Owned(name))
165 }
166}
167
168impl<'a> From<&'a String> for VariantPathElement<'a> {
169 fn from(name: &'a String) -> Self {
170 VariantPathElement::field(Cow::Borrowed(name.as_str()))
171 }
172}
173
174impl<'a> From<usize> for VariantPathElement<'a> {
175 fn from(index: usize) -> Self {
176 VariantPathElement::index(index)
177 }
178}