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::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::CliFileFilter;
|
||||||
|
|
||||||
mod header;
|
mod header;
|
||||||
|
|
||||||
|
@ -136,15 +138,13 @@ impl<'a> BM<'a> {
|
||||||
fn command_remove(&self, matches: &ArgMatches) -> bool {
|
fn command_remove(&self, matches: &ArgMatches) -> bool {
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
let (filtered, files) = self.get_files(matches, "id", "match", "tags");
|
let parser = Parser::new(JsonHeaderParser::new(None));
|
||||||
|
let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser, matches, "id", "match", "tags", None);
|
||||||
if !filtered {
|
let result = self.rt
|
||||||
error!("Unexpected error. Exiting");
|
.store()
|
||||||
exit(1);
|
.load_for_module(self, &parser)
|
||||||
}
|
|
||||||
|
|
||||||
let result = files
|
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|file| filter.filter_file(file))
|
||||||
.map(|file| {
|
.map(|file| {
|
||||||
debug!("File loaded, can remove now: {:?}", file);
|
debug!("File loaded, can remove now: {:?}", file);
|
||||||
let f = file.deref().borrow();
|
let f = file.deref().borrow();
|
||||||
|
@ -209,15 +209,18 @@ impl<'a> BM<'a> {
|
||||||
})
|
})
|
||||||
.unwrap_or(vec![]);
|
.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));
|
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()
|
.into_iter()
|
||||||
|
.filter(|file| filter.filter_file(file))
|
||||||
.map(|file| {
|
.map(|file| {
|
||||||
debug!("Remove tags from file: {:?}", file);
|
debug!("Remove tags from file: {:?}", file);
|
||||||
|
|
||||||
|
@ -250,96 +253,6 @@ impl<'a> BM<'a> {
|
||||||
.all(|x| x)
|
.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.
|
* Does no spec verification.
|
||||||
*/
|
*/
|
||||||
pub fn get_url_from_header(header: &FHD) -> Option<String> {
|
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 {
|
match header {
|
||||||
&FHD::Map{keys: ref ks} => {
|
&FHD::Map{keys: ref ks} => {
|
||||||
let mut keys : Vec<FHD> = ks.clone();
|
let mut keys : Vec<FHD> = ks.clone();
|
||||||
keys.iter().find(|k| {
|
keys.iter().find(|k| {
|
||||||
match k.deref() {
|
match k.deref() {
|
||||||
&FHD::Key{name: ref n, value: ref v} => n == "URL",
|
&FHD::Key{name: ref n, value: ref v} => n == name,
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
}).and_then(|urlkey| {
|
}).and_then(|urlkey| {
|
||||||
match urlkey.deref().clone() {
|
match urlkey.deref().clone() {
|
||||||
FHD::Key{name: _, value: ref v} => {
|
FHD::Key{name: ref n, value: ref v} => {
|
||||||
match v.deref().clone() {
|
match v.deref().clone() {
|
||||||
FHD::Text(s) => Some(s),
|
FHD::Text(s) => Some(s),
|
||||||
_ => {
|
_ => {
|
||||||
warn!("Malformed Header Data: Expected Text, found non-Text");
|
warn!("Malformed Header Data: Expected Text, found non-Text");
|
||||||
|
debug!(" in {}", n);
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* Utility helpers for modules
|
* Utility helpers for modules
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
pub mod cli;
|
||||||
pub mod header;
|
pub mod header;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue