mod empty_map;
use std::ops::SubAssign;
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
#[cfg(test)]
use super::Ipld;
#[cfg(test)]
use Selector::*;
#[allow(dead_code)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub enum Selector {
#[serde(rename = ".", with = "empty_map")]
Matcher,
#[serde(rename = "a")]
ExploreAll {
#[serde(rename = ">")]
next: Box<Selector>,
},
#[serde(rename = "f")]
ExploreFields {
#[serde(rename = "f>")]
fields: IndexMap<String, Selector>,
},
#[serde(rename = "i")]
ExploreIndex {
#[serde(rename = "i")]
index: usize,
#[serde(rename = ">")]
next: Box<Selector>,
},
#[serde(rename = "r")]
ExploreRange {
#[serde(rename = "^")]
start: usize,
#[serde(rename = "$")]
end: usize,
#[serde(rename = ">")]
next: Box<Selector>,
},
#[serde(rename = "R")]
ExploreRecursive {
#[serde(rename = ":>")]
sequence: Box<Selector>,
#[serde(rename = "l")]
limit: RecursionLimit,
#[serde(rename = "!")]
stop_at: Option<Condition>,
#[serde(skip_deserializing)]
current: Option<Box<Selector>>,
},
#[serde(rename = "|")]
ExploreUnion(Vec<Selector>),
#[serde(rename = "@", with = "empty_map")]
ExploreRecursiveEdge,
}
#[allow(dead_code)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Copy)]
pub enum RecursionLimit {
#[serde(rename = "none", with = "empty_map")]
None,
#[serde(rename = "depth")]
Depth(u64),
}
impl SubAssign<u64> for RecursionLimit {
fn sub_assign(&mut self, other: u64) {
if let RecursionLimit::Depth(v) = self {
*v -= other;
}
}
}
#[allow(dead_code)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Copy)]
pub enum Condition {
#[serde(rename = "hasField")]
HasField,
#[serde(rename = "=")]
HasValue,
#[serde(rename = "%")]
HasKind,
#[serde(rename = "/")]
IsLink,
#[serde(rename = "greaterThan")]
GreaterThan,
#[serde(rename = "lessThan")]
LessThan,
#[serde(rename = "and")]
And,
#[serde(rename = "or")]
Or,
}
#[cfg(test)]
impl Selector {
pub fn explore(self, ipld: &Ipld, p: &str) -> Option<Selector> {
match self {
ExploreAll { next } => Some(*next),
ExploreFields { mut fields } => match ipld {
Ipld::Map(m) => {
m.get(p)?;
fields.swap_remove(p)
}
,
Ipld::List(l) => {
if p.parse::<usize>().ok()? >= l.len() {
return None;
}
fields.swap_remove(p)
}
_ => None,
},
ExploreIndex { index, next } => match ipld {
Ipld::List(l) => {
let i = p.parse::<usize>().ok()?;
if i != index || i >= l.len() {
None
} else {
Some(*next)
}
}
_ => None,
},
ExploreRange { start, end, next } => {
match ipld {
Ipld::List(l) => {
let i = p.parse::<usize>().ok()?;
if i < start || i >= end || i >= l.len() {
None
} else {
Some(*next)
}
}
_ => None,
}
}
ExploreRecursive {
current,
sequence,
mut limit,
stop_at,
} => {
let next = current
.unwrap_or_else(|| sequence.clone())
.explore(ipld, p)?;
if !has_recursive_edge(&next) {
return Some(ExploreRecursive {
sequence,
current: Some(next.into()),
limit,
stop_at,
});
}
if let RecursionLimit::Depth(depth) = limit {
if depth < 2 {
return replace_recursive_edge(next, None);
}
limit -= 1;
}
Some(ExploreRecursive {
current: replace_recursive_edge(next, Some(*sequence.clone())).map(Box::new),
sequence,
limit,
stop_at,
})
}
ExploreUnion(selectors) => {
let replace_selectors: Vec<_> = selectors
.into_iter()
.filter_map(|s| s.explore(ipld, p))
.collect();
Selector::from_selectors(replace_selectors)
}
ExploreRecursiveEdge => None,
Matcher => None,
}
}
fn from_selectors(mut vec: Vec<Self>) -> Option<Self> {
match vec.len() {
0 | 1 => vec.pop(),
_ => Some(ExploreUnion(vec)),
}
}
}
#[cfg(test)]
fn replace_recursive_edge(next_sel: Selector, replace: Option<Selector>) -> Option<Selector> {
match next_sel {
ExploreRecursiveEdge => replace,
ExploreUnion(selectors) => {
let replace_selectors: Vec<_> = selectors
.into_iter()
.filter_map(|s| replace_recursive_edge(s, replace.clone()))
.collect();
Selector::from_selectors(replace_selectors)
}
_ => Some(next_sel),
}
}
#[cfg(test)]
fn has_recursive_edge(next_sel: &Selector) -> bool {
match next_sel {
ExploreRecursiveEdge => true,
ExploreUnion(selectors) => selectors.iter().any(has_recursive_edge),
_ => false,
}
}