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:
parent
a2037f3909
commit
a49113cb5f
2 changed files with 184 additions and 122 deletions
|
@ -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)
|
||||||
|
|
|
@ -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 })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue