use crate::*;
use pest::Parser;
use pest_derive::*;
use std::collections::BTreeMap;
macro_rules! parse_array_index {
($pair:ident, $name:ident) => {
$name[$pair
.as_str()
.parse::<usize>()
.map_err(|e| format!("Parse failure: {}!", e))?]
};
}
macro_rules! parse_char_string {
($pair: ident) => {
String::from($pair.as_str())
};
}
macro_rules! parse_char {
($pair:ident, $name:ident) => {
$name[$pair.as_str()]
};
}
macro_rules! parse_ident_string {
($pair: ident) => {
String::from($pair.as_str())
};
}
macro_rules! parse_ident {
($pair:ident, $name:ident) => {
$name[$pair.as_str()]
};
}
macro_rules! parse_range {
($pair:ident, $name:ident) => {
match $name {
Unstructured::<T>::Seq(s) => {
let mut range: Vec<usize> = $pair
.as_str()
.split(":")
.map(|v| v.parse::<usize>().unwrap_or(0))
.collect();
if range[1] > s.len() || range[1] == 0 {
range[1] = s.len();
}
if range[0] >= s.len() {
Unstructured::<T>::Seq(vec![])
} else {
let res = Vec::from(&s[range[0]..range[1]]);
Unstructured::<T>::Seq(res)
}
}
_ => return Err(format!("Cannot take range on non-sequence value!")),
}
};
}
macro_rules! parse_doc_index {
($pair:ident) => {
$pair
.as_str()
.parse::<usize>()
.map_err(|e| format!("Parse failure: {}!", e))?
};
}
#[derive(Parser)]
#[grammar = "selector/grammar/selector.pest"]
struct SelectorParser;
impl<T: UnstructuredDataTrait> Unstructured<T> {
pub fn select<'a>(&'a self, sel: &str) -> Result<&'a Unstructured<T>, String>
where
T: Clone,
{
let selection = SelectorParser::parse(Rule::selector, sel).map_err(|e| e.to_string())?;
let mut result = self;
for selector in selection {
match selector.as_rule() {
Rule::index => result = &parse_array_index!(selector, result),
Rule::chars => result = &parse_char!(selector, result),
Rule::ident => result = &parse_ident!(selector, result),
Rule::EOI => return Ok(result),
_ => return Err(format!("Invalid selector {}", selector)),
};
}
Ok(result)
}
pub fn select_mut<'a>(&'a mut self, sel: &str) -> Result<&'a mut Unstructured<T>, String>
where
T: Clone,
{
let selection = SelectorParser::parse(Rule::selector, sel).map_err(|e| e.to_string())?;
let mut result = self;
for selector in selection {
match selector.as_rule() {
Rule::index => result = &mut parse_array_index!(selector, result),
Rule::chars => result = &mut parse_char!(selector, result),
Rule::ident => result = &mut parse_ident!(selector, result),
Rule::EOI => return Ok(result),
_ => return Err(format!("Invalid selector {}", selector)),
};
}
Ok(result)
}
pub fn filter(docs: &[Unstructured<T>], sel: &str) -> Result<Unstructured<T>, String>
where
T: Clone,
{
let mut result = Unstructured::<T>::Map(BTreeMap::new());
if !docs.is_empty() {
let mut current_owned = None;
let mut current = &docs[0];
let mut key_path = vec![];
let selection =
SelectorParser::parse(Rule::selector_filter, sel).map_err(|e| e.to_string())?;
for selector in selection {
match selector.as_rule() {
Rule::doc_index => {
let index = parse_doc_index!(selector);
if index >= docs.len() {
return Err(format!("Document index of {} is out of bounds", index));
} else {
current = &docs[index];
}
}
Rule::doc_wildcard => {
for doc in docs.iter() {
result = result + doc.clone();
}
}
Rule::index => current = &parse_array_index!(selector, current),
Rule::chars => {
current = &parse_char!(selector, current);
if current != &Unstructured::<T>::Null {
key_path.push(parse_char_string!(selector));
}
}
Rule::ident => {
current = &parse_ident!(selector, current);
if current != &Unstructured::<T>::Null {
key_path.push(parse_ident_string!(selector));
}
}
Rule::range => current_owned = Some(parse_range!(selector, current)),
Rule::EOI | Rule::pipe => {
if !key_path.is_empty() {
let mut tree = Unstructured::<T>::Map(BTreeMap::default());
let mut pos = &mut tree;
for (i, path) in key_path.iter().enumerate() {
let mut new_doc = Unstructured::<T>::Map(BTreeMap::default());
if i == key_path.len() - 1 {
new_doc = new_doc
+ match current_owned {
Some(s) => s,
None => current.clone(),
};
current_owned = None;
current = &docs[0];
}
pos[&path] = new_doc;
pos = &mut pos[&path];
}
if tree != Unstructured::<T>::Null {
result = result + tree;
}
key_path.clear();
} else {
let temp = match current_owned {
Some(s) => s,
None => current.clone(),
};
if temp != Unstructured::<T>::Null {
result = result + temp;
}
current_owned = None;
current = &docs[0];
}
}
_ => return Err(format!("Invalid selector {}", selector)),
}
}
}
Ok(result)
}
}