diff --git a/libimagentryfilter/Cargo.toml b/libimagentryfilter/Cargo.toml new file mode 100644 index 00000000..00aae942 --- /dev/null +++ b/libimagentryfilter/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "libimagentryfilter" +version = "0.1.0" +authors = ["Matthias Beyer "] + +[dependencies] +clap = "1.5.5" +itertools = "0.4.7" +log = "0.3.4" +regex = "0.1.48" +toml = "0.1.27" + +[dependencies.libimagstore] +path = "../libimagstore" + diff --git a/libimagentryfilter/src/builtin/content/grep.rs b/libimagentryfilter/src/builtin/content/grep.rs new file mode 100644 index 00000000..a3d89af4 --- /dev/null +++ b/libimagentryfilter/src/builtin/content/grep.rs @@ -0,0 +1,57 @@ +use std::convert::Into; + +use regex::Regex; +use regex::Error as RError; + +use libimagstore::store::Entry; + +use builtin::header::field_path::FieldPath; +use filter::Filter; + +pub trait IntoRegex { + + fn into_regex(self) -> Result; + +} + +impl<'a> IntoRegex for &'a str { + + fn into_regex(self) -> Result { + Regex::new(self) + } +} + +impl<'a> IntoRegex for Regex { + + fn into_regex(self) -> Result { + Ok(self) + } +} + +pub struct ContentGrep { + regex: Regex, +} + +impl ContentGrep { + + pub fn new(regex: IR) -> Result + where IR: IntoRegex + { + regex.into_regex() + .map(|reg| { + ContentGrep { + regex: reg, + } + }) + } + +} + +impl Filter for ContentGrep { + + fn filter(&self, e: &Entry) -> bool { + self.regex.captures(&e.get_content()[..]).is_some() + } + +} + diff --git a/libimagentryfilter/src/builtin/content/length/is_over.rs b/libimagentryfilter/src/builtin/content/length/is_over.rs new file mode 100644 index 00000000..f8016805 --- /dev/null +++ b/libimagentryfilter/src/builtin/content/length/is_over.rs @@ -0,0 +1,28 @@ +use libimagstore::store::Entry; + +use builtin::header::field_path::FieldPath; +use filter::Filter; + +pub struct ContentLengthIsOver { + val: usize +} + +impl ContentLengthIsOver { + + pub fn new(value: usize) -> ContentLengthIsOver { + ContentLengthIsOver { + val: value, + } + } + +} + +impl Filter for ContentLengthIsOver { + + fn filter(&self, e: &Entry) -> bool { + e.get_content().len() > self.val + } + +} + + diff --git a/libimagentryfilter/src/builtin/content/length/is_under.rs b/libimagentryfilter/src/builtin/content/length/is_under.rs new file mode 100644 index 00000000..07ade8dc --- /dev/null +++ b/libimagentryfilter/src/builtin/content/length/is_under.rs @@ -0,0 +1,28 @@ +use libimagstore::store::Entry; + +use builtin::header::field_path::FieldPath; +use filter::Filter; + +pub struct ContentLengthIsUnder { + val: usize +} + +impl ContentLengthIsUnder { + + pub fn new(value: usize) -> ContentLengthIsUnder { + ContentLengthIsUnder { + val: value, + } + } + +} + +impl Filter for ContentLengthIsUnder { + + fn filter(&self, e: &Entry) -> bool { + e.get_content().len() < self.val + } + +} + + diff --git a/libimagentryfilter/src/builtin/content/length/mod.rs b/libimagentryfilter/src/builtin/content/length/mod.rs new file mode 100644 index 00000000..94121f96 --- /dev/null +++ b/libimagentryfilter/src/builtin/content/length/mod.rs @@ -0,0 +1,2 @@ +pub mod is_over; +pub mod is_under; diff --git a/libimagentryfilter/src/builtin/content/mod.rs b/libimagentryfilter/src/builtin/content/mod.rs new file mode 100644 index 00000000..b0ec6127 --- /dev/null +++ b/libimagentryfilter/src/builtin/content/mod.rs @@ -0,0 +1,2 @@ +pub mod grep; +pub mod length; diff --git a/libimagentryfilter/src/builtin/header/field_eq.rs b/libimagentryfilter/src/builtin/header/field_eq.rs new file mode 100644 index 00000000..fc4e1e77 --- /dev/null +++ b/libimagentryfilter/src/builtin/header/field_eq.rs @@ -0,0 +1,36 @@ +use libimagstore::store::Entry; + +use builtin::header::field_path::FieldPath; +use filter::Filter; + +use toml::Value; + +/// Check whether certain header field in a entry is equal to a value +pub struct FieldEq { + header_field_path: FieldPath, + expected_value: Value +} + +impl FieldEq { + + pub fn new(path: FieldPath, expected_value: Value) -> FieldEq { + FieldEq { + header_field_path: path, + expected_value: expected_value, + } + } + +} + +impl Filter for FieldEq { + + fn filter(&self, e: &Entry) -> bool { + let header = e.get_header(); + self.header_field_path + .walk(header) + .map(|v| self.expected_value == v.clone()) + .unwrap_or(false) + } + +} + diff --git a/libimagentryfilter/src/builtin/header/field_exists.rs b/libimagentryfilter/src/builtin/header/field_exists.rs new file mode 100644 index 00000000..45bc870a --- /dev/null +++ b/libimagentryfilter/src/builtin/header/field_exists.rs @@ -0,0 +1,31 @@ +use libimagstore::store::Entry; + +use builtin::header::field_path::FieldPath; +use filter::Filter; + +use toml::Value; + +pub struct FieldExists { + header_field_path: FieldPath, +} + +impl FieldExists { + + pub fn new(path: FieldPath) -> FieldExists { + FieldExists { + header_field_path: path, + } + } + +} + +impl Filter for FieldExists { + + fn filter(&self, e: &Entry) -> bool { + let header = e.get_header(); + self.header_field_path.walk(header).is_some() + } + +} + + diff --git a/libimagentryfilter/src/builtin/header/field_grep.rs b/libimagentryfilter/src/builtin/header/field_grep.rs new file mode 100644 index 00000000..f1535602 --- /dev/null +++ b/libimagentryfilter/src/builtin/header/field_grep.rs @@ -0,0 +1,43 @@ +use regex::Regex; +use toml::Value; + +use libimagstore::store::Entry; + +use builtin::header::field_path::FieldPath; +use filter::Filter; + +/// Check whether certain header field in a entry is equal to a value +pub struct FieldGrep { + header_field_path: FieldPath, + grep: Regex, +} + +impl FieldGrep { + + pub fn new(path: FieldPath, grep: Regex) -> FieldGrep { + FieldGrep { + header_field_path: path, + grep: grep, + } + } + +} + +impl Filter for FieldGrep { + + fn filter(&self, e: &Entry) -> bool { + let header = e.get_header(); + self.header_field_path + .walk(header) + .map(|v| { + match v { + Value::String(s) => self.grep.captures(&s[..]).is_some(), + _ => false, + } + }) + .unwrap_or(false) + } + +} + + diff --git a/libimagentryfilter/src/builtin/header/field_isempty.rs b/libimagentryfilter/src/builtin/header/field_isempty.rs new file mode 100644 index 00000000..83f74984 --- /dev/null +++ b/libimagentryfilter/src/builtin/header/field_isempty.rs @@ -0,0 +1,45 @@ +use libimagstore::store::Entry; + +use builtin::header::field_path::FieldPath; +use filter::Filter; + +use toml::Value; + +pub struct FieldIsEmpty { + header_field_path: FieldPath, +} + +impl FieldIsEmpty { + + pub fn new(path: FieldPath) -> FieldIsEmpty { + FieldIsEmpty { + header_field_path: path, + } + } + +} + +impl Filter for FieldIsEmpty { + + fn filter(&self, e: &Entry) -> bool { + let header = e.get_header(); + self.header_field_path + .walk(header) + .map(|v| { + match v { + Value::Array(a) => a.is_empty(), + Value::Boolean(_) => false, + Value::Float(_) => false, + Value::Integer(_) => false, + Value::String(_) => false, + Value::Table(t) => t.is_empty(), + _ => true, + } + }) + .unwrap_or(false) + } + +} + + + diff --git a/libimagentryfilter/src/builtin/header/field_istype.rs b/libimagentryfilter/src/builtin/header/field_istype.rs new file mode 100644 index 00000000..23191611 --- /dev/null +++ b/libimagentryfilter/src/builtin/header/field_istype.rs @@ -0,0 +1,62 @@ +use libimagstore::store::Entry; + +use builtin::header::field_path::FieldPath; +use filter::Filter; + +use toml::Value; + +pub enum Type { + Array, + Boolean, + Float, + Integer, + None, + String, + Table, +} + +impl Type { + + fn matches(&self, v: &Value) -> bool { + match (self, v) { + (&Type::String, &Value::String(_)) => true, + (&Type::Integer, &Value::Integer(_)) => true, + (&Type::Float, &Value::Float(_)) => true, + (&Type::Boolean, &Value::Boolean(_)) => true, + (&Type::Array, &Value::Array(_)) => true, + (&Type::Table, &Value::Table(_)) => true, + _ => false, + } + } + +} + +pub struct FieldIsType { + header_field_path: FieldPath, + expected_type: Type, +} + +impl FieldIsType { + + pub fn new(path: FieldPath, expected_type: Type) -> FieldIsType { + FieldIsType { + header_field_path: path, + expected_type: expected_type, + } + } + +} + +impl Filter for FieldIsType { + + fn filter(&self, e: &Entry) -> bool { + let header = e.get_header(); + self.header_field_path + .walk(header) + .map(|v| self.expected_type.matches(&v)) + .unwrap_or(false) + } + +} + + diff --git a/libimagentryfilter/src/builtin/header/field_path/element.rs b/libimagentryfilter/src/builtin/header/field_path/element.rs new file mode 100644 index 00000000..d6ad599a --- /dev/null +++ b/libimagentryfilter/src/builtin/header/field_path/element.rs @@ -0,0 +1,47 @@ +use std::fmt::{Display, Formatter}; +use std::fmt::Error as FmtError; + +use toml::Value; + +#[derive(Clone, Debug)] +pub struct FieldPathElement { + name: String +} + +impl FieldPathElement { + + pub fn new(name: String) -> FieldPathElement { + FieldPathElement { name: name } + } + + pub fn apply(&self, value: Value) -> Option { + use std::str::FromStr; + use std::ops::Index; + + match value { + Value::Table(t) => { + t.get(&self.name).map(|a| a.clone()) + }, + + Value::Array(a) => { + usize::from_str(&self.name[..]) + .ok() + .and_then(|i| Some(a[i].clone())) + }, + + _ => None, + } + } + +} + +impl Display for FieldPathElement { + + fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> { + try!(write!(fmt, "{}", self.name)); + Ok(()) + } + +} + + diff --git a/libimagentryfilter/src/builtin/header/field_path/error.rs b/libimagentryfilter/src/builtin/header/field_path/error.rs new file mode 100644 index 00000000..69703cbf --- /dev/null +++ b/libimagentryfilter/src/builtin/header/field_path/error.rs @@ -0,0 +1,39 @@ +use std::fmt::{Display, Formatter}; +use std::fmt::Error as FmtError; +use std::error::Error; + +use builtin::header::field_path::element::FieldPathElement; + +#[derive(Debug)] +pub struct FieldPathParsingError { + source: String, + token: FieldPathElement +} + +impl FieldPathParsingError { + + pub fn new(source: String, token: FieldPathElement) -> FieldPathParsingError { + FieldPathParsingError { source: source, token: token } + } +} + +impl Display for FieldPathParsingError { + + fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> { + try!(write!(fmt, "Failed to compile '{}', failed at: '{}'", self.source, self.token)); + Ok(()) + } + +} + +impl Error for FieldPathParsingError { + + fn description(&self) -> &str { + &self.source[..] + } + + fn cause(&self) -> Option<&Error> { + None + } + +} diff --git a/libimagentryfilter/src/builtin/header/field_path/mod.rs b/libimagentryfilter/src/builtin/header/field_path/mod.rs new file mode 100644 index 00000000..0760c596 --- /dev/null +++ b/libimagentryfilter/src/builtin/header/field_path/mod.rs @@ -0,0 +1,44 @@ +use std::fmt::{Display, Formatter}; +use std::fmt::Error as FmtError; +use std::error::Error; + +use toml::Value; + +pub mod element; +pub mod error; + +use libimagstore::store::Entry; +use libimagstore::store::EntryHeader; + +use builtin::header::field_path::element::FieldPathElement; +use builtin::header::field_path::error::FieldPathParsingError; + +pub struct FieldPath { + elements: Vec, +} + +impl FieldPath { + + pub fn new(elements: Vec) -> FieldPath { + FieldPath { + elements: elements, + } + } + + pub fn compile(source: String) -> Result { + unimplemented!() + } + + pub fn walk(&self, e: &EntryHeader) -> Option { + let init_val : Value = Value::Table(e.toml().clone()); + + self.elements + .clone() + .into_iter() + .fold(Some(init_val), |acc: Option, token: FieldPathElement| { + acc.and_then(|element| token.apply(element)) + }) + } + +} + diff --git a/libimagentryfilter/src/builtin/header/mod.rs b/libimagentryfilter/src/builtin/header/mod.rs new file mode 100644 index 00000000..886e5ce6 --- /dev/null +++ b/libimagentryfilter/src/builtin/header/mod.rs @@ -0,0 +1,6 @@ +pub mod field_eq; +pub mod field_exists; +pub mod field_grep; +pub mod field_isempty; +pub mod field_istype; +pub mod field_path; diff --git a/libimagentryfilter/src/builtin/mod.rs b/libimagentryfilter/src/builtin/mod.rs new file mode 100644 index 00000000..260f1a1b --- /dev/null +++ b/libimagentryfilter/src/builtin/mod.rs @@ -0,0 +1,2 @@ +pub mod content; +pub mod header; diff --git a/libimagentryfilter/src/cli.rs b/libimagentryfilter/src/cli.rs new file mode 100644 index 00000000..e69de29b diff --git a/libimagentryfilter/src/filter.rs b/libimagentryfilter/src/filter.rs new file mode 100644 index 00000000..2d0015a1 --- /dev/null +++ b/libimagentryfilter/src/filter.rs @@ -0,0 +1,54 @@ +use libimagstore::store::Entry; + +pub use ops::and::And; +pub use ops::not::Not; +pub use ops::or::Or; + +pub trait Filter { + + fn filter(&self, &Entry) -> bool; + + fn not(self) -> Not + where Self: Sized + 'static + { + Not::new(Box::new(self)) + } + + fn or(self, other: Box) -> Or + where Self: Sized + 'static + { + Or::new(Box::new(self), other) + } + + fn or_not(self, other: Box) -> Or + where Self: Sized + 'static + { + self.or(Box::new(Not::new(other))) + } + + fn or3(self, other: Box, other2: Box) -> Or + where Self: Sized + 'static + { + Or::new(Box::new(self), Box::new(Or::new(other, other2))) + } + + fn and(self, other: Box) -> And + where Self: Sized + 'static + { + And::new(Box::new(self), other) + } + + fn and3(self, other: Box, other2: Box) -> And + where Self: Sized + 'static + { + And::new(Box::new(self), Box::new(And::new(other, other2))) + } + + fn and_not(self, other: Box) -> And + where Self: Sized + 'static + { + self.and(Box::new(Not::new(other))) + } + +} + diff --git a/libimagentryfilter/src/lib.rs b/libimagentryfilter/src/lib.rs new file mode 100644 index 00000000..0a3c4316 --- /dev/null +++ b/libimagentryfilter/src/lib.rs @@ -0,0 +1,12 @@ +#[macro_use] extern crate log; + +extern crate itertools; +extern crate regex; +extern crate toml; + +extern crate libimagstore; + +pub mod cli; +pub mod builtin; +pub mod filter; +pub mod ops; diff --git a/libimagentryfilter/src/ops/and.rs b/libimagentryfilter/src/ops/and.rs new file mode 100644 index 00000000..79470b9b --- /dev/null +++ b/libimagentryfilter/src/ops/and.rs @@ -0,0 +1,24 @@ +use libimagstore::store::Entry; + +use filter::Filter; + +pub struct And { + a: Box, + b: Box +} + +impl And { + + pub fn new(a: Box, b: Box) -> And { + And { a: a, b: b } + } + +} + +impl Filter for And { + + fn filter(&self, e: &Entry) -> bool { + self.a.filter(e) && self.b.filter(e) + } + +} diff --git a/libimagentryfilter/src/ops/mod.rs b/libimagentryfilter/src/ops/mod.rs new file mode 100644 index 00000000..668eedc5 --- /dev/null +++ b/libimagentryfilter/src/ops/mod.rs @@ -0,0 +1,3 @@ +pub mod and; +pub mod not; +pub mod or; diff --git a/libimagentryfilter/src/ops/not.rs b/libimagentryfilter/src/ops/not.rs new file mode 100644 index 00000000..a709d95b --- /dev/null +++ b/libimagentryfilter/src/ops/not.rs @@ -0,0 +1,23 @@ +use libimagstore::store::Entry; + +use filter::Filter; + +pub struct Not { + a: Box +} + +impl Not { + + pub fn new(a: Box) -> Not { + Not { a: a } + } + +} + +impl Filter for Not { + + fn filter(&self, e: &Entry) -> bool { + !self.a.filter(e) + } + +} diff --git a/libimagentryfilter/src/ops/or.rs b/libimagentryfilter/src/ops/or.rs new file mode 100644 index 00000000..e2ca810b --- /dev/null +++ b/libimagentryfilter/src/ops/or.rs @@ -0,0 +1,24 @@ +use libimagstore::store::Entry; + +use filter::Filter; + +pub struct Or { + a: Box, + b: Box +} + +impl Or { + + pub fn new(a: Box, b: Box) -> Or { + Or { a: a, b: b } + } + +} + +impl Filter for Or { + + fn filter(&self, e: &Entry) -> bool { + self.a.filter(e) || self.b.filter(e) + } + +}