Auto merge of #35 - matthiasbeyer:refactor, r=matthiasbeyer
Refactor Refactor for better and more generic CLI-filter abstractions for modules.
This commit is contained in:
commit
9b1ba78c1f
4 changed files with 234 additions and 107 deletions
|
@ -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
189
src/module/helpers/cli.rs
Normal 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 })
|
||||
}
|
||||
|
|
@ -19,21 +19,45 @@ pub mod data {
|
|||
* Does no spec verification.
|
||||
*/
|
||||
pub fn get_url_from_header(header: &FHD) -> Option<String> {
|
||||
get_named_text_from_header("URL", header)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an NAME from a header, whereas the header has to have the following format:
|
||||
*
|
||||
* { ..., "NAME": "<NAME>", ... }
|
||||
*
|
||||
* Does no spec verification.
|
||||
*/
|
||||
pub fn get_name_from_header(header: &FHD) -> Option<String> {
|
||||
get_named_text_from_header("NAME", header)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a named field from the header, which has to be of this format
|
||||
*
|
||||
* { ..., "<name of field>": "<content as string>", ... }
|
||||
*
|
||||
* Does no spec verification.
|
||||
*/
|
||||
pub fn get_named_text_from_header(name: &'static str, header: &FHD) -> Option<String> {
|
||||
match header {
|
||||
&FHD::Map{keys: ref ks} => {
|
||||
let mut keys : Vec<FHD> = ks.clone();
|
||||
keys.iter().find(|k| {
|
||||
match k.deref() {
|
||||
&FHD::Key{name: ref n, value: ref v} => n == "URL",
|
||||
&FHD::Key{name: ref n, value: ref v} => n == name,
|
||||
_ => false
|
||||
}
|
||||
}).and_then(|urlkey| {
|
||||
match urlkey.deref().clone() {
|
||||
FHD::Key{name: _, value: ref v} => {
|
||||
FHD::Key{name: ref n, value: ref v} => {
|
||||
match v.deref().clone() {
|
||||
FHD::Text(s) => Some(s),
|
||||
_ => {
|
||||
warn!("Malformed Header Data: Expected Text, found non-Text");
|
||||
debug!(" in {}", n);
|
||||
None
|
||||
},
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Utility helpers for modules
|
||||
*/
|
||||
|
||||
pub mod cli;
|
||||
pub mod header;
|
||||
pub mod utils;
|
||||
|
||||
|
|
Loading…
Reference in a new issue