From 16d40e9bfed26c7b11a4f885b85964659ec97c47 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 28 Dec 2015 18:46:36 +0100 Subject: [PATCH 01/11] Add util: is_url(String) -> bool --- src/main.rs | 1 + src/util/mod.rs | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 src/util/mod.rs diff --git a/src/main.rs b/src/main.rs index 0d4716a4..19ffa8c7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,7 @@ mod runtime; mod module; mod storage; mod ui; +mod util; use module::bm::BM; diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 00000000..6bef3251 --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1,6 @@ +use url::Url; + +pub fn is_url(url: &String) -> bool { + Url::parse(&url[..]).is_ok() +} + From db204f3cbaecc845d08f44e25eaf33d5b2dc2d03 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 28 Dec 2015 18:07:10 +0100 Subject: [PATCH 02/11] BM::command_add(): Add URL verification --- src/module/bm/mod.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/module/bm/mod.rs b/src/module/bm/mod.rs index ff56e591..08dfe252 100644 --- a/src/module/bm/mod.rs +++ b/src/module/bm/mod.rs @@ -6,6 +6,7 @@ use clap::ArgMatches; use runtime::Runtime; use module::Module; +use storage::Store; use storage::file::hash::FileHash; use storage::file::id::FileID; use storage::parser::FileHeaderParser; @@ -31,11 +32,20 @@ impl<'a> BM<'a> { } fn command_add(&self, matches: &ArgMatches) -> bool { + use std::process::exit; use self::header::build_header; let parser = Parser::new(JsonHeaderParser::new(None)); let url = matches.value_of("url").map(String::from).unwrap(); // clap ensures this is present + + if !self.validate_url(&url, &parser) { + error!("URL validation failed, exiting."); + exit(1); + } else { + debug!("Verification succeeded"); + } + let tags = matches.value_of("tags").and_then(|s| { Some(s.split(",").map(String::from).collect()) }).unwrap_or(vec![]); @@ -52,6 +62,37 @@ impl<'a> BM<'a> { }).unwrap_or(false) } + fn validate_url(&self, url: &String, parser: &Parser) -> bool + where HP: FileHeaderParser + { + use self::header::get_url_from_header; + use std::ops::Deref; + use util::is_url; + + if !is_url(url) { + error!("Url '{}' is not a valid URL. Will not store.", url); + return false; + } + + let is_in_store = self.rt + .store() + .load_for_module(self, parser) + .iter() + .any(|file| { + let f = file.deref().borrow(); + get_url_from_header(f.header()).map(|url_in_store| { + &url_in_store == url + }).unwrap_or(false) + }); + + if is_in_store { + error!("URL '{}' seems to be in the store already", url); + return false; + } + + return true; + } + fn command_list(&self, matches: &ArgMatches) -> bool { use ui::file::{FilePrinter, TablePrinter}; use self::header::get_url_from_header; From bbd11086d8f97dfa0158f7489198ca311c3f7238 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 28 Dec 2015 19:08:03 +0100 Subject: [PATCH 03/11] Remove bm option --check -> wont support these things by now --- etc/cli.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/etc/cli.yml b/etc/cli.yml index 4a6590d2..6d36b58b 100644 --- a/etc/cli.yml +++ b/etc/cli.yml @@ -118,13 +118,6 @@ subcommands: required: false takes_value: true - - check: - short: c - long: check - help: Ensure there are no references to this link - required: false - takes_value: false - - todo: about: Todo module version: 0.1 From e84986680d45d0f21d36a5fa453bb76561ccb974 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 28 Dec 2015 19:35:42 +0100 Subject: [PATCH 04/11] Add functions to get files from Store by certain predicated (id, match, tags) --- src/module/bm/mod.rs | 116 ++++++++++++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 34 deletions(-) diff --git a/src/module/bm/mod.rs b/src/module/bm/mod.rs index 08dfe252..ef9f3847 100644 --- a/src/module/bm/mod.rs +++ b/src/module/bm/mod.rs @@ -1,7 +1,12 @@ use std::fmt::{Debug, Display, Formatter}; +use std::rc::Rc; +use std::cell::RefCell; use std::fmt; +use std::ops::Deref; +use std::process::exit; use clap::ArgMatches; +use regex::Regex; use runtime::Runtime; use module::Module; @@ -9,12 +14,16 @@ use module::Module; use storage::Store; use storage::file::hash::FileHash; use storage::file::id::FileID; +use storage::file::File; use storage::parser::FileHeaderParser; use storage::parser::Parser; use storage::json::parser::JsonHeaderParser; mod header; +use self::header::get_url_from_header; +use self::header::get_tags_from_header; + pub struct BM<'a> { rt: &'a Runtime<'a>, } @@ -65,8 +74,6 @@ impl<'a> BM<'a> { fn validate_url(&self, url: &String, parser: &Parser) -> bool where HP: FileHeaderParser { - use self::header::get_url_from_header; - use std::ops::Deref; use util::is_url; if !is_url(url) { @@ -95,8 +102,6 @@ impl<'a> BM<'a> { fn command_list(&self, matches: &ArgMatches) -> bool { use ui::file::{FilePrinter, TablePrinter}; - use self::header::get_url_from_header; - use self::header::get_tags_from_header; use std::ops::Deref; let parser = Parser::new(JsonHeaderParser::new(None)); @@ -154,48 +159,79 @@ impl<'a> BM<'a> { } fn remove_by_hash(&self, hash: FileHash) -> bool { - use std::ops::Deref; - debug!("Removing for hash = '{:?}'", hash); - let parser = Parser::new(JsonHeaderParser::new(None)); - let file = self.rt.store().load_by_hash(self, &parser, hash); - debug!("file = {:?}", file); - file.map(|file| { - debug!("File loaded, can remove now: {:?}", file); - let f = file.deref().borrow(); - self.rt.store().remove(f.id().clone()) - }).unwrap_or(false) + self.get_files_by_id(hash) + .iter() + .map(|file| { + debug!("File loaded, can remove now: {:?}", file); + let f = file.deref().borrow(); + self.rt.store().remove(f.id().clone()) + }) + .all(|x| x) } fn remove_by_tags(&self, tags: Vec) -> bool { use std::fs::remove_file; - use std::ops::Deref; - use self::header::get_tags_from_header; let parser = Parser::new(JsonHeaderParser::new(None)); - self.rt - .store() - .load_for_module(self, &parser) + self.get_files_by_tags(tags) .iter() - .filter(|file| { - let f = file.deref().borrow(); - get_tags_from_header(f.header()).iter().any(|tag| { - tags.iter().any(|remtag| remtag == tag) - }) - }).map(|file| { + .map(|file| { let f = file.deref().borrow(); self.rt.store().remove(f.id().clone()) }).all(|x| x) } fn remove_by_match(&self, matcher: String) -> bool { - use self::header::get_url_from_header; use std::fs::remove_file; - use std::ops::Deref; - use std::process::exit; - use regex::Regex; + self.get_files_by_match(matcher) + .iter() + .map(|file| { + let f = file.deref().borrow(); + self.rt.store().remove(f.id().clone()) + }).all(|x| x) + } + + fn get_files(&self, + matches: &ArgMatches, + id_key: &'static str, + match_key: &'static str, + tag_key: &'static str) + -> Vec>> + { + if matches.is_present(id_key) { + let hash = FileHash::from(matches.value_of(id_key).unwrap()); + self.get_files_by_id(hash) + } else if matches.is_present(match_key) { + let matcher = String::from(matches.value_of(match_key).unwrap()); + 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::>(); + self.get_files_by_tags(tags) + } else { + // get all files + let parser = Parser::new(JsonHeaderParser::new(None)); + self.rt.store().load_for_module(self, &parser) + } + } + + fn get_files_by_id(&self, hash: FileHash) -> Vec>> { + let parser = Parser::new(JsonHeaderParser::new(None)); + self.rt + .store() + .load_by_hash(self, &parser, hash) + .map(|f| vec![f]) + .unwrap_or(vec![]) + } + + fn get_files_by_match(&self, matcher: String) -> Vec>> { + 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); @@ -204,11 +240,10 @@ impl<'a> BM<'a> { debug!("Compiled '{}' to regex: '{:?}'", matcher, re); - let parser = Parser::new(JsonHeaderParser::new(None)); self.rt .store() .load_for_module(self, &parser) - .iter() + .into_iter() .filter(|file| { let f = file.deref().borrow(); let url = get_url_from_header(f.header()); @@ -217,10 +252,23 @@ impl<'a> BM<'a> { debug!("Matching '{}' ~= '{}'", re.as_str(), u); re.is_match(&u[..]) }).unwrap_or(false) - }).map(|file| { + }) + .collect() + } + + fn get_files_by_tags(&self, tags: Vec) -> Vec>> { + let parser = Parser::new(JsonHeaderParser::new(None)); + self.rt + .store() + .load_for_module(self, &parser) + .into_iter() + .filter(|file| { let f = file.deref().borrow(); - self.rt.store().remove(f.id().clone()) - }).all(|x| x) + get_tags_from_header(f.header()).iter().any(|tag| { + tags.iter().any(|remtag| remtag == tag) + }) + }) + .collect() } } From cbefa577e94fb3ffb0ae027221cac9e880d84677 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 28 Dec 2015 19:46:59 +0100 Subject: [PATCH 05/11] Use get_files() to filter files, remove afterwards Change get_files() signature, so we know whether there was a filter applied, so we can abort the action if there was no filter involved. --- src/module/bm/mod.rs | 81 +++++++++++--------------------------------- 1 file changed, 20 insertions(+), 61 deletions(-) diff --git a/src/module/bm/mod.rs b/src/module/bm/mod.rs index ef9f3847..0d56665c 100644 --- a/src/module/bm/mod.rs +++ b/src/module/bm/mod.rs @@ -127,27 +127,21 @@ impl<'a> BM<'a> { fn command_remove(&self, matches: &ArgMatches) -> bool { use std::process::exit; - let result = - if matches.is_present("id") { - debug!("Removing by ID (Hash)"); - let hash = FileHash::from(matches.value_of("id").unwrap()); - self.remove_by_hash(hash) - } else if matches.is_present("tags") { - debug!("Removing by tags"); - let tags = matches.value_of("tags") - .unwrap() - .split(",") - .map(String::from) - .collect::>(); - self.remove_by_tags(tags) - } else if matches.is_present("match") { - debug!("Removing by match"); - self.remove_by_match(String::from(matches.value_of("match").unwrap())) - } else { - error!("Unexpected error. Exiting"); - exit(1); - false - }; + let (filtered, files) = self.get_files(matches, "id", "match", "tags"); + + if !filtered { + error!("Unexpected error. Exiting"); + exit(1); + } + + let result = files + .iter() + .map(|file| { + debug!("File loaded, can remove now: {:?}", file); + let f = file.deref().borrow(); + self.rt.store().remove(f.id().clone()) + }) + .all(|x| x); if result { info!("Removing succeeded"); @@ -158,66 +152,31 @@ impl<'a> BM<'a> { return result; } - fn remove_by_hash(&self, hash: FileHash) -> bool { - debug!("Removing for hash = '{:?}'", hash); - - self.get_files_by_id(hash) - .iter() - .map(|file| { - debug!("File loaded, can remove now: {:?}", file); - let f = file.deref().borrow(); - self.rt.store().remove(f.id().clone()) - }) - .all(|x| x) - } - - fn remove_by_tags(&self, tags: Vec) -> bool { - use std::fs::remove_file; - - let parser = Parser::new(JsonHeaderParser::new(None)); - self.get_files_by_tags(tags) - .iter() - .map(|file| { - let f = file.deref().borrow(); - self.rt.store().remove(f.id().clone()) - }).all(|x| x) - } - - fn remove_by_match(&self, matcher: String) -> bool { - use std::fs::remove_file; - - self.get_files_by_match(matcher) - .iter() - .map(|file| { - let f = file.deref().borrow(); - self.rt.store().remove(f.id().clone()) - }).all(|x| x) - } fn get_files(&self, matches: &ArgMatches, id_key: &'static str, match_key: &'static str, tag_key: &'static str) - -> Vec>> + -> (bool, Vec>>) { if matches.is_present(id_key) { let hash = FileHash::from(matches.value_of(id_key).unwrap()); - self.get_files_by_id(hash) + (true, self.get_files_by_id(hash)) } else if matches.is_present(match_key) { let matcher = String::from(matches.value_of(match_key).unwrap()); - self.get_files_by_match(matcher) + (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::>(); - self.get_files_by_tags(tags) + (true, self.get_files_by_tags(tags)) } else { // get all files let parser = Parser::new(JsonHeaderParser::new(None)); - self.rt.store().load_for_module(self, &parser) + (false, self.rt.store().load_for_module(self, &parser)) } } From 46b7ae9384bcd31b8910eb3bc061eaecad810227 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 28 Dec 2015 20:17:43 +0100 Subject: [PATCH 06/11] Add BM::command_add_tags() --- etc/cli.yml | 31 +++++++++++++++++++++++++++++ src/module/bm/mod.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/etc/cli.yml b/etc/cli.yml index 6d36b58b..2e7db1ae 100644 --- a/etc/cli.yml +++ b/etc/cli.yml @@ -118,6 +118,37 @@ subcommands: required: false takes_value: true + - add_tags: + about: Add tags to bookmark(s) + version: 0.1 + author: Matthias Beyer + args: + - with_id: + long: with-id + help: Add tags to bookmark with ID + required: false + takes_value: true + + - with_match: + short: m + long: with-match + help: Add tags to bookmark(s) which match this regex + required: false + takes_value: true + + - with_tags: + long: with-tags + help: Add tags to bookmark(s) which have these tag(s) + required: false + takes_value: true + + - tags: + short: t + long: tags + help: Add these tags + required: true + takes_value: true + - todo: about: Todo module version: 0.1 diff --git a/src/module/bm/mod.rs b/src/module/bm/mod.rs index 0d56665c..ba808229 100644 --- a/src/module/bm/mod.rs +++ b/src/module/bm/mod.rs @@ -152,6 +152,49 @@ impl<'a> BM<'a> { return result; } + fn command_add_tags(&self, matches: &ArgMatches) -> bool { + use self::header::set_tags_in_header; + + let tags = matches.value_of("tags") + .map(|ts| { + ts.split(",") + .map(String::from) + .collect::>() + }) + .unwrap_or(vec![]); + let (filter, files) = self.get_files(matches, "with_id", "with_match", "with_tags"); + + if tags.len() == 0 { + error!("No tags to add, exiting."); + exit(1); + } + + if !filter { + warn!("There were no filter applied when loading the files"); + } + + let result = files + .iter() + .map(|file| { + debug!("Adding tags to file: {:?}", file); + let f = file.deref().borrow(); + let hdr = f.header(); + let mut ts = get_tags_from_header(hdr); + let mut append_tags = tags.clone(); + ts.append(&mut append_tags); + set_tags_in_header(hdr, ts); + true + }) + .all(|x| x); + + if result { + info!("Adding tags to links succeeded"); + } else { + error!("Adding tags to links failed"); + } + + return result; + } fn get_files(&self, matches: &ArgMatches, @@ -248,6 +291,10 @@ impl<'a> Module<'a> for BM<'a> { self.command_remove(matches.subcommand_matches("remove").unwrap()) }, + Some("add_tags") => { + self.command_add_tags(matches.subcommand_matches("add_tags").unwrap()) + }, + Some(_) | None => { info!("No command given, doing nothing"); false From 966e9902cbceba1b5e1256a0be1e478305aa90e1 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 28 Dec 2015 20:18:22 +0100 Subject: [PATCH 07/11] BM header helpers: Add set_tags_in_header() --- src/module/bm/header.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/module/bm/header.rs b/src/module/bm/header.rs index 0f0f9076..4a823cde 100644 --- a/src/module/bm/header.rs +++ b/src/module/bm/header.rs @@ -30,6 +30,10 @@ pub fn get_tags_from_header(header: &FHD) -> Vec { headerhelpers::tags::data::get_tags_from_header(header) } +pub fn set_tags_in_header(header: &FHD, tags: Vec) { + headerhelpers::tags::data::set_tags_in_header(header, tags) +} + pub fn get_url_from_header(header: &FHD) -> Option { headerhelpers::data::get_url_from_header(header) } From 7e401b5881911b197fe540577a5bd3ed6b0f9dba Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 28 Dec 2015 20:46:19 +0100 Subject: [PATCH 08/11] Add generic alter_tags_in_files() function --- src/module/bm/header.rs | 8 ++--- src/module/bm/mod.rs | 68 ++++++++++++++++++++++++++--------------- 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/src/module/bm/header.rs b/src/module/bm/header.rs index 4a823cde..3bbe2861 100644 --- a/src/module/bm/header.rs +++ b/src/module/bm/header.rs @@ -30,11 +30,11 @@ pub fn get_tags_from_header(header: &FHD) -> Vec { headerhelpers::tags::data::get_tags_from_header(header) } -pub fn set_tags_in_header(header: &FHD, tags: Vec) { - headerhelpers::tags::data::set_tags_in_header(header, tags) -} - pub fn get_url_from_header(header: &FHD) -> Option { headerhelpers::data::get_url_from_header(header) } +pub fn rebuild_header_with_tags(header: &FHD, tags: Vec) -> Option { + get_url_from_header(header).map(|url| build_header(url, tags)) +} + diff --git a/src/module/bm/mod.rs b/src/module/bm/mod.rs index ba808229..ae8ba9bf 100644 --- a/src/module/bm/mod.rs +++ b/src/module/bm/mod.rs @@ -153,47 +153,65 @@ impl<'a> BM<'a> { } fn command_add_tags(&self, matches: &ArgMatches) -> bool { - use self::header::set_tags_in_header; + self.alter_tags_in_files(matches, |old_tags, cli_tags| { + let mut new_tags = old_tags.clone(); + new_tags.append(&mut cli_tags.clone()); + new_tags + }) + } - let tags = matches.value_of("tags") + fn alter_tags_in_files(&self, matches: &ArgMatches, generate_new_tags: F) -> bool + where F: Fn(Vec, &Vec) -> Vec + { + use self::header::rebuild_header_with_tags; + + let cli_tags = matches.value_of("tags") .map(|ts| { ts.split(",") .map(String::from) .collect::>() }) .unwrap_or(vec![]); - let (filter, files) = self.get_files(matches, "with_id", "with_match", "with_tags"); - if tags.len() == 0 { - error!("No tags to add, exiting."); - exit(1); - } + 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 result = files - .iter() + let parser = Parser::new(JsonHeaderParser::new(None)); + files + .into_iter() .map(|file| { - debug!("Adding tags to file: {:?}", file); - let f = file.deref().borrow(); - let hdr = f.header(); - let mut ts = get_tags_from_header(hdr); - let mut append_tags = tags.clone(); - ts.append(&mut append_tags); - set_tags_in_header(hdr, ts); + debug!("Remove tags from file: {:?}", file); + + let hdr = { + let f = file.deref().borrow(); + f.header().clone() + }; + + debug!("Tags:..."); + let old_tags = get_tags_from_header(&hdr); + debug!(" old_tags = {:?}", &old_tags); + debug!(" cli_tags = {:?}", &cli_tags); + + let new_tags = generate_new_tags(old_tags, &cli_tags); + debug!(" new_tags = {:?}", &new_tags); + + let new_header = rebuild_header_with_tags(&hdr, new_tags) + .unwrap_or_else(|| { + error!("Could not rebuild header for file"); + exit(1); + }); + { + let mut f_mut = file.deref().borrow_mut(); + f_mut.set_header(new_header); + } + + self.rt.store().persist(&parser, file); true }) - .all(|x| x); - - if result { - info!("Adding tags to links succeeded"); - } else { - error!("Adding tags to links failed"); - } - - return result; + .all(|x| x) } fn get_files(&self, From bcbd56831148ffe04432160f7c99b5b3919d0976 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 28 Dec 2015 20:47:47 +0100 Subject: [PATCH 09/11] Add BM::command_rm_tags() --- etc/cli.yml | 31 +++++++++++++++++++++++++++++++ src/module/bm/mod.rs | 13 +++++++++++++ 2 files changed, 44 insertions(+) diff --git a/etc/cli.yml b/etc/cli.yml index 2e7db1ae..1ab526a3 100644 --- a/etc/cli.yml +++ b/etc/cli.yml @@ -149,6 +149,37 @@ subcommands: required: true takes_value: true + - rm_tags: + about: Remove tags from bookmark(s) + version: 0.1 + author: Matthias Beyer + args: + - with_id: + long: with-id + help: Remove tags from bookmark with ID + required: false + takes_value: true + + - with_match: + short: m + long: with-match + help: Remove tags from bookmark(s) which match this regex + required: false + takes_value: true + + - with_tags: + long: with-tags + help: Remove tags from bookmark(s) which have these tag(s) + required: false + takes_value: true + + - tags: + short: t + long: tags + help: Remove these tags + required: true + takes_value: true + - todo: about: Todo module version: 0.1 diff --git a/src/module/bm/mod.rs b/src/module/bm/mod.rs index ae8ba9bf..0e5d783b 100644 --- a/src/module/bm/mod.rs +++ b/src/module/bm/mod.rs @@ -160,6 +160,15 @@ impl<'a> BM<'a> { }) } + fn command_rm_tags(&self, matches: &ArgMatches) -> bool { + self.alter_tags_in_files(matches, |old_tags, cli_tags| { + old_tags.clone() + .into_iter() + .filter(|tag| !cli_tags.contains(tag)) + .collect() + }) + } + fn alter_tags_in_files(&self, matches: &ArgMatches, generate_new_tags: F) -> bool where F: Fn(Vec, &Vec) -> Vec { @@ -313,6 +322,10 @@ impl<'a> Module<'a> for BM<'a> { self.command_add_tags(matches.subcommand_matches("add_tags").unwrap()) }, + Some("rm_tags") => { + self.command_rm_tags(matches.subcommand_matches("rm_tags").unwrap()) + }, + Some(_) | None => { info!("No command given, doing nothing"); false From 68f66ca27d39ddaf3c39f523226e3c5f33bdf1b3 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 28 Dec 2015 20:51:32 +0100 Subject: [PATCH 10/11] Add BM::command_set_tags() --- etc/cli.yml | 31 +++++++++++++++++++++++++++++++ src/module/bm/mod.rs | 11 +++++++++++ 2 files changed, 42 insertions(+) diff --git a/etc/cli.yml b/etc/cli.yml index 1ab526a3..50ca507c 100644 --- a/etc/cli.yml +++ b/etc/cli.yml @@ -180,6 +180,37 @@ subcommands: required: true takes_value: true + - set_tags: + about: Set tags in bookmark(s) + version: 0.1 + author: Matthias Beyer + args: + - to_id: + long: to-id + help: Set tags in bookmark with this id + required: false + takes_value: true + + - to_match: + short: m + long: to-match + help: Set tags in bookmark(s) which match this regex + required: false + takes_value: true + + - to_tags: + long: to-tags + help: Set tags in bookmark(s) which have these tag(s) + required: false + takes_value: true + + - tags: + short: t + long: tags + help: Set these tags + required: true + takes_value: true + - todo: about: Todo module version: 0.1 diff --git a/src/module/bm/mod.rs b/src/module/bm/mod.rs index 0e5d783b..6644c59e 100644 --- a/src/module/bm/mod.rs +++ b/src/module/bm/mod.rs @@ -169,6 +169,12 @@ impl<'a> BM<'a> { }) } + fn command_set_tags(&self, matches: &ArgMatches) -> bool { + self.alter_tags_in_files(matches, |old_tags, cli_tags| { + cli_tags.clone() + }) + } + fn alter_tags_in_files(&self, matches: &ArgMatches, generate_new_tags: F) -> bool where F: Fn(Vec, &Vec) -> Vec { @@ -223,6 +229,7 @@ impl<'a> BM<'a> { .all(|x| x) } + fn get_files(&self, matches: &ArgMatches, id_key: &'static str, @@ -326,6 +333,10 @@ impl<'a> Module<'a> for BM<'a> { self.command_rm_tags(matches.subcommand_matches("rm_tags").unwrap()) }, + Some("set_tags") => { + self.command_set_tags(matches.subcommand_matches("set_tags").unwrap()) + }, + Some(_) | None => { info!("No command given, doing nothing"); false From 6ca4818302604289a05092d5eab620b818493845 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 28 Dec 2015 21:25:33 +0100 Subject: [PATCH 11/11] Add File::set_header() --- src/storage/file/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/storage/file/mod.rs b/src/storage/file/mod.rs index 14b4e1e3..16a072ef 100644 --- a/src/storage/file/mod.rs +++ b/src/storage/file/mod.rs @@ -40,6 +40,10 @@ impl File { &self.header } + pub fn set_header(&mut self, new_header: FileHeaderData) { + self.header = new_header; + } + pub fn data(&self) -> &String { &self.data }