use core::hash::Hash;
use std::{borrow::Cow, str::FromStr};
use thiserror::Error;
use crate::rules::matcher::{MatchParser, Rule};
#[derive(Error, Debug, Clone, PartialEq)]
pub enum PathError {
#[error("{0}")]
Parse(#[from] Box<pest::error::Error<Rule>>),
}
#[derive(Clone, Debug, Eq)]
pub struct XPath {
path: String,
segments: Vec<String>,
}
impl Hash for XPath {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.path.hash(state)
}
}
impl FromStr for XPath {
type Err = PathError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}
impl PartialEq for XPath {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
if self.path.len() != other.path.len() {
return false;
}
let mut o = other.path.as_bytes().iter().rev();
for b in self.path.as_bytes().iter().rev() {
if Some(b) != o.next() {
return false;
}
}
true
}
}
impl std::fmt::Display for XPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.path)
}
}
impl XPath {
#[inline]
pub fn parse<S: AsRef<str>>(s: S) -> Result<Self, PathError> {
Ok(XPath {
path: String::from(s.as_ref()),
segments: MatchParser::parse_path(s)?,
})
}
#[inline(always)]
pub fn segments(&self) -> &[String] {
&self.segments
}
#[inline(always)]
pub fn iter_segments(&self) -> core::slice::Iter<'_, std::string::String> {
self.segments.iter()
}
#[inline(always)]
pub fn to_string_lossy(&self) -> Cow<'_, str> {
Cow::from(&self.path)
}
}
#[cfg(test)]
mod test {
use std::str::FromStr;
use super::XPath;
#[test]
fn test_path() {
let s = r#".data.exe.file."with space"."with .""#;
let p = XPath::from_str(s).unwrap();
assert_eq!(p.to_string_lossy(), s);
assert_eq!(p.segments[0], "data");
assert_eq!(p.segments[1], "exe");
assert_eq!(p.segments[2], "file");
assert_eq!(p.segments[3], "with space");
assert_eq!(p.segments[4], "with .");
assert_eq!(p, XPath::from_str(s).unwrap());
let mut i = p.iter_segments();
assert_eq!(i.next(), Some(&"data".into()));
assert_eq!(i.next(), Some(&"exe".into()));
assert_eq!(i.next(), Some(&"file".into()));
assert_eq!(i.next(), Some(&"with space".into()));
assert_eq!(i.next(), Some(&"with .".into()));
}
}