pub mod error;
pub mod operator;
mod parser;
mod query;
mod scanner;
mod token;
pub mod value;
pub use crate::error::FltrError;
use crate::operator::{OperatorFn, Operators};
use crate::parser::{parse, AsValueFn, Parser};
use crate::value::Value;
pub type Result<T> = core::result::Result<T, FltrError>;
pub trait AsString {
fn as_string(&self) -> String;
}
macro_rules! as_string {
( $($t:ty) + ) => {
$(
impl AsString for $t {
fn as_string(&self) -> String {
self.to_string()
}
}
impl AsString for Option<$t> {
fn as_string(&self) -> String {
match self {
Some(v) => v.to_string(),
None => String::new(),
}
}
}
) *
}
}
as_string! { bool char &str String usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 f32 f64 }
pub trait Filterable: PartialEq<Value> + PartialOrd<Value> + AsString {}
impl<V: PartialEq<Value> + PartialOrd<Value> + AsString> Filterable for V {}
pub trait PathResolver {
fn path_to_index(path: &str) -> Option<usize>;
fn value(&self, idx: usize) -> &dyn Filterable;
}
impl<F: Filterable> PathResolver for F {
fn path_to_index(_path: &str) -> Option<usize> {
Some(0)
}
fn value(&self, _idx: usize) -> &dyn Filterable {
self
}
}
pub type Predicate<PR> = Box<dyn Fn(&PR) -> bool>;
pub fn query<PR: PathResolver + 'static>(query: &str) -> Result<Predicate<PR>> {
crate::query::query(parse(query)?, &Operators::<PR>::default())
}
pub struct Query<PR> {
ops: Operators<PR>,
as_value_fn: Vec<(&'static str, AsValueFn)>,
}
impl<PR: PathResolver + 'static> Query<PR> {
pub fn build() -> Self {
Self {
ops: Operators::default(),
as_value_fn: vec![],
}
}
pub fn operators(mut self, ops: &[(&'static str, OperatorFn<PR>)]) -> Self {
self.ops.op.extend_from_slice(ops);
self
}
pub fn as_value_fn(mut self, fns: &[(&'static str, AsValueFn)]) -> Self {
self.as_value_fn.extend_from_slice(fns);
self
}
pub fn query(&self, query: &str) -> Result<Predicate<PR>> {
let mut p = Parser::new(query);
p.ops = self.ops.get_ops_names();
p.as_value_fns = self.as_value_fn.clone();
crate::query::query(p.parse()?, &self.ops)
}
}
#[cfg(test)]
mod test {
use super::*;
use test_case::test_case;
#[test]
fn iter_char_space() -> Result<()> {
let result: Vec<char> = [' ', 'a', ' ', 'b', ' ']
.into_iter()
.filter(query(r#"= ' '"#)?)
.collect();
assert_eq!(vec![' ', ' ', ' '], result);
Ok(())
}
#[test_case(" > 1 " => vec![Some(2), Some(3)] ; "> 1" )]
#[test_case(" one_of [1, 2] " => vec![Some(1), Some(2)] ; "one_of 1 2" )]
#[test_case(" one_of [1, none] " => vec![None, Some(1), None, None] ; "one_of 1 none" )]
#[test_case(" = " => vec![None, None, None] ; "eq" )]
#[test_case(" = none" => vec![None, None, None] ; "eq none" )]
#[test_case(" = None" => vec![None, None, None] ; "eq upper None" )]
#[test_case(" = null" => vec![None, None, None] ; "eq null" )]
#[test_case(" = Null" => vec![None, None, None] ; "eq upper Null" )]
#[test_case(" is_empty " => vec![None, None, None] ; "is_empty")]
#[test_case(" not is_empty" => vec![Some(1), Some(2), Some(3)] ; "not is_empty" )]
#[test_case(" not < 2" => vec![None, None, Some(2), Some(3), None] ; "not less 2" )]
fn iter_option(query_str: &str) -> Vec<Option<i32>> {
let result: Vec<Option<i32>> = [None, Some(1), None, Some(2), Some(3), None]
.into_iter()
.filter(query(query_str).unwrap())
.collect();
result
}
#[derive(PartialEq, Debug)]
struct Point {
x: i8,
y: i16,
}
impl Point {
fn new(x: i8, y: i16) -> Self {
Self { x, y }
}
}
impl PathResolver for Point {
fn path_to_index(path: &str) -> Option<usize> {
match path {
"x" => Some(0),
"y" => Some(1),
_ => None,
}
}
fn value(&self, idx: usize) -> &dyn Filterable {
match idx {
0 => &self.x,
_ => &self.y,
}
}
}
#[cfg(feature = "regex")]
#[test]
fn iter_regex() -> Result<()> {
assert_eq!(
2,
[1, 22, 333]
.into_iter()
.filter(query(r#"regex "[0-9]{2}""#)?)
.count()
);
Ok(())
}
#[cfg(feature = "regex")]
#[test]
fn iter_point_regex() -> Result<()> {
assert_eq!(
1,
[Point::new(22, 4), Point::new(3, 5)]
.into_iter()
.filter(query(r#"x regex "[0-9]{2}""#)?)
.count()
);
Ok(())
}
#[test]
fn iter_point_fltrs() -> Result<()> {
assert_eq!(
1,
[Point::new(2, 4), Point::new(3, 5)]
.into_iter()
.filter(query("x > 1 and y < 5")?)
.count()
);
Ok(())
}
#[test]
fn iter_point_one_of() -> Result<()> {
assert_eq!(
2,
[Point::new(2, 4), Point::new(3, 5), Point::new(4, 6)]
.into_iter()
.filter(query("x one_of [1, 2, 7, 4]")?)
.count()
);
Ok(())
}
#[test]
fn iter_str_empty() -> Result<()> {
let result: Vec<&str> = ["", "abc", "", "xyz", ""]
.into_iter()
.filter(query("is_empty")?)
.collect();
assert_eq!(vec!["", "", ""], result);
let result: Vec<&str> = ["", "abc", "", "xyz", ""]
.into_iter()
.filter(query(r#"= """#)?)
.collect();
assert_eq!(vec!["", "", ""], result);
Ok(())
}
#[test]
fn iter_str_not_empty() -> Result<()> {
let result: Vec<&str> = ["", "abc", "", "xyz", ""]
.into_iter()
.filter(query("not is_empty")?)
.collect();
assert_eq!(vec!["abc", "xyz"], result);
let result: Vec<&str> = ["", "abc", "", "xyz", ""]
.into_iter()
.filter(query(r#"not ( = "")"#)?)
.collect();
assert_eq!(vec!["abc", "xyz"], result);
Ok(())
}
#[test]
fn iter_str_one_of_empty() -> Result<()> {
let result: Vec<&str> = ["", "abc", "", "xyz", ""]
.into_iter()
.filter(query(r#"one_of [""]"#)?)
.collect();
assert_eq!(vec!["", "", ""], result);
Ok(())
}
}