unstructured 0.5.1

Generic types for unstructured data
Documentation
use crate::*;
use pest::Parser;
use pest_derive::*;
use std::collections::BTreeMap;

// #[cfg(test)]
// mod test {
//     use super::*;

//     #[derive(Serialize)]
//     struct TestStruct {
//         val: String,
//         vals: Vec<usize>,
//     }

//     #[test]
//     fn test_selector() {
//         let mut doc = Unstructured::<T>::new(TestStruct {
//             val: "some_val".to_string(),
//             vals: vec![1, 2, 3],
//         })
//         .unwrap();

//         assert_eq!(&mut doc["vals"][1], &mut Unstructured::<T>::Number(Number::U64(2)));
//         assert_eq!(doc.select("/vals/1").unwrap().clone(), Unstructured::<T>::Number(Number::U64(2)));

//         assert_eq!(
//             doc.select("/vals/1").unwrap().clone(),
//             doc.select_mut("/vals/1").unwrap().clone()
//         );

//         assert_eq!(
//             doc.select(".[\"vals\"].[1]").unwrap(),
//             doc.select("/vals/1").unwrap()
//         );
//     }

//     #[test]
//     fn test_filter() -> Result<(), String> {
//         let docs = vec![
//             Unstructured::<T>::new(TestStruct {
//                 val: "some_val".to_string(),
//                 vals: vec![1, 2, 3],
//             })
//             .map_err(|e| format!("{:?}", e))?,
//             Unstructured::<T>::new(TestStruct {
//                 val: "Another".to_string(),
//                 vals: vec![4, 5, 6],
//             })
//             .map_err(|e| format!("{:?}", e))?,
//         ];

//         let result = Unstructured::<T>::filter(&docs, "[1].val | [0].vals.[1:3]")?;
//         println!("{}", result);
//         Ok(())
//     }
// }

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)
    }
}