#![deny(warnings)]
use std::fmt;
use std::fmt::Debug;
use snafu::Snafu;
pub use borrowed::{BorrowedSegment, BorrowedTargetPath, BorrowedValuePath};
pub use concat::PathConcat;
pub use owned::{OwnedSegment, OwnedTargetPath, OwnedValuePath};
use self::jit::JitValuePath;
mod borrowed;
mod concat;
mod jit;
mod owned;
#[derive(Clone, Debug, Eq, PartialEq, Snafu)]
pub enum PathParseError {
#[snafu(display("Invalid field path {:?}", path))]
InvalidPathSyntax { path: String },
}
#[macro_export]
macro_rules! path {
($($segment:expr_2021),*) => { $crate::path::BorrowedValuePath {
segments: &[$($crate::path::BorrowedSegment::from($segment),)*],
}};
}
#[macro_export]
macro_rules! event_path {
($($segment:expr_2021),*) => { $crate::path::BorrowedTargetPath {
prefix: $crate::path::PathPrefix::Event,
path: $crate::path!($($segment),*),
}};
}
#[macro_export]
macro_rules! metadata_path {
($($segment:expr_2021),*) => { $crate::path::BorrowedTargetPath {
prefix: $crate::path::PathPrefix::Metadata,
path: $crate::path!($($segment),*),
}};
}
#[macro_export]
macro_rules! owned_value_path {
($($segment:expr_2021),*) => {{
$crate::path::OwnedValuePath::from(vec![$($crate::path::OwnedSegment::from($segment),)*])
}};
}
#[macro_export]
macro_rules! owned_event_path {
($($tokens:tt)*) => {
$crate::path::OwnedTargetPath::event($crate::owned_value_path!($($tokens)*))
}
}
#[macro_export]
macro_rules! owned_metadata_path {
($($tokens:tt)*) => {
$crate::path::OwnedTargetPath::metadata($crate::owned_value_path!($($tokens)*))
}
}
pub fn parse_value_path(path: &str) -> Result<OwnedValuePath, PathParseError> {
JitValuePath::new(path)
.to_owned_value_path()
.map_err(|_| PathParseError::InvalidPathSyntax {
path: path.to_owned(),
})
}
pub fn parse_target_path(path: &str) -> Result<OwnedTargetPath, PathParseError> {
let (prefix, value_path) = get_target_prefix(path);
let value_path = parse_value_path(value_path)?;
Ok(OwnedTargetPath {
prefix,
path: value_path,
})
}
pub trait TargetPath<'a>: Clone {
type ValuePath: ValuePath<'a>;
fn prefix(&self) -> PathPrefix;
fn value_path(&self) -> Self::ValuePath;
}
pub trait ValuePath<'a>: Clone {
type Iter: Iterator<Item = BorrowedSegment<'a>> + Clone;
fn segment_iter(&self) -> Self::Iter;
fn concat<T: ValuePath<'a>>(&self, path: T) -> PathConcat<Self, T> {
PathConcat {
a: self.clone(),
b: path,
}
}
fn eq(&self, other: impl ValuePath<'a>) -> bool {
self.segment_iter().eq(other.segment_iter())
}
fn can_start_with(&self, prefix: impl ValuePath<'a>) -> bool {
let (self_path, prefix_path) = if let (Ok(self_path), Ok(prefix_path)) =
(self.to_owned_value_path(), prefix.to_owned_value_path())
{
(self_path, prefix_path)
} else {
return false;
};
let mut self_segments = self_path.segments.into_iter();
for prefix_segment in prefix_path.segments.iter() {
match self_segments.next() {
None => return false,
Some(self_segment) => {
if !self_segment.can_start_with(prefix_segment) {
return false;
}
}
}
}
true
}
#[allow(clippy::result_unit_err)]
fn to_owned_value_path(&self) -> Result<OwnedValuePath, ()> {
self.segment_iter()
.map(OwnedSegment::try_from)
.collect::<Result<Vec<OwnedSegment>, ()>>()
.map(OwnedValuePath::from)
}
}
#[cfg(any(feature = "string_path", test))]
impl<'a> ValuePath<'a> for &'a str {
type Iter = jit::JitValuePathIter<'a>;
fn segment_iter(&self) -> Self::Iter {
JitValuePath::new(self).segment_iter()
}
}
#[cfg(any(feature = "string_path", test))]
impl<'a> TargetPath<'a> for &'a str {
type ValuePath = &'a str;
fn prefix(&self) -> PathPrefix {
get_target_prefix(self).0
}
fn value_path(&self) -> Self::ValuePath {
get_target_prefix(self).1
}
}
impl<'a> TargetPath<'a> for &'a OwnedTargetPath {
type ValuePath = &'a OwnedValuePath;
fn prefix(&self) -> PathPrefix {
self.prefix
}
fn value_path(&self) -> Self::ValuePath {
&self.path
}
}
impl<'a, T: ValuePath<'a>> TargetPath<'a> for (PathPrefix, T) {
type ValuePath = T;
fn prefix(&self) -> PathPrefix {
self.0
}
fn value_path(&self) -> Self::ValuePath {
self.1.clone()
}
}
fn get_target_prefix(path: &str) -> (PathPrefix, &str) {
match path.chars().next() {
Some('.') => {
(PathPrefix::Event, path)
}
Some('%') => (PathPrefix::Metadata, &path[1..]),
_ => {
(PathPrefix::Event, path)
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "proptest"), derive(proptest_derive::Arbitrary))]
pub enum PathPrefix {
Event,
Metadata,
}
impl fmt::Display for PathPrefix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PathPrefix::Event => write!(f, "."),
PathPrefix::Metadata => write!(f, "%"),
}
}
}
#[cfg(test)]
mod test {
use crate::path::PathPrefix;
use crate::path::TargetPath;
use crate::path::ValuePath;
use crate::path::parse_target_path;
#[test]
fn test_parse_target_path() {
assert_eq!(parse_target_path("i"), Ok(owned_event_path!("i")));
}
#[test]
fn test_path_macro() {
assert!(ValuePath::eq(&path!("a", "b"), "a.b"))
}
#[test]
fn test_event_path_macro() {
let path = event_path!("a", "b");
let expected = "a.b";
assert!(ValuePath::eq(&path.value_path(), expected));
assert_eq!(path.prefix(), PathPrefix::Event);
}
#[test]
fn test_metadata_path_macro() {
let path = metadata_path!("a", "b");
let expected = "a.b";
assert!(ValuePath::eq(&path.value_path(), expected));
assert_eq!(path.prefix(), PathPrefix::Metadata);
}
#[test]
fn test_owned_value_path_macro() {
assert!(ValuePath::eq(&&owned_value_path!("a", "b"), "a.b"))
}
}