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}
173
174impl<'a> VariantPathElement<'a> {
175 pub fn field(name: impl Into<Cow<'a, str>>) -> VariantPathElement<'a> {
176 let name = name.into();
177 VariantPathElement::Field { name }
178 }
179
180 pub fn index(index: usize) -> VariantPathElement<'a> {
181 VariantPathElement::Index { index }
182 }
183}
184
185impl<'a> From<Cow<'a, str>> for VariantPathElement<'a> {
187 fn from(name: Cow<'a, str>) -> Self {
188 VariantPathElement::field(name)
189 }
190}
191
192impl<'a> From<&'a str> for VariantPathElement<'a> {
193 fn from(name: &'a str) -> Self {
194 VariantPathElement::field(Cow::Borrowed(name))
195 }
196}
197
198impl From<String> for VariantPathElement<'_> {
199 fn from(name: String) -> Self {
200 VariantPathElement::field(Cow::Owned(name))
201 }
202}
203
204impl<'a> From<&'a String> for VariantPathElement<'a> {
205 fn from(name: &'a String) -> Self {
206 VariantPathElement::field(Cow::Borrowed(name.as_str()))
207 }
208}
209
210impl From<usize> for VariantPathElement<'_> {
211 fn from(index: usize) -> Self {
212 VariantPathElement::index(index)
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219
220 #[test]
221 fn test_variant_path_empty() {
222 let path = VariantPath::from_iter([]);
223 assert!(path.is_empty());
224 }
225
226 #[test]
227 fn test_variant_path_empty_str() {
228 let path = VariantPath::try_from("").unwrap();
229 assert!(path.is_empty());
230 }
231
232 #[test]
233 fn test_variant_path_non_empty() {
234 let p = VariantPathElement::from("a");
235 let path = VariantPath::from_iter([p]);
236 assert!(!path.is_empty());
237 }
238
239 #[test]
240 fn test_variant_path_dot_notation_with_array_index() {
241 let path = VariantPath::try_from("city.store.books[3].title").unwrap();
242
243 let expected = VariantPath::try_from("city")
244 .unwrap()
245 .join("store")
246 .join("books")
247 .join(3)
248 .join("title");
249
250 assert_eq!(path, expected);
251 }
252
253 #[test]
254 fn test_variant_path_dot_notation_with_only_array_index() {
255 let path = VariantPath::try_from("[3]").unwrap();
256
257 let expected = VariantPath::from(3);
258
259 assert_eq!(path, expected);
260 }
261
262 #[test]
263 fn test_variant_path_dot_notation_with_starting_array_index() {
264 let path = VariantPath::try_from("[3].title").unwrap();
265
266 let expected = VariantPath::from(3).join("title");
267
268 assert_eq!(path, expected);
269 }
270
271 #[test]
272 fn test_variant_path_field_in_bracket() {
273 let path = VariantPath::try_from("foo[0].bar").unwrap();
275 let expected = VariantPath::from_iter([
276 VariantPathElement::field("foo"),
277 VariantPathElement::index(0),
278 VariantPathElement::field("bar"),
279 ]);
280 assert_eq!(path, expected);
281
282 let path = VariantPath::try_from("foo.bar[42]").unwrap();
284 let expected = VariantPath::from_iter([
285 VariantPathElement::field("foo"),
286 VariantPathElement::field("bar"),
287 VariantPathElement::index(42),
288 ]);
289 assert_eq!(path, expected);
290
291 let path = VariantPath::try_from("foo.bar['abc'][\"def\"]").unwrap();
293 let expected = VariantPath::from_iter([
294 VariantPathElement::field("foo"),
295 VariantPathElement::field("bar"),
296 VariantPathElement::field("abc"),
297 VariantPathElement::field("def"),
298 ]);
299 assert_eq!(path, expected);
300
301 let path = VariantPath::try_from("foo['0'].bar[\"1\"]").unwrap();
303 let expected = VariantPath::from_iter([
304 VariantPathElement::field("foo"),
305 VariantPathElement::field("0"),
306 VariantPathElement::field("bar"),
307 VariantPathElement::field("1"),
308 ]);
309 assert_eq!(path, expected);
310 }
311
312 #[test]
313 fn test_invalid_path_parse() {
314 let err = VariantPath::try_from(".foo.bar").unwrap_err();
316 assert_eq!(err.to_string(), "Parser error: Unexpected leading '.'");
317
318 let err = VariantPath::try_from("foo.bar.").unwrap_err();
320 assert_eq!(err.to_string(), "Parser error: Unexpected trailing '.'");
321
322 let err = VariantPath::try_from("foo.bar[2.baz").unwrap_err();
324 assert_eq!(err.to_string(), "Parser error: Unclosed '[' at byte 7");
325
326 let err = VariantPath::try_from("foo.bar[2\\].fds").unwrap_err();
328 assert_eq!(err.to_string(), "Parser error: Unclosed '[' at byte 7");
329
330 let err = VariantPath::try_from("foo.bar[fdafa\\").unwrap_err();
332 assert_eq!(err.to_string(), "Parser error: Unclosed '[' at byte 7");
333
334 let err = VariantPath::try_from("foo.bar]baz").unwrap_err();
336 assert_eq!(err.to_string(), "Parser error: Unexpected ']' at byte 7");
337
338 let err = VariantPath::try_from("foo.bar[123abc]").unwrap_err();
340 assert_eq!(
341 err.to_string(),
342 "Parser error: Invalid token in bracket request: `123abc`. Expected a quoted string or a number(e.g., `['field']` or `[123]`)"
343 );
344
345 let err = VariantPath::try_from("foo.bar[abc]").unwrap_err();
346 assert_eq!(
347 err.to_string(),
348 "Parser error: Invalid token in bracket request: `abc`. Expected a quoted string or a number(e.g., `['field']` or `[123]`)"
349 );
350
351 let too_large_index = (usize::MAX as u128) + 1;
353 let err = VariantPath::try_from(format!("[{too_large_index}]").as_str()).unwrap_err();
354 assert!(
355 err.to_string()
356 .contains("Parser error: Invalid token in bracket request"),
357 "{err}"
358 );
359 }
360}