1use crate::utils::parse_path;
18use arrow_schema::ArrowError;
19use std::{borrow::Cow, ops::Deref};
20
21#[derive(Debug, Clone, PartialEq, Default)]
90pub struct VariantPath<'a>(Vec<VariantPathElement<'a>>);
91
92impl<'a> VariantPath<'a> {
93 pub fn new(path: Vec<VariantPathElement<'a>>) -> Self {
95 Self(path)
96 }
97
98 pub fn path(&self) -> &Vec<VariantPathElement<'_>> {
100 &self.0
101 }
102
103 pub fn join(mut self, element: impl Into<VariantPathElement<'a>>) -> Self {
105 self.push(element);
106 self
107 }
108
109 pub fn push(&mut self, element: impl Into<VariantPathElement<'a>>) {
111 self.0.push(element.into());
112 }
113
114 pub fn is_empty(&self) -> bool {
116 self.0.is_empty()
117 }
118}
119
120impl<'a> From<Vec<VariantPathElement<'a>>> for VariantPath<'a> {
121 fn from(value: Vec<VariantPathElement<'a>>) -> Self {
122 Self::new(value)
123 }
124}
125
126impl<'a> TryFrom<&'a str> for VariantPath<'a> {
128 type Error = ArrowError;
129
130 fn try_from(path: &'a str) -> Result<Self, Self::Error> {
131 parse_path(path).map(VariantPath::new)
132 }
133}
134
135impl From<usize> for VariantPath<'_> {
137 fn from(index: usize) -> Self {
138 VariantPath::new(vec![VariantPathElement::index(index)])
139 }
140}
141
142impl<'a> From<&[VariantPathElement<'a>]> for VariantPath<'a> {
143 fn from(elements: &[VariantPathElement<'a>]) -> Self {
144 VariantPath::new(elements.to_vec())
145 }
146}
147
148impl<'a> FromIterator<VariantPathElement<'a>> for VariantPath<'a> {
150 fn from_iter<T: IntoIterator<Item = VariantPathElement<'a>>>(iter: T) -> Self {
151 VariantPath::new(Vec::from_iter(iter))
152 }
153}
154
155impl<'a> Deref for VariantPath<'a> {
156 type Target = [VariantPathElement<'a>];
157
158 fn deref(&self) -> &Self::Target {
159 &self.0
160 }
161}
162
163#[derive(Debug, Clone, PartialEq)]
167pub enum VariantPathElement<'a> {
168 Field { name: Cow<'a, str> },
170 Index { index: usize },
172 ListElement,
174}
175
176impl<'a> VariantPathElement<'a> {
177 pub fn field(name: impl Into<Cow<'a, str>>) -> VariantPathElement<'a> {
178 let name = name.into();
179 VariantPathElement::Field { name }
180 }
181
182 pub fn index(index: usize) -> VariantPathElement<'a> {
183 VariantPathElement::Index { index }
184 }
185
186 pub fn list_element() -> VariantPathElement<'a> {
187 VariantPathElement::ListElement
188 }
189}
190
191impl<'a> From<Cow<'a, str>> for VariantPathElement<'a> {
193 fn from(name: Cow<'a, str>) -> Self {
194 VariantPathElement::field(name)
195 }
196}
197
198impl<'a> From<&'a str> for VariantPathElement<'a> {
199 fn from(name: &'a str) -> Self {
200 VariantPathElement::field(Cow::Borrowed(name))
201 }
202}
203
204impl From<String> for VariantPathElement<'_> {
205 fn from(name: String) -> Self {
206 VariantPathElement::field(Cow::Owned(name))
207 }
208}
209
210impl<'a> From<&'a String> for VariantPathElement<'a> {
211 fn from(name: &'a String) -> Self {
212 VariantPathElement::field(Cow::Borrowed(name.as_str()))
213 }
214}
215
216impl From<usize> for VariantPathElement<'_> {
217 fn from(index: usize) -> Self {
218 VariantPathElement::index(index)
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225
226 #[test]
227 fn test_variant_path_empty() {
228 let path = VariantPath::from_iter([]);
229 assert!(path.is_empty());
230 }
231
232 #[test]
233 fn test_variant_path_empty_str() {
234 let path = VariantPath::try_from("").unwrap();
235 assert!(path.is_empty());
236 }
237
238 #[test]
239 fn test_variant_path_non_empty() {
240 let p = VariantPathElement::from("a");
241 let path = VariantPath::from_iter([p]);
242 assert!(!path.is_empty());
243 }
244
245 #[test]
246 fn test_variant_path_dot_notation_with_array_index() {
247 let path = VariantPath::try_from("city.store.books[3].title").unwrap();
248
249 let expected = VariantPath::try_from("city")
250 .unwrap()
251 .join("store")
252 .join("books")
253 .join(3)
254 .join("title");
255
256 assert_eq!(path, expected);
257 }
258
259 #[test]
260 fn test_variant_path_dot_notation_with_only_array_index() {
261 let path = VariantPath::try_from("[3]").unwrap();
262
263 let expected = VariantPath::from(3);
264
265 assert_eq!(path, expected);
266 }
267
268 #[test]
269 fn test_variant_path_dot_notation_with_starting_array_index() {
270 let path = VariantPath::try_from("[3].title").unwrap();
271
272 let expected = VariantPath::from(3).join("title");
273
274 assert_eq!(path, expected);
275 }
276
277 #[test]
278 fn test_variant_path_field_in_bracket() {
279 let path = VariantPath::try_from("foo[0].bar").unwrap();
281 let expected = VariantPath::from_iter([
282 VariantPathElement::field("foo"),
283 VariantPathElement::index(0),
284 VariantPathElement::field("bar"),
285 ]);
286 assert_eq!(path, expected);
287
288 let path = VariantPath::try_from("foo.bar[42]").unwrap();
290 let expected = VariantPath::from_iter([
291 VariantPathElement::field("foo"),
292 VariantPathElement::field("bar"),
293 VariantPathElement::index(42),
294 ]);
295 assert_eq!(path, expected);
296
297 let path = VariantPath::try_from("foo[*]['*']").unwrap();
299 let expected = VariantPath::from_iter([
300 VariantPathElement::field("foo"),
301 VariantPathElement::list_element(),
302 VariantPathElement::field("*"),
303 ]);
304 assert_eq!(path, expected);
305
306 let path = VariantPath::try_from("foo.bar['abc'][\"def\"]").unwrap();
308 let expected = VariantPath::from_iter([
309 VariantPathElement::field("foo"),
310 VariantPathElement::field("bar"),
311 VariantPathElement::field("abc"),
312 VariantPathElement::field("def"),
313 ]);
314 assert_eq!(path, expected);
315
316 let path = VariantPath::try_from("foo['0'].bar[\"1\"]").unwrap();
318 let expected = VariantPath::from_iter([
319 VariantPathElement::field("foo"),
320 VariantPathElement::field("0"),
321 VariantPathElement::field("bar"),
322 VariantPathElement::field("1"),
323 ]);
324 assert_eq!(path, expected);
325 }
326
327 #[test]
328 fn test_invalid_path_parse() {
329 let err = VariantPath::try_from(".foo.bar").unwrap_err();
331 assert_eq!(err.to_string(), "Parser error: Unexpected leading '.'");
332
333 let err = VariantPath::try_from("foo.bar.").unwrap_err();
335 assert_eq!(err.to_string(), "Parser error: Unexpected trailing '.'");
336
337 let err = VariantPath::try_from("foo.bar[2.baz").unwrap_err();
339 assert_eq!(err.to_string(), "Parser error: Unclosed '[' at byte 7");
340
341 let err = VariantPath::try_from("foo.bar[2\\].fds").unwrap_err();
343 assert_eq!(err.to_string(), "Parser error: Unclosed '[' at byte 7");
344
345 let err = VariantPath::try_from("foo.bar[fdafa\\").unwrap_err();
347 assert_eq!(err.to_string(), "Parser error: Unclosed '[' at byte 7");
348
349 let err = VariantPath::try_from("foo.bar]baz").unwrap_err();
351 assert_eq!(err.to_string(), "Parser error: Unexpected ']' at byte 7");
352
353 let err = VariantPath::try_from("foo.bar[123abc]").unwrap_err();
355 assert_eq!(
356 err.to_string(),
357 "Parser error: Invalid token in bracket request: `123abc`. Expected `*`, a quoted string, or a number(e.g., `[*]`, `['field']`, or `[123]`)"
358 );
359
360 let err = VariantPath::try_from("foo.bar[abc]").unwrap_err();
361 assert_eq!(
362 err.to_string(),
363 "Parser error: Invalid token in bracket request: `abc`. Expected `*`, a quoted string, or a number(e.g., `[*]`, `['field']`, or `[123]`)"
364 );
365
366 let too_large_index = (usize::MAX as u128) + 1;
368 let err = VariantPath::try_from(format!("[{too_large_index}]").as_str()).unwrap_err();
369 assert!(
370 err.to_string()
371 .contains("Parser error: Invalid token in bracket request"),
372 "{err}"
373 );
374 }
375}