1use crate::utils::parse_path;
18use arrow_schema::ArrowError;
19use std::{borrow::Cow, ops::Deref};
20
21#[derive(Debug, Clone, PartialEq, Default)]
88pub struct VariantPath<'a>(Vec<VariantPathElement<'a>>);
89
90impl<'a> VariantPath<'a> {
91 pub fn new(path: Vec<VariantPathElement<'a>>) -> Self {
93 Self(path)
94 }
95
96 pub fn path(&self) -> &Vec<VariantPathElement<'_>> {
98 &self.0
99 }
100
101 pub fn join(mut self, element: impl Into<VariantPathElement<'a>>) -> Self {
103 self.push(element);
104 self
105 }
106
107 pub fn push(&mut self, element: impl Into<VariantPathElement<'a>>) {
109 self.0.push(element.into());
110 }
111
112 pub fn is_empty(&self) -> bool {
114 self.0.is_empty()
115 }
116}
117
118impl<'a> From<Vec<VariantPathElement<'a>>> for VariantPath<'a> {
119 fn from(value: Vec<VariantPathElement<'a>>) -> Self {
120 Self::new(value)
121 }
122}
123
124impl<'a> TryFrom<&'a str> for VariantPath<'a> {
126 type Error = ArrowError;
127
128 fn try_from(path: &'a str) -> Result<Self, Self::Error> {
129 parse_path(path).map(VariantPath::new)
130 }
131}
132
133impl<'a> From<usize> for VariantPath<'a> {
135 fn from(index: usize) -> Self {
136 VariantPath::new(vec![VariantPathElement::index(index)])
137 }
138}
139
140impl<'a> From<&[VariantPathElement<'a>]> for VariantPath<'a> {
141 fn from(elements: &[VariantPathElement<'a>]) -> Self {
142 VariantPath::new(elements.to_vec())
143 }
144}
145
146impl<'a> FromIterator<VariantPathElement<'a>> for VariantPath<'a> {
148 fn from_iter<T: IntoIterator<Item = VariantPathElement<'a>>>(iter: T) -> Self {
149 VariantPath::new(Vec::from_iter(iter))
150 }
151}
152
153impl<'a> Deref for VariantPath<'a> {
154 type Target = [VariantPathElement<'a>];
155
156 fn deref(&self) -> &Self::Target {
157 &self.0
158 }
159}
160
161#[derive(Debug, Clone, PartialEq)]
165pub enum VariantPathElement<'a> {
166 Field { name: Cow<'a, str> },
168 Index { index: usize },
170}
171
172impl<'a> VariantPathElement<'a> {
173 pub fn field(name: impl Into<Cow<'a, str>>) -> VariantPathElement<'a> {
174 let name = name.into();
175 VariantPathElement::Field { name }
176 }
177
178 pub fn index(index: usize) -> VariantPathElement<'a> {
179 VariantPathElement::Index { index }
180 }
181}
182
183impl<'a> From<Cow<'a, str>> for VariantPathElement<'a> {
185 fn from(name: Cow<'a, str>) -> Self {
186 VariantPathElement::field(name)
187 }
188}
189
190impl<'a> From<&'a str> for VariantPathElement<'a> {
191 fn from(name: &'a str) -> Self {
192 VariantPathElement::field(Cow::Borrowed(name))
193 }
194}
195
196impl<'a> From<String> for VariantPathElement<'a> {
197 fn from(name: String) -> Self {
198 VariantPathElement::field(Cow::Owned(name))
199 }
200}
201
202impl<'a> From<&'a String> for VariantPathElement<'a> {
203 fn from(name: &'a String) -> Self {
204 VariantPathElement::field(Cow::Borrowed(name.as_str()))
205 }
206}
207
208impl<'a> From<usize> for VariantPathElement<'a> {
209 fn from(index: usize) -> Self {
210 VariantPathElement::index(index)
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217
218 #[test]
219 fn test_variant_path_empty() {
220 let path = VariantPath::from_iter([]);
221 assert!(path.is_empty());
222 }
223
224 #[test]
225 fn test_variant_path_empty_str() {
226 let path = VariantPath::try_from("").unwrap();
227 assert!(path.is_empty());
228 }
229
230 #[test]
231 fn test_variant_path_non_empty() {
232 let p = VariantPathElement::from("a");
233 let path = VariantPath::from_iter([p]);
234 assert!(!path.is_empty());
235 }
236
237 #[test]
238 fn test_variant_path_dot_notation_with_array_index() {
239 let path = VariantPath::try_from("city.store.books[3].title").unwrap();
240
241 let expected = VariantPath::try_from("city")
242 .unwrap()
243 .join("store")
244 .join("books")
245 .join(3)
246 .join("title");
247
248 assert_eq!(path, expected);
249 }
250
251 #[test]
252 fn test_variant_path_dot_notation_with_only_array_index() {
253 let path = VariantPath::try_from("[3]").unwrap();
254
255 let expected = VariantPath::from(3);
256
257 assert_eq!(path, expected);
258 }
259
260 #[test]
261 fn test_variant_path_dot_notation_with_starting_array_index() {
262 let path = VariantPath::try_from("[3].title").unwrap();
263
264 let expected = VariantPath::from(3).join("title");
265
266 assert_eq!(path, expected);
267 }
268
269 #[test]
270 fn test_variant_path_field_in_bracket() {
271 let path = VariantPath::try_from("foo[0].bar").unwrap();
273 let expected = VariantPath::from_iter([
274 VariantPathElement::field("foo"),
275 VariantPathElement::index(0),
276 VariantPathElement::field("bar"),
277 ]);
278 assert_eq!(path, expected);
279
280 let path = VariantPath::try_from("foo.bar[42]").unwrap();
282 let expected = VariantPath::from_iter([
283 VariantPathElement::field("foo"),
284 VariantPathElement::field("bar"),
285 VariantPathElement::index(42),
286 ]);
287 assert_eq!(path, expected);
288
289 let path = VariantPath::try_from("foo.bar[abc]").unwrap();
291 let expected = VariantPath::from_iter([
292 VariantPathElement::field("foo"),
293 VariantPathElement::field("bar"),
294 VariantPathElement::field("abc"),
295 ]);
296 assert_eq!(path, expected);
297 }
298
299 #[test]
300 fn test_invalid_path_parse() {
301 let err = VariantPath::try_from(".foo.bar").unwrap_err();
303 assert_eq!(err.to_string(), "Parser error: Unexpected leading '.'");
304
305 let err = VariantPath::try_from("foo.bar.").unwrap_err();
307 assert_eq!(err.to_string(), "Parser error: Unexpected trailing '.'");
308
309 let err = VariantPath::try_from("foo.bar[2.baz").unwrap_err();
311 assert_eq!(err.to_string(), "Parser error: Unclosed '[' at byte 7");
312
313 let err = VariantPath::try_from("foo.bar[2\\].fds").unwrap_err();
315 assert_eq!(err.to_string(), "Parser error: Unclosed '[' at byte 7");
316
317 let err = VariantPath::try_from("foo.bar[fdafa\\").unwrap_err();
319 assert_eq!(err.to_string(), "Parser error: Unclosed '[' at byte 7");
320
321 let err = VariantPath::try_from("foo.bar]baz").unwrap_err();
323 assert_eq!(err.to_string(), "Parser error: Unexpected ']' at byte 7");
324 }
325}