Refactor: Provide generic cli-to-filter-function generator

The refactors functions from the BM module into more generic functions
for all modules.

The previous state was, that there were functions in the BM module which
were used to load from the store and applying some filters.

The new state is that there are functions to generate the filter
functions which are then used to filter the loaded files from the store,
so we can apply some more functionality on the filtered list.
This commit is contained in:
Matthias Beyer 2015-12-30 12:30:45 +01:00
parent 09aa7d9ec3
commit 7bbe1bb6d0
3 changed files with 208 additions and 105 deletions

View file

@ -18,6 +18,8 @@ use storage::file::File;
use storage::parser::FileHeaderParser;
use storage::parser::Parser;
use storage::json::parser::JsonHeaderParser;
use module::helpers::cli::get_file_filter_by_cli;
use module::helpers::cli::CliFileFilter;
mod header;
@ -136,15 +138,13 @@ impl<'a> BM<'a> {
fn command_remove(&self, matches: &ArgMatches) -> bool {
use std::process::exit;
let (filtered, files) = self.get_files(matches, "id", "match", "tags");
if !filtered {
error!("Unexpected error. Exiting");
exit(1);
}
let result = files
let parser = Parser::new(JsonHeaderParser::new(None));
let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser, matches, "id", "match", "tags", None);
let result = self.rt
.store()
.load_for_module(self, &parser)
.iter()
.filter(|file| filter.filter_file(file))
.map(|file| {
debug!("File loaded, can remove now: {:?}", file);
let f = file.deref().borrow();
@ -209,15 +209,18 @@ impl<'a> BM<'a> {
})
.unwrap_or(vec![]);
let (filter, files) = self.get_files(matches, "with_id", "with_match", "with_tags");
if !filter {
warn!("There were no filter applied when loading the files");
}
let parser = Parser::new(JsonHeaderParser::new(None));
files
let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser,
matches,
"with_id",
"with_match",
"with_tags",
None);
self.rt
.store()
.load_for_module(self, &parser)
.into_iter()
.filter(|file| filter.filter_file(file))
.map(|file| {
debug!("Remove tags from file: {:?}", file);
@ -250,96 +253,6 @@ impl<'a> BM<'a> {
.all(|x| x)
}
/**
* Helper function to get files from the store filtered by the constraints passed via the
* CLI
*/
fn get_files(&self,
matches: &ArgMatches,
id_key: &'static str,
match_key: &'static str,
tag_key: &'static str)
-> (bool, Vec<Rc<RefCell<File>>>)
{
if matches.is_present(id_key) {
let hash = FileHash::from(matches.value_of(id_key).unwrap());
(true, self.get_files_by_id(hash))
} else if matches.is_present(match_key) {
let matcher = String::from(matches.value_of(match_key).unwrap());
(true, self.get_files_by_match(matcher))
} else if matches.is_present(tag_key) {
let tags = matches.value_of(tag_key)
.unwrap()
.split(",")
.map(String::from)
.collect::<Vec<String>>();
(true, self.get_files_by_tags(tags))
} else {
// get all files
let parser = Parser::new(JsonHeaderParser::new(None));
(false, self.rt.store().load_for_module(self, &parser))
}
}
/**
* Get files from the store, filtere by ID
*/
fn get_files_by_id(&self, hash: FileHash) -> Vec<Rc<RefCell<File>>> {
let parser = Parser::new(JsonHeaderParser::new(None));
self.rt
.store()
.load_by_hash(self, &parser, hash)
.map(|f| vec![f])
.unwrap_or(vec![])
}
/**
* Get files from the store, filtere by Regex
*/
fn get_files_by_match(&self, matcher: String) -> Vec<Rc<RefCell<File>>> {
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);
self.rt
.store()
.load_for_module(self, &parser)
.into_iter()
.filter(|file| {
let f = file.deref().borrow();
let url = get_url_from_header(f.header());
debug!("url = {:?}", url);
url.map(|u| {
debug!("Matching '{}' ~= '{}'", re.as_str(), u);
re.is_match(&u[..])
}).unwrap_or(false)
})
.collect()
}
/**
* Get files from the store, filtere by tags
*/
fn get_files_by_tags(&self, tags: Vec<String>) -> Vec<Rc<RefCell<File>>> {
let parser = Parser::new(JsonHeaderParser::new(None));
self.rt
.store()
.load_for_module(self, &parser)
.into_iter()
.filter(|file| {
let f = file.deref().borrow();
get_tags_from_header(f.header()).iter().any(|tag| {
tags.iter().any(|remtag| remtag == tag)
})
})
.collect()
}
}
/**

189
src/module/helpers/cli.rs Normal file
View file

@ -0,0 +1,189 @@
use std::rc::Rc;
use std::cell::RefCell;
use std::ops::Deref;
use std::process::exit;
use clap::ArgMatches;
use regex::Regex;
use storage::file::File;
use storage::file::hash::FileHash;
use storage::file::header::data::FileHeaderData;
use storage::file::id::FileID;
use storage::json::parser::JsonHeaderParser;
use storage::parser::FileHeaderParser;
use storage::parser::Parser;
pub trait CliFileFilter {
fn filter_file(&self, &Rc<RefCell<File>>) -> bool;
}
struct CliFileFilterDefault {
default: bool,
}
impl CliFileFilter for CliFileFilterDefault {
fn filter_file(&self, _: &Rc<RefCell<File>>) -> bool {
debug!("Filtering file with default value = {}", self.default);
return self.default
}
}
struct CliFileFilterByHash {
hash: FileHash,
}
impl CliFileFilter for CliFileFilterByHash {
fn filter_file(&self, file: &Rc<RefCell<File>>) -> bool {
debug!("Filtering file with hash = {}", self.hash);
let f = file.deref().borrow();
f.id().get_id() == self.hash
}
}
struct CliFileFilterByDataRegex {
regex: Regex,
}
impl CliFileFilter for CliFileFilterByDataRegex {
fn filter_file(&self, file: &Rc<RefCell<File>>) -> bool {
debug!("Filtering file with regex = {:?}", self.regex);
let f = file.deref().borrow();
self.regex.is_match(&f.data()[..])
}
}
struct CliFileFilterByHeaderRegex {
header_field_name: &'static str,
regex: Regex,
}
impl CliFileFilter for CliFileFilterByHeaderRegex {
fn filter_file(&self, file: &Rc<RefCell<File>>) -> bool {
use module::helpers::header::data::get_named_text_from_header;
debug!("Filtering file (header field = {}) with regex = {:?}",
self.header_field_name,
self.regex);
let f = file.deref().borrow();
get_named_text_from_header(self.header_field_name, f.header())
.map(|headerfield| self.regex.is_match(&headerfield[..]))
.unwrap_or(false)
}
}
struct CliFileFilterByTags {
tags: Vec<String>,
}
impl CliFileFilter for CliFileFilterByTags {
fn filter_file(&self, file: &Rc<RefCell<File>>) -> bool {
use module::helpers::header::tags::data::get_tags_from_header;
debug!("Filtering file with tags = {:?}", self.tags);
let f = file.deref().borrow();
get_tags_from_header(f.header())
.iter()
.any(|tag| self.tags.iter().any(|remtag| remtag == tag))
}
}
/**
* Helper function to get files from the store filtered by the constraints passed via the
* CLI
*/
pub fn get_file_filter_by_cli<HP>(parser: &Parser<HP>,
matches: &ArgMatches,
id_key: &'static str,
match_key: &'static str,
tag_key: &'static str,
header_field_name: Option<&'static str>)
-> Box<CliFileFilter>
where HP: FileHeaderParser,
{
if matches.is_present(id_key) {
Box::new(CliFileFilterByHash { hash: FileHash::from(matches.value_of(id_key).unwrap()) })
} else if matches.is_present(match_key) {
let matcher = String::from(matches.value_of(match_key).unwrap());
header_field_name
.and_then(|header_field_name| {
Some(get_files_by_header_field_match_filter(parser,
&matcher,
header_field_name))
})
.unwrap_or(get_file_by_match_filter(parser, &matcher))
} else if matches.is_present(tag_key) {
let tags = matches.value_of(tag_key)
.unwrap()
.split(",")
.map(String::from)
.collect::<Vec<String>>();
get_file_by_tags_filter(tags)
} else {
Box::new(CliFileFilterDefault { default: true })
}
}
/**
* 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);
Box::new(CliFileFilterByDataRegex { regex: re })
}
fn get_files_by_header_field_match_filter<HP>(parser: &Parser<HP>,
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 })
}

View file

@ -2,6 +2,7 @@
* Utility helpers for modules
*/
pub mod cli;
pub mod header;
pub mod utils;