Refactor filter building into types which can be chained up

State before was to generate functions which can be used as filter()
arguments.

State now is that there are types which can be lined up and be executed
as filters. And/Or/Not-Filters are provided and so are helper functions
to initialize objects of these filter types.

The BM module is adapted for this.
This commit is contained in:
Matthias Beyer 2015-12-30 16:43:00 +01:00
parent a2037f3909
commit a49113cb5f
2 changed files with 184 additions and 122 deletions

View file

@ -11,7 +11,10 @@ use module::Module;
use storage::parser::FileHeaderParser; use storage::parser::FileHeaderParser;
use storage::parser::Parser; use storage::parser::Parser;
use storage::json::parser::JsonHeaderParser; use storage::json::parser::JsonHeaderParser;
use module::helpers::cli::get_file_filter_by_cli; use module::helpers::cli::create_tag_filter;
use module::helpers::cli::create_hash_filter;
use module::helpers::cli::create_text_header_field_grep_filter;
use module::helpers::cli::create_content_grep_filter;
use module::helpers::cli::CliFileFilter; use module::helpers::cli::CliFileFilter;
mod header; mod header;
@ -119,7 +122,12 @@ impl<'a> BM<'a> {
use std::ops::Deref; use std::ops::Deref;
let parser = Parser::new(JsonHeaderParser::new(None)); let parser = Parser::new(JsonHeaderParser::new(None));
let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser, matches, "id", "match", "tags", Some("URL")); let filter = {
let hash_filter = create_hash_filter(matches, "id", true);
let text_filter = create_text_header_field_grep_filter(matches, "match", "URL", true);
let tags_filter = create_tag_filter(matches, "tags", true);
hash_filter.and(Box::new(text_filter)).and(Box::new(tags_filter))
};
let files = self.rt let files = self.rt
.store() .store()
@ -151,7 +159,12 @@ impl<'a> BM<'a> {
use open; use open;
let parser = Parser::new(JsonHeaderParser::new(None)); let parser = Parser::new(JsonHeaderParser::new(None));
let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser, matches, "id", "match", "tags", None); let filter : Box<CliFileFilter> = {
let hash_filter = create_hash_filter(matches, "id", true);
let text_filter = create_text_header_field_grep_filter(matches, "match", "URL", true);
let tags_filter = create_tag_filter(matches, "tags", true);
Box::new(hash_filter.and(Box::new(text_filter)).and(Box::new(tags_filter)))
};
let result = self.rt let result = self.rt
.store() .store()
.load_for_module(self, &parser) .load_for_module(self, &parser)
@ -191,7 +204,14 @@ impl<'a> BM<'a> {
*/ */
fn command_remove(&self, matches: &ArgMatches) -> bool { fn command_remove(&self, matches: &ArgMatches) -> bool {
let parser = Parser::new(JsonHeaderParser::new(None)); let parser = Parser::new(JsonHeaderParser::new(None));
let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser, matches, "id", "match", "tags", None);
let filter = {
let hash_filter = create_hash_filter(matches, "id", false);
let text_filter = create_text_header_field_grep_filter(matches, "match", "URL", false);
let tags_filter = create_tag_filter(matches, "tags", false);
hash_filter.or(Box::new(text_filter)).or(Box::new(tags_filter))
};
let result = self.rt let result = self.rt
.store() .store()
.load_for_module(self, &parser) .load_for_module(self, &parser)
@ -268,12 +288,14 @@ impl<'a> BM<'a> {
.unwrap_or(vec![]); .unwrap_or(vec![]);
let parser = Parser::new(JsonHeaderParser::new(None)); let parser = Parser::new(JsonHeaderParser::new(None));
let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser,
matches, let filter = {
"with_id", let hash_filter = create_hash_filter(matches, "with:id", false);
"with_match", let text_filter = create_text_header_field_grep_filter(matches, "with_match", "URL", false);
"with_tags", let tags_filter = create_tag_filter(matches, "with_tags", false);
None); hash_filter.or(Box::new(text_filter)).or(Box::new(tags_filter))
};
self.rt self.rt
.store() .store()
.load_for_module(self, &parser) .load_for_module(self, &parser)

View file

@ -16,52 +16,112 @@ pub trait CliFileFilter {
fn filter_file(&self, &Rc<RefCell<File>>) -> bool; fn filter_file(&self, &Rc<RefCell<File>>) -> bool;
} fn not(self) -> CliFileFilterNot
where Self: Sized + 'static
{
CliFileFilterNot {
a: Box::new(self),
}
}
struct CliFileFilterDefault { fn or(self, other: Box<CliFileFilter>) -> CliFileFilterOr
default: bool, where Self: Sized + 'static
} {
CliFileFilterOr {
a: Box::new(self),
b: other
}
}
impl CliFileFilter for CliFileFilterDefault { fn and(self, other: Box<CliFileFilter>) -> CliFileFilterAnd
where Self: Sized + 'static
fn filter_file(&self, _: &Rc<RefCell<File>>) -> bool { {
debug!("Filtering file with default value = {}", self.default); CliFileFilterAnd {
return self.default a: Box::new(self),
b: other
}
} }
} }
struct CliFileFilterByHash { pub struct CliFileFilterNot {
hash: FileHash, a: Box<CliFileFilter>,
}
impl CliFileFilter for CliFileFilterNot {
fn filter_file(&self, f: &Rc<RefCell<File>>) -> bool {
!self.a.filter_file(f)
}
}
pub struct CliFileFilterOr {
a: Box<CliFileFilter>,
b: Box<CliFileFilter>
}
impl CliFileFilter for CliFileFilterOr {
fn filter_file(&self, f: &Rc<RefCell<File>>) -> bool {
self.a.filter_file(f) || self.b.filter_file(f)
}
}
pub struct CliFileFilterAnd {
a: Box<CliFileFilter>,
b: Box<CliFileFilter>
}
impl CliFileFilter for CliFileFilterAnd {
fn filter_file(&self, f: &Rc<RefCell<File>>) -> bool {
self.a.filter_file(f) && self.b.filter_file(f)
}
}
pub struct CliFileFilterByHash {
default: bool,
hash: Option<FileHash>,
} }
impl CliFileFilter for CliFileFilterByHash { impl CliFileFilter for CliFileFilterByHash {
fn filter_file(&self, file: &Rc<RefCell<File>>) -> bool { fn filter_file(&self, file: &Rc<RefCell<File>>) -> bool {
debug!("Filtering file with hash = {}", self.hash); self.hash.clone().map(|h| {
let f = file.deref().borrow(); debug!("Filtering file with hash = {}", h);
f.id().get_id() == self.hash let f = file.deref().borrow();
f.id().get_id() == h
})
.unwrap_or(self.default)
} }
} }
struct CliFileFilterByDataRegex { pub struct CliFileFilterByDataRegex {
regex: Regex, default: bool,
regex: Option<Regex>,
} }
impl CliFileFilter for CliFileFilterByDataRegex { impl CliFileFilter for CliFileFilterByDataRegex {
fn filter_file(&self, file: &Rc<RefCell<File>>) -> bool { fn filter_file(&self, file: &Rc<RefCell<File>>) -> bool {
debug!("Filtering file with regex = {:?}", self.regex); self.regex.clone().map(|r| {
let f = file.deref().borrow(); debug!("Filtering file with regex = {:?}", r);
self.regex.is_match(&f.data()[..]) let f = file.deref().borrow();
r.is_match(&f.data()[..])
})
.unwrap_or(self.default)
} }
} }
struct CliFileFilterByHeaderRegex { pub struct CliFileFilterByHeaderRegex {
default: bool,
header_field_name: &'static str, header_field_name: &'static str,
regex: Regex, regex: Option<Regex>,
} }
impl CliFileFilter for CliFileFilterByHeaderRegex { impl CliFileFilter for CliFileFilterByHeaderRegex {
@ -69,20 +129,22 @@ impl CliFileFilter for CliFileFilterByHeaderRegex {
fn filter_file(&self, file: &Rc<RefCell<File>>) -> bool { fn filter_file(&self, file: &Rc<RefCell<File>>) -> bool {
use module::helpers::header::data::get_named_text_from_header; use module::helpers::header::data::get_named_text_from_header;
debug!("Filtering file (header field = {}) with regex = {:?}", self.regex.clone().map(|r| {
self.header_field_name, debug!("Filtering file (header field = {}) with regex = {:?}", self.header_field_name, r);
self.regex);
let f = file.deref().borrow(); let f = file.deref().borrow();
get_named_text_from_header(self.header_field_name, f.header()) get_named_text_from_header(self.header_field_name, f.header())
.map(|headerfield| self.regex.is_match(&headerfield[..])) .map(|headerfield| r.is_match(&headerfield[..]))
.unwrap_or(false) .unwrap_or(self.default)
})
.unwrap_or(self.default)
} }
} }
struct CliFileFilterByTags { pub struct CliFileFilterByTags {
tags: Vec<String>, default: bool,
tags: Option<Vec<String>>,
} }
impl CliFileFilter for CliFileFilterByTags { impl CliFileFilter for CliFileFilterByTags {
@ -90,98 +152,76 @@ impl CliFileFilter for CliFileFilterByTags {
fn filter_file(&self, file: &Rc<RefCell<File>>) -> bool { fn filter_file(&self, file: &Rc<RefCell<File>>) -> bool {
use module::helpers::header::tags::data::get_tags_from_header; use module::helpers::header::tags::data::get_tags_from_header;
debug!("Filtering file with tags = {:?}", self.tags); self.tags.clone().map(|ts| {
debug!("Filtering file with tags = {:?}", ts);
let f = file.deref().borrow(); let f = file.deref().borrow();
get_tags_from_header(f.header()) get_tags_from_header(f.header())
.iter() .iter()
.any(|tag| self.tags.iter().any(|remtag| remtag == tag)) .any(|tag| ts.iter().any(|remtag| remtag == tag))
})
.unwrap_or(self.default)
} }
} }
/** /*
* Helper function to get files from the store filtered by the constraints passed via the *
* CLI *
* Functions to generate filters
*
*
*/ */
pub fn get_file_filter_by_cli<HP>(parser: &Parser<HP>,
matches: &ArgMatches, pub fn create_hash_filter(matches: &ArgMatches, id_key: &'static str, default: bool) -> CliFileFilterByHash {
id_key: &'static str, CliFileFilterByHash {
match_key: &'static str, hash: matches.value_of(id_key).map(FileHash::from),
tag_key: &'static str, default: default
header_field_name: Option<&'static str>) }
-> Box<CliFileFilter> }
where HP: FileHeaderParser,
{ pub fn create_content_grep_filter(matches: &ArgMatches, match_key: &'static str, default: bool) -> CliFileFilterByDataRegex {
if matches.is_present(id_key) { use std::process::exit;
Box::new(CliFileFilterByHash { hash: FileHash::from(matches.value_of(id_key).unwrap()) })
} else if matches.is_present(match_key) { CliFileFilterByDataRegex {
let matcher = String::from(matches.value_of(match_key).unwrap()); regex: matches.value_of(match_key).map(|m| {
header_field_name Regex::new(&m[..]).unwrap_or_else(|e| {
.and_then(|header_field_name| { error!("Regex compiler error: {}", e);
Some(get_files_by_header_field_match_filter(parser, exit(1);
&matcher,
header_field_name))
}) })
.unwrap_or(get_file_by_match_filter(parser, &matcher)) }),
} else if matches.is_present(tag_key) { default: default,
let tags = matches.value_of(tag_key) }
.unwrap() }
.split(",")
.map(String::from) pub fn create_text_header_field_grep_filter(matches: &ArgMatches,
.collect::<Vec<String>>(); match_key: &'static str,
get_file_by_tags_filter(tags) header_field_name: &'static str,
} else { default: bool)
Box::new(CliFileFilterDefault { default: true }) -> CliFileFilterByHeaderRegex
{
CliFileFilterByHeaderRegex {
default: default,
header_field_name: header_field_name,
regex: matches.value_of(match_key)
.map(|m| {
Regex::new(&m[..]).unwrap_or_else(|e| {
error!("Regex compiler error: {}", e);
exit(1);
})
}),
} }
} }
/** pub fn create_tag_filter(matches: &ArgMatches, tag_key: &'static str, default: bool) -> CliFileFilterByTags {
* Get files from the store, filtere by Regex
*/
fn get_file_by_match_filter<HP>(parser: &Parser<HP>, matcher: &String)
-> Box<CliFileFilter>
where HP: FileHeaderParser
{
let parser = Parser::new(JsonHeaderParser::new(None));
let re = Regex::new(&matcher[..]).unwrap_or_else(|e| {
error!("Cannot build regex out of '{}'", matcher);
error!("{}", e);
exit(1);
});
debug!("Compiled '{}' to regex: '{:?}'", matcher, re); CliFileFilterByTags {
default: default,
Box::new(CliFileFilterByDataRegex { regex: re }) tags: matches.value_of(tag_key)
} .map(|m| m.split(",")
.map(String::from)
fn get_files_by_header_field_match_filter<HP>(parser: &Parser<HP>, .collect::<Vec<String>>()
matcher: &String, ),
header_field_name: &'static str) }
-> Box<CliFileFilter>
where HP: FileHeaderParser,
{
let parser = Parser::new(JsonHeaderParser::new(None));
let re = Regex::new(&matcher[..]).unwrap_or_else(|e| {
error!("Cannot build regex out of '{}'", matcher);
error!("{}", e);
exit(1);
});
debug!("Compiled '{}' to regex: '{:?}'", matcher, re);
Box::new(CliFileFilterByHeaderRegex {
header_field_name: header_field_name,
regex: re
})
}
/**
* Get files from the store, filtere by tags
*/
fn get_file_by_tags_filter(tags: Vec<String>)
-> Box<CliFileFilter>
{
Box::new(CliFileFilterByTags { tags: tags })
} }