96 lines
2.1 KiB
Rust
96 lines
2.1 KiB
Rust
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<LazyCompact>,
|
|
},
|
|
|
|
#[error("invalid index {str:?} at {position}")]
|
|
InvalidIndexString {
|
|
position: usize,
|
|
str: SmartString<LazyCompact>,
|
|
},
|
|
|
|
#[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
|
|
/// - `.<name>` selects aPathSegment::Field of an object
|
|
/// - `[n]` selects an item of an array
|
|
#[derive(Debug, Clone)]
|
|
pub struct ObjectPath {
|
|
pub segments: Vec<PathSegment>,
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for ObjectPath {
|
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
|
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<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
|
|
v.parse().map_err(de::Error::custom)
|
|
}
|
|
}
|
|
|
|
deserializer.deserialize_str(PathVisitor)
|
|
}
|
|
}
|
|
|
|
impl FromStr for ObjectPath {
|
|
type Err = PathParseError;
|
|
|
|
fn from_str(source: &str) -> Result<Self, Self::Err> {
|
|
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 });
|
|
}
|
|
}
|