use core::fmt;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PathComponent {
Key(String),
Index(usize),
All,
}
impl From<&str> for PathComponent {
fn from(key: &str) -> Self {
Self::Key(key.to_owned())
}
}
impl From<String> for PathComponent {
fn from(key: String) -> Self {
Self::Key(key)
}
}
impl From<usize> for PathComponent {
fn from(index: usize) -> Self {
Self::Index(index)
}
}
impl From<()> for PathComponent {
fn from((): ()) -> Self {
Self::All
}
}
impl fmt::Display for PathComponent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Key(key) => write!(f, "{key:?}"),
Self::Index(index) => write!(f, "{index}"),
Self::All => f.write_str("null"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ClaimPath(Vec<PathComponent>);
impl ClaimPath {
pub fn new<I, C>(components: I) -> Self
where
I: IntoIterator<Item = C>,
C: Into<PathComponent>,
{
Self(components.into_iter().map(Into::into).collect())
}
pub fn root() -> Self {
Self(Vec::new())
}
pub fn components(&self) -> &[PathComponent] {
&self.0
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn is_concrete(&self) -> bool {
!self.0.contains(&PathComponent::All)
}
pub fn matches(&self, path: &ClaimPath) -> bool {
self.0.len() == path.0.len()
&& self.0.iter().zip(&path.0).all(|(pattern, component)| {
*pattern == PathComponent::All && matches!(component, PathComponent::Index(_))
|| pattern == component
})
}
pub fn is_prefix_of(&self, path: &ClaimPath) -> bool {
self.0.len() <= path.0.len()
&& self.0.iter().zip(&path.0).all(|(pattern, component)| {
*pattern == PathComponent::All && matches!(component, PathComponent::Index(_))
|| pattern == component
})
}
#[must_use]
pub fn join(&self, component: impl Into<PathComponent>) -> Self {
let mut components = self.0.clone();
components.push(component.into());
Self(components)
}
pub fn select<'a>(&self, root: &'a Value) -> Result<Vec<&'a Value>, ClaimPathError> {
let mut selected = vec![root];
for (depth, component) in self.0.iter().enumerate() {
let mut next = Vec::new();
for value in selected {
match (component, value) {
(PathComponent::Key(key), Value::Object(object)) => {
next.extend(object.get(key));
}
(PathComponent::Index(index), Value::Array(array)) => {
next.extend(array.get(*index));
}
(PathComponent::All, Value::Array(array)) => next.extend(array),
(PathComponent::Key(_), _) => {
return Err(ClaimPathError {
path: self.clone(),
depth,
expected: "an object",
});
}
_ => {
return Err(ClaimPathError {
path: self.clone(),
depth,
expected: "an array",
});
}
}
}
selected = next;
}
Ok(selected)
}
pub fn resolve(&self, root: &Value) -> Result<Vec<ClaimPath>, ClaimPathError> {
let mut selected = vec![(ClaimPath(Vec::new()), root)];
for (depth, component) in self.0.iter().enumerate() {
let mut next = Vec::new();
for (path, value) in selected {
match (component, value) {
(PathComponent::Key(key), Value::Object(object)) => {
if let Some(value) = object.get(key) {
next.push((path.join(key.as_str()), value));
}
}
(PathComponent::Index(index), Value::Array(array)) => {
if let Some(value) = array.get(*index) {
next.push((path.join(*index), value));
}
}
(PathComponent::All, Value::Array(array)) => {
next.extend(
array
.iter()
.enumerate()
.map(|(index, value)| (path.join(index), value)),
);
}
(PathComponent::Key(_), _) => {
return Err(ClaimPathError {
path: self.clone(),
depth,
expected: "an object",
});
}
_ => {
return Err(ClaimPathError {
path: self.clone(),
depth,
expected: "an array",
});
}
}
}
selected = next;
}
Ok(selected.into_iter().map(|(path, _)| path).collect())
}
}
impl<C: Into<PathComponent>> FromIterator<C> for ClaimPath {
fn from_iter<I: IntoIterator<Item = C>>(iter: I) -> Self {
Self::new(iter)
}
}
impl<C: Into<PathComponent>, const N: usize> From<[C; N]> for ClaimPath {
fn from(components: [C; N]) -> Self {
Self::new(components)
}
}
impl From<Vec<PathComponent>> for ClaimPath {
fn from(components: Vec<PathComponent>) -> Self {
Self(components)
}
}
impl fmt::Display for ClaimPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("[")?;
for (i, component) in self.0.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
write!(f, "{component}")?;
}
f.write_str("]")
}
}
#[macro_export]
macro_rules! claim_path {
($($component:expr),* $(,)?) => {
$crate::vc::ClaimPath::from(vec![$($crate::vc::PathComponent::from($component)),*])
};
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("claim path {path}: component {depth} expects {expected}")]
pub struct ClaimPathError {
pub path: ClaimPath,
pub depth: usize,
pub expected: &'static str,
}