use std::{fmt, str::FromStr}; use serde::{ Deserialize, Deserializer, de::{self, Visitor}, }; use smartstring::{LazyCompact, SmartString}; use thiserror::Error; use crate::Label; mod parser; mod tokenizer; #[derive(Debug, Error, PartialEq)] pub enum PathParseError { #[error("invalid syntax at index {position}")] Syntax { position: usize }, #[error("path string must start with $")] MustStartWithRoot { position: usize }, #[error("invalid field {str:?} at {position}")] InvalidField { position: usize, str: SmartString, }, #[error("invalid index {str:?} at {position}")] InvalidIndexString { position: usize, str: SmartString, }, #[error("non-ascii character {char:?} at index {position}")] NonAsciiChar { position: usize, char: char }, } #[derive(Debug, Clone, PartialEq, Eq)] pub enum PathSegment { /// Go to root node (`$` identifier) Root, /// Go to a child of the current object Field(Label), /// Go to an element of the current list Index(i64), } /// A path to aPathSegment::Field inside a nested object, /// This is a subset of the rfc9535 jsonpath. /// /// Format: /// - `$` refers to the root object /// - `.` selects aPathSegment::Field of an object /// - `[n]` selects an item of an array #[derive(Debug, Clone)] pub struct ObjectPath { pub segments: Vec, } impl<'de> Deserialize<'de> for ObjectPath { fn deserialize>(deserializer: D) -> Result { struct PathVisitor; impl Visitor<'_> for PathVisitor { type Value = ObjectPath; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("an objectpath") } fn visit_str(self, v: &str) -> Result { v.parse().map_err(de::Error::custom) } } deserializer.deserialize_str(PathVisitor) } } impl FromStr for ObjectPath { type Err = PathParseError; fn from_str(source: &str) -> Result { let tk = tokenizer::Tokenizer::new(); let tk = tk.tokenize(source)?; let ps = parser::Parser::new(); let segments = ps.parse(source, &tk)?; return Ok(Self { segments }); } }