From c58b0d323e0def63d545f4823d2ec7597fc133f7 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 6 Dec 2019 20:03:29 +0100 Subject: [PATCH 1/5] Remove old implementation, define interface for new implementation Signed-off-by: Matthias Beyer --- lib/domain/libimagbookmark/Cargo.toml | 6 + lib/domain/libimagbookmark/src/collection.rs | 191 ------------------- lib/domain/libimagbookmark/src/lib.rs | 5 +- lib/domain/libimagbookmark/src/link.rs | 76 -------- lib/domain/libimagbookmark/src/store.rs | 67 +++++++ 5 files changed, 76 insertions(+), 269 deletions(-) delete mode 100644 lib/domain/libimagbookmark/src/collection.rs delete mode 100644 lib/domain/libimagbookmark/src/link.rs create mode 100644 lib/domain/libimagbookmark/src/store.rs diff --git a/lib/domain/libimagbookmark/Cargo.toml b/lib/domain/libimagbookmark/Cargo.toml index 9e55b986..4f766009 100644 --- a/lib/domain/libimagbookmark/Cargo.toml +++ b/lib/domain/libimagbookmark/Cargo.toml @@ -29,3 +29,9 @@ libimagerror = { version = "0.10.0", path = "../../../lib/core/libimagerror" libimagentrylink = { version = "0.10.0", path = "../../../lib/entry/libimagentrylink" } libimagentryurl = { version = "0.10.0", path = "../../../lib/entry/libimagentryurl" } +[dependencies.uuid] +version = "0.7" +default-features = false +features = ["serde", "v4"] + + diff --git a/lib/domain/libimagbookmark/src/collection.rs b/lib/domain/libimagbookmark/src/collection.rs deleted file mode 100644 index a50208ad..00000000 --- a/lib/domain/libimagbookmark/src/collection.rs +++ /dev/null @@ -1,191 +0,0 @@ -// -// imag - the personal information management suite for the commandline -// Copyright (C) 2015-2019 Matthias Beyer and contributors -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; version -// 2.1 of the License. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -//! BookmarkCollection module -//! -//! A BookmarkCollection is nothing more than a simple store entry. One can simply call functions -//! from the libimagentryurl::linker::UrlLinker trait on this to generate external links. -//! -//! The BookmarkCollection type offers helper functions to get all links or such things. - -use regex::Regex; - -use failure::Fallible as Result; -use failure::ResultExt; -use failure::Error; - -use libimagstore::store::Store; -use libimagstore::store::Entry; -use libimagstore::store::FileLockEntry; -use libimagstore::storeid::StoreId; -use libimagentryurl::linker::UrlLinker; -use libimagentryurl::iter::UrlIter; -use libimagentrylink::linkable::Linkable; -use libimagentrylink::link::Link as StoreLink; - -use crate::link::Link; - -use self::iter::LinksMatchingRegexIter; - -pub trait BookmarkCollectionStore<'a> { - fn new(&'a self, name: &str) -> Result>; - fn get(&'a self, name: &str) -> Result>>; - fn delete(&'a self, name: &str) -> Result<()>; -} - -impl<'a> BookmarkCollectionStore<'a> for Store { - - #[allow(clippy::new_ret_no_self)] - fn new(&'a self, name: &str) -> Result> { - crate::module_path::new_id(name) - .and_then(|id| self.create(id) - .context("Failed to create FileLockEntry") - .map_err(Error::from)) - .context("Failed to create Id for new Bookmark Collection") - .map_err(Error::from) - } - - fn get(&'a self, name: &str) -> Result>> { - crate::module_path::new_id(name) - .and_then(|id| self.get(id) - .context("Failed to get FileLockEntry") - .map_err(Error::from)) - .context("Failed to get Bookmark Collection") - .map_err(Error::from) - } - - fn delete(&'a self, name: &str) -> Result<()> { - crate::module_path::new_id(name) - .and_then(|id| self.delete(id) - .context("Failed to delete FileLockEntry") - .map_err(Error::from)) - .context("Failed to delete Bookmark Collection") - .map_err(Error::from) - } - -} - -pub trait BookmarkCollection : Sized + Linkable + UrlLinker { - fn get_links<'a>(&self, store: &'a Store) -> Result>; - fn link_entries(&self) -> Result>; - fn add_link(&mut self, store: &Store, l: Link) -> Result>; - fn get_links_matching<'a>(&self, store: &'a Store, r: Regex) -> Result>; - fn remove_link(&mut self, store: &Store, l: Link) -> Result>; -} - -impl BookmarkCollection for Entry { - - fn get_links<'a>(&self, store: &'a Store) -> Result> { - self.get_urls(store) - } - - #[allow(clippy::redundant_closure)] - fn link_entries(&self) -> Result> { - use libimagentryurl::util::is_external_link_storeid; - self.links().map(|v| v.filter(|id| is_external_link_storeid(id)).collect()) - } - - fn add_link(&mut self, store: &Store, l: Link) -> Result> { - use crate::link::IntoUrl; - l.into_url().and_then(|url| self.add_url(store, url)) - } - - fn get_links_matching<'a>(&self, store: &'a Store, r: Regex) -> Result> { - use self::iter::IntoLinksMatchingRegexIter; - self.get_urls(store).map(|iter| iter.matching_regex(r)) - } - - fn remove_link(&mut self, store: &Store, l: Link) -> Result> { - use crate::link::IntoUrl; - l.into_url().and_then(|url| self.remove_url(store, url)) - } - -} - -pub mod iter { - use crate::link::Link; - use failure::Fallible as Result; - use regex::Regex; - - use libimagentryurl::iter::UrlIter; - - pub struct LinkIter(I) - where I: Iterator; - - impl> LinkIter { - pub fn new(i: I) -> LinkIter { - LinkIter(i) - } - } - - impl> Iterator for LinkIter { - type Item = Link; - - fn next(&mut self) -> Option { - self.0.next() - } - } - - impl From for LinkIter where I: Iterator { - fn from(i: I) -> LinkIter { - LinkIter(i) - } - } - - pub struct LinksMatchingRegexIter<'a>(UrlIter<'a>, Regex); - - impl<'a> LinksMatchingRegexIter<'a> { - pub fn new(i: UrlIter<'a>, r: Regex) -> LinksMatchingRegexIter<'a> { - LinksMatchingRegexIter(i, r) - } - } - - impl<'a> Iterator for LinksMatchingRegexIter<'a> { - type Item = Result; - - fn next(&mut self) -> Option { - loop { - let n = match self.0.next() { - Some(Ok(n)) => n, - Some(Err(e)) => return Some(Err(e)), - None => return None, - }; - - let s = n.into_string(); - if self.1.is_match(&s[..]) { - return Some(Ok(Link::from(s))) - } else { - continue; - } - } - } - } - - pub trait IntoLinksMatchingRegexIter<'a> { - fn matching_regex(self, _: Regex) -> LinksMatchingRegexIter<'a>; - } - - impl<'a> IntoLinksMatchingRegexIter<'a> for UrlIter<'a> { - fn matching_regex(self, r: Regex) -> LinksMatchingRegexIter<'a> { - LinksMatchingRegexIter(self, r) - } - } - -} - diff --git a/lib/domain/libimagbookmark/src/lib.rs b/lib/domain/libimagbookmark/src/lib.rs index c8b2c59c..455a2a3f 100644 --- a/lib/domain/libimagbookmark/src/lib.rs +++ b/lib/domain/libimagbookmark/src/lib.rs @@ -38,6 +38,7 @@ )] extern crate url; +extern crate uuid; extern crate regex; #[macro_use] extern crate failure; @@ -48,5 +49,5 @@ extern crate libimagentryurl; module_entry_path_mod!("bookmark"); -pub mod collection; -pub mod link; +pub mod store; + diff --git a/lib/domain/libimagbookmark/src/link.rs b/lib/domain/libimagbookmark/src/link.rs deleted file mode 100644 index 8210162d..00000000 --- a/lib/domain/libimagbookmark/src/link.rs +++ /dev/null @@ -1,76 +0,0 @@ -// -// imag - the personal information management suite for the commandline -// Copyright (C) 2015-2019 Matthias Beyer and contributors -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; version -// 2.1 of the License. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -use std::ops::{Deref, DerefMut}; - -use failure::Fallible as Result; -use failure::ResultExt; -use failure::Error; -use failure::err_msg; - -use url::Url; - -#[derive(Debug, Clone)] -pub struct Link(String); - -impl From for Link { - - fn from(s: String) -> Link { - Link(s) - } - -} - -impl<'a> From<&'a str> for Link { - - fn from(s: &'a str) -> Link { - Link(String::from(s)) - } - -} - -impl Deref for Link { - type Target = String; - - fn deref(&self) -> &String { - &self.0 - } - -} - -impl DerefMut for Link { - - fn deref_mut(&mut self) -> &mut String { - &mut self.0 - } - -} - -pub trait IntoUrl { - fn into_url(self) -> Result; -} - -impl IntoUrl for Link { - - fn into_url(self) -> Result { - Url::parse(&self[..]).context(err_msg("Link parsing error")).map_err(Error::from) - } - -} - diff --git a/lib/domain/libimagbookmark/src/store.rs b/lib/domain/libimagbookmark/src/store.rs new file mode 100644 index 00000000..32401228 --- /dev/null +++ b/lib/domain/libimagbookmark/src/store.rs @@ -0,0 +1,67 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015-2019 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use failure::Fallible as Result; +use uuid::Uuid; +use url::Url; + +use libimagstore::store::Store; +use libimagstore::storeid::StoreId; +use libimagstore::store::FileLockEntry; +use libimagstore::iter::Entries; +use libimagentryurl::link::Link; + + +pub trait BookmarkStore { + + fn add_bookmark<'a>(&'a self, url: Url) -> Result<(Uuid, FileLockEntry<'a>)>; + + fn get_bookmark_by_uuid<'a>(&'a self, uuid: &Uuid) -> Result>>; + + fn remove_bookmark_by_uuid(&self, uuid: &Uuid) -> Result<()>; + + fn all_bookmarks<'a>(&'a self) -> Result>; + +} + +impl BookmarkStore for Store { + fn add_bookmark<'a>(&'a self, url: Url) -> Result<(Uuid, FileLockEntry<'a>)> { + let uuid = uuid::Uuid::new_v4(); + id_for_uuid(&uuid) + .and_then(|id| self.create(id)) + .and_then(|mut entry| entry.set_url(url).map(|_| (uuid, entry))) + } + + fn get_bookmark_by_uuid<'a>(&'a self, uuid: &Uuid) -> Result>> { + id_for_uuid(uuid).and_then(|id| self.get(id)) + } + + fn remove_bookmark_by_uuid(&self, uuid: &Uuid) -> Result<()> { + id_for_uuid(uuid).and_then(|id| self.delete(id)) + } + + fn all_bookmarks<'a>(&'a self) -> Result> { + self.entries()?.in_collection("bookmark") + } +} + +fn id_for_uuid(uuid: &Uuid) -> Result { + crate::module_path::new_id(uuid.to_hyphenated().encode_lower(&mut Uuid::encode_buffer())) +} + From 75ab0c1408ae400a40e80277c7fbdd82e0a8c474 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 8 Dec 2019 13:28:26 +0100 Subject: [PATCH 2/5] Add "is"-flag setting when creating bookmark Signed-off-by: Matthias Beyer --- lib/domain/libimagbookmark/Cargo.toml | 1 + lib/domain/libimagbookmark/src/bookmark.rs | 37 ++++++++++++++++++++++ lib/domain/libimagbookmark/src/lib.rs | 2 ++ lib/domain/libimagbookmark/src/store.rs | 8 ++++- 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 lib/domain/libimagbookmark/src/bookmark.rs diff --git a/lib/domain/libimagbookmark/Cargo.toml b/lib/domain/libimagbookmark/Cargo.toml index 4f766009..eb441965 100644 --- a/lib/domain/libimagbookmark/Cargo.toml +++ b/lib/domain/libimagbookmark/Cargo.toml @@ -28,6 +28,7 @@ libimagstore = { version = "0.10.0", path = "../../../lib/core/libimagstore" libimagerror = { version = "0.10.0", path = "../../../lib/core/libimagerror" } libimagentrylink = { version = "0.10.0", path = "../../../lib/entry/libimagentrylink" } libimagentryurl = { version = "0.10.0", path = "../../../lib/entry/libimagentryurl" } +libimagentryutil = { version = "0.10.0", path = "../../../lib/entry/libimagentryutil" } [dependencies.uuid] version = "0.7" diff --git a/lib/domain/libimagbookmark/src/bookmark.rs b/lib/domain/libimagbookmark/src/bookmark.rs new file mode 100644 index 00000000..d16df969 --- /dev/null +++ b/lib/domain/libimagbookmark/src/bookmark.rs @@ -0,0 +1,37 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015-2019 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use failure::Fallible as Result; + +use libimagentryutil::isa::Is; +use libimagentryutil::isa::IsKindHeaderPathProvider; +use libimagstore::store::Entry; + +provide_kindflag_path!(pub IsBookmark, "bookmark.is_bookmark"); + +pub trait Bookmark { + fn is_bookmark(&self) -> Result; +} + +impl Bookmark for Entry { + fn is_bookmark(&self) -> Result { + self.is::() + } +} + diff --git a/lib/domain/libimagbookmark/src/lib.rs b/lib/domain/libimagbookmark/src/lib.rs index 455a2a3f..1f4900b0 100644 --- a/lib/domain/libimagbookmark/src/lib.rs +++ b/lib/domain/libimagbookmark/src/lib.rs @@ -43,11 +43,13 @@ extern crate regex; #[macro_use] extern crate failure; #[macro_use] extern crate libimagstore; +#[macro_use] extern crate libimagentryutil; extern crate libimagerror; extern crate libimagentrylink; extern crate libimagentryurl; module_entry_path_mod!("bookmark"); +pub mod bookmark; pub mod store; diff --git a/lib/domain/libimagbookmark/src/store.rs b/lib/domain/libimagbookmark/src/store.rs index 32401228..d11fe331 100644 --- a/lib/domain/libimagbookmark/src/store.rs +++ b/lib/domain/libimagbookmark/src/store.rs @@ -26,7 +26,10 @@ use libimagstore::storeid::StoreId; use libimagstore::store::FileLockEntry; use libimagstore::iter::Entries; use libimagentryurl::link::Link; +use libimagentryutil::isa::Is; +use crate::bookmark::IsBookmark; +use crate::bookmark::Bookmark; pub trait BookmarkStore { @@ -45,7 +48,10 @@ impl BookmarkStore for Store { let uuid = uuid::Uuid::new_v4(); id_for_uuid(&uuid) .and_then(|id| self.create(id)) - .and_then(|mut entry| entry.set_url(url).map(|_| (uuid, entry))) + .and_then(|mut entry| { + entry.set_isflag::()?; + entry.set_url(url).map(|_| (uuid, entry)) + }) } fn get_bookmark_by_uuid<'a>(&'a self, uuid: &Uuid) -> Result>> { From 37b7b2e67ea89c492abc1b6c6f3b62c5c643e75d Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 8 Dec 2019 13:37:21 +0100 Subject: [PATCH 3/5] Add functions to get by id and remove with FileLockEntry Signed-off-by: Matthias Beyer --- lib/domain/libimagbookmark/src/store.rs | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/domain/libimagbookmark/src/store.rs b/lib/domain/libimagbookmark/src/store.rs index d11fe331..9e3d4429 100644 --- a/lib/domain/libimagbookmark/src/store.rs +++ b/lib/domain/libimagbookmark/src/store.rs @@ -36,9 +36,12 @@ pub trait BookmarkStore { fn add_bookmark<'a>(&'a self, url: Url) -> Result<(Uuid, FileLockEntry<'a>)>; fn get_bookmark_by_uuid<'a>(&'a self, uuid: &Uuid) -> Result>>; + fn get_bookmark_by_id<'a>(&'a self, sid: StoreId) -> Result>>; fn remove_bookmark_by_uuid(&self, uuid: &Uuid) -> Result<()>; + fn remove_bookmark<'a>(&self, fle: FileLockEntry<'a>) -> Result<()>; + fn all_bookmarks<'a>(&'a self) -> Result>; } @@ -58,10 +61,41 @@ impl BookmarkStore for Store { id_for_uuid(uuid).and_then(|id| self.get(id)) } + /// Get a bookmark by store id + /// + /// + /// # Warning + /// + /// Returns an error if the StoreId does not refer to an entry that is a Bookmark. + /// If you want to ignore these errors on this API level and handle these errors yourself, + /// use Store::get() + /// + fn get_bookmark_by_id<'a>(&'a self, sid: StoreId) -> Result>> { + if let Some(entry) = self.get(sid)? { + if !entry.is_bookmark()? { + return Err(format_err!("Not a bookmark: {}", entry.get_location())); + } else { + Ok(Some(entry)) + } + } else { + Ok(None) + } + } + fn remove_bookmark_by_uuid(&self, uuid: &Uuid) -> Result<()> { id_for_uuid(uuid).and_then(|id| self.delete(id)) } + fn remove_bookmark<'a>(&self, fle: FileLockEntry<'a>) -> Result<()> { + if fle.is_bookmark()? { + let id = fle.get_location().clone(); + drop(fle); + self.delete(id) + } else { + Err(format_err!("Not a bookmark: {}", fle.get_location())) + } + } + fn all_bookmarks<'a>(&'a self) -> Result> { self.entries()?.in_collection("bookmark") } From 4f2ea236af73d9fc03819933c78d46fbd39e9820 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 8 Dec 2019 13:16:16 +0100 Subject: [PATCH 4/5] Reimplement imag-bookmark commandline frontend Signed-off-by: Matthias Beyer --- bin/domain/imag-bookmark/Cargo.toml | 8 ++ bin/domain/imag-bookmark/src/lib.rs | 132 +++++++++------------------- bin/domain/imag-bookmark/src/ui.rs | 88 +++++++++---------- 3 files changed, 92 insertions(+), 136 deletions(-) diff --git a/bin/domain/imag-bookmark/Cargo.toml b/bin/domain/imag-bookmark/Cargo.toml index 7e039df1..c5851236 100644 --- a/bin/domain/imag-bookmark/Cargo.toml +++ b/bin/domain/imag-bookmark/Cargo.toml @@ -25,11 +25,14 @@ toml = "0.5.1" toml-query = "0.9.2" failure = "0.1.5" resiter = "0.4.0" +url = "2" libimagrt = { version = "0.10.0", path = "../../../lib/core/libimagrt" } libimagerror = { version = "0.10.0", path = "../../../lib/core/libimagerror" } +libimagstore = { version = "0.10.0", path = "../../../lib/core/libimagstore" } libimagbookmark = { version = "0.10.0", path = "../../../lib/domain/libimagbookmark" } libimagentrylink = { version = "0.10.0", path = "../../../lib/entry/libimagentrylink" } +libimagentryurl = { version = "0.10.0", path = "../../../lib/entry/libimagentryurl" } libimagutil = { version = "0.10.0", path = "../../../lib/etc/libimagutil" } [dependencies.clap] @@ -37,6 +40,11 @@ version = "2.33.0" default-features = false features = ["color", "suggestions", "wrap_help"] +[dependencies.uuid] +version = "0.7" +default-features = false +features = ["serde", "v4"] + [lib] name = "libimagbookmarkfrontend" path = "src/lib.rs" diff --git a/bin/domain/imag-bookmark/src/lib.rs b/bin/domain/imag-bookmark/src/lib.rs index 537c6f4d..98f50fca 100644 --- a/bin/domain/imag-bookmark/src/lib.rs +++ b/bin/domain/imag-bookmark/src/lib.rs @@ -37,6 +37,8 @@ extern crate clap; #[macro_use] extern crate log; extern crate toml; +extern crate url; +extern crate uuid; extern crate toml_query; #[macro_use] extern crate failure; extern crate resiter; @@ -44,25 +46,27 @@ extern crate resiter; extern crate libimagbookmark; extern crate libimagrt; extern crate libimagerror; +extern crate libimagstore; extern crate libimagutil; -extern crate libimagentrylink; +extern crate libimagentryurl; use std::io::Write; -use std::ops::DerefMut; -use toml_query::read::TomlValueReadTypeExt; use failure::Error; use failure::err_msg; use failure::Fallible as Result; use resiter::AndThen; +use resiter::IterInnerOkOrElse; use clap::App; +use url::Url; use libimagrt::runtime::Runtime; use libimagrt::application::ImagApplication; -use libimagbookmark::collection::BookmarkCollection; -use libimagbookmark::collection::BookmarkCollectionStore; -use libimagbookmark::link::Link as BookmarkLink; -use libimagentrylink::linkable::Linkable; +use libimagstore::iter::get::StoreIdGetIteratorExtension; +use libimagbookmark::store::BookmarkStore; +use libimagbookmark::bookmark::Bookmark; +use libimagentryurl::link::Link; + mod ui; @@ -75,7 +79,6 @@ impl ImagApplication for ImagBookmark { fn run(rt: Runtime) -> Result<()> { match rt.cli().subcommand_name().ok_or_else(|| err_msg("No subcommand called"))? { "add" => add(&rt), - "collection" => collection(&rt), "list" => list(&rt), "remove" => remove(&rt), other => { @@ -108,105 +111,52 @@ impl ImagApplication for ImagBookmark { fn add(rt: &Runtime) -> Result<()> { let scmd = rt.cli().subcommand_matches("add").unwrap(); - let coll = get_collection_name(rt, "add", "collection")?; - - let mut collection = BookmarkCollectionStore::get(rt.store(), &coll)? - .ok_or_else(|| format_err!("No bookmark collection '{}' found", coll))?; - - rt.report_touched(collection.get_location())?; - scmd.values_of("urls") .unwrap() .into_iter() - .map(|url| { - let new_ids = BookmarkCollection::add_link(collection.deref_mut(), rt.store(), BookmarkLink::from(url))?; - rt.report_all_touched(new_ids.into_iter()).map_err(Error::from) + .map(|s| Url::parse(s).map_err(Error::from)) + .and_then_ok(|url| { + let (uuid, fle) = rt.store().add_bookmark(url.clone())?; + debug!("Created entry for url '{}' with uuid '{}'", url, uuid); + info!("{} = {}", url, uuid); + rt.report_touched(fle.get_location()).map_err(Error::from) }) .collect() } -fn collection(rt: &Runtime) -> Result<()> { - let scmd = rt.cli().subcommand_matches("collection").unwrap(); - - if scmd.is_present("add") { // adding a new collection - let name = scmd.value_of("add").unwrap(); - let id = BookmarkCollectionStore::new(rt.store(), &name)?; - rt.report_touched(id.get_location())?; - info!("Created: {}", name); - } - - if scmd.is_present("remove") { // remove a collection - let name = scmd.value_of("remove").unwrap(); - - { // remove all links - BookmarkCollectionStore::get(rt.store(), &name)? - .ok_or_else(|| format_err!("Collection does not exist: {}", name))? - .unlink(rt.store())?; - } - - BookmarkCollectionStore::delete(rt.store(), &name)?; - info!("Deleted: {}", name); - } - - Ok(()) -} - fn list(rt: &Runtime) -> Result<()> { - let coll = get_collection_name(rt, "list", "collection")?; + rt.store() + .all_bookmarks()? + .into_get_iter() + .map_inner_ok_or_else(|| err_msg("Did not find one entry")) + .and_then_ok(|entry| { + if entry.is_bookmark()? { + let url = entry.get_url()? + .ok_or_else(|| format_err!("Failed to retrieve URL for {}", entry.get_location()))?; + if !rt.output_is_pipe() { + writeln!(rt.stdout(), "{}", url)?; + } - let collection = BookmarkCollectionStore::get(rt.store(), &coll)? - .ok_or_else(|| format_err!("No bookmark collection '{}' found", coll))?; - - rt.report_touched(collection.get_location())?; - - let mut i = 0; // poor mans enumerate() - - collection - .get_links(rt.store())? - .and_then_ok(|link| { - let r = writeln!(rt.stdout(), "{: >3}: {}", i, link).map_err(Error::from); - i += 1; - r + rt.report_touched(entry.get_location()).map_err(Error::from) + } else { + Ok(()) + } }) .collect() } fn remove(rt: &Runtime) -> Result<()> { - let scmd = rt.cli().subcommand_matches("remove").unwrap(); - let coll = get_collection_name(rt, "list", "collection")?; - - let mut collection = BookmarkCollectionStore::get(rt.store(), &coll)? - .ok_or_else(|| format_err!("No bookmark collection '{}' found", coll))?; - - rt.report_touched(collection.get_location())?; - - scmd.values_of("urls") - .unwrap() + rt.ids::()? + .ok_or_else(|| err_msg("No ids supplied"))? .into_iter() - .map(|url| { - let removed_links = BookmarkCollection::remove_link(collection.deref_mut(), rt.store(), BookmarkLink::from(url))?; - rt.report_all_touched(removed_links.into_iter()).map_err(Error::from) + .map(Ok) + .into_get_iter(rt.store()) + .map_inner_ok_or_else(|| err_msg("Did not find one entry")) + .and_then_ok(|fle| { + rt.report_touched(fle.get_location()) + .map_err(Error::from) + .and_then(|_| rt.store().remove_bookmark(fle)) }) .collect() } - -fn get_collection_name(rt: &Runtime, - subcommand_name: &str, - collection_argument_name: &str) - -> Result -{ - if let Some(cn) = rt.cli() - .subcommand_matches(subcommand_name) - .and_then(|scmd| scmd.value_of(collection_argument_name).map(String::from)) - { - return Ok(cn) - } else { - rt.config().ok_or_else(|| err_msg("No configuration availablew")) - .and_then(|cfg| { - cfg.read_string("bookmark.default_collection")? - .ok_or_else(|| err_msg("Missing config: 'bookmark.default_collection'.")) - }) - } -} - diff --git a/bin/domain/imag-bookmark/src/ui.rs b/bin/domain/imag-bookmark/src/ui.rs index 93a48cdb..e248a420 100644 --- a/bin/domain/imag-bookmark/src/ui.rs +++ b/bin/domain/imag-bookmark/src/ui.rs @@ -17,23 +17,21 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -use clap::{Arg, App, SubCommand}; +use std::path::PathBuf; + +use failure::Fallible as Result; +use clap::{Arg, ArgMatches, App, SubCommand}; use libimagutil::cli_validators::*; +use libimagstore::storeid::StoreId; +use libimagstore::storeid::IntoStoreId; +use libimagrt::runtime::IdPathProvider; pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { app .subcommand(SubCommand::with_name("add") .about("Add bookmarks") .version("0.1") - .arg(Arg::with_name("collection") - .long("collection") - .short("c") - .takes_value(true) - .required(false) - .multiple(false) - .value_name("COLLECTION") - .help("Add to this collection, if not specified default from config will be used")) .arg(Arg::with_name("urls") .index(1) .takes_value(true) @@ -47,22 +45,13 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .subcommand(SubCommand::with_name("remove") .about("Remove bookmarks") .version("0.1") - .arg(Arg::with_name("collection") - .long("collection") - .short("c") - .takes_value(true) - .required(false) - .multiple(false) - .value_name("COLLECTION") - .help("Remove from this collection, if not specified default from config will be used")) - .arg(Arg::with_name("urls") + .arg(Arg::with_name("ids") .index(1) .takes_value(true) .required(true) .multiple(true) - .value_name("URL") - .validator(is_url) - .help("Remove this url(s)")) + .value_name("ID") + .help("Remove these urls, specified as ID")) ) // .subcommand(SubCommand::with_name("open") @@ -79,32 +68,41 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { // ) .subcommand(SubCommand::with_name("list") - .about("List bookmarks") + .about("List bookmarks, if used in pipe, ignores everything that is not a bookmark") .version("0.1") - .arg(Arg::with_name("collection") - .long("collection") - .short("c") + + .arg(Arg::with_name("ids") + .index(1) .takes_value(true) .required(false) - .multiple(false) - .value_name("COLLECTION") - .help("Select from this collection, if not specified default from config will be used")) - ) - - .subcommand(SubCommand::with_name("collection") - .about("Collection commands") - .version("0.1") - .arg(Arg::with_name("add") - .long("add") - .short("a") - .takes_value(true) - .value_name("NAME") - .help("Add a collection with this name")) - .arg(Arg::with_name("remove") - .long("remove") - .short("r") - .takes_value(true) - .value_name("NAME") - .help("Remove a collection with this name (and all links)")) + .multiple(true) + .value_name("ID") + .help("IDs of bookmarks to list")) ) } + +pub struct PathProvider; +impl IdPathProvider for PathProvider { + fn get_ids(matches: &ArgMatches) -> Result>> { + fn no_ids_error() -> Result>> { + Err(format_err!("Command does not get IDs as input")) + } + + fn get_id_paths(field: &str, subm: &ArgMatches) -> Result>> { + subm.values_of(field) + .map(|v| v + .map(PathBuf::from) + .map(|pb| pb.into_storeid()) + .collect::>>() + ) + .transpose() + } + + match matches.subcommand() { + ("add", _) => no_ids_error(), + ("remove", Some(subm)) => get_id_paths("ids", subm), + ("list", Some(subm)) => get_id_paths("ids", subm), + (other, _) => Err(format_err!("Not a known command: {}", other)), + } + } +} From 401e900936a38ce492076b53b551dce0af42f824 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 8 Dec 2019 14:00:31 +0100 Subject: [PATCH 5/5] Implement imag-bookmark "find" command Signed-off-by: Matthias Beyer --- bin/domain/imag-bookmark/src/lib.rs | 33 +++++++++++++++++++++++++++++ bin/domain/imag-bookmark/src/ui.rs | 22 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/bin/domain/imag-bookmark/src/lib.rs b/bin/domain/imag-bookmark/src/lib.rs index 98f50fca..6782ed59 100644 --- a/bin/domain/imag-bookmark/src/lib.rs +++ b/bin/domain/imag-bookmark/src/lib.rs @@ -81,6 +81,7 @@ impl ImagApplication for ImagBookmark { "add" => add(&rt), "list" => list(&rt), "remove" => remove(&rt), + "find" => find(&rt), other => { debug!("Unknown command"); if rt.handle_unknown_subcommand("imag-bookmark", other, rt.cli())?.success() { @@ -160,3 +161,35 @@ fn remove(rt: &Runtime) -> Result<()> { .collect() } +fn find(rt: &Runtime) -> Result<()> { + let substr = rt.cli().subcommand_matches("find").unwrap().value_of("substr").unwrap(); + + if let Some(ids) = rt.ids::()? { + ids.into_iter() + .map(Ok) + .into_get_iter(rt.store()) + } else { + rt.store() + .all_bookmarks()? + .into_get_iter() + } + .map_inner_ok_or_else(|| err_msg("Did not find one entry")) + .and_then_ok(|fle| { + if fle.is_bookmark()? { + let url = fle + .get_url()? + .ok_or_else(|| format_err!("Failed to retrieve URL for {}", fle.get_location()))?; + if url.as_str().contains(substr) { + if !rt.output_is_pipe() { + writeln!(rt.stdout(), "{}", url)?; + } + rt.report_touched(fle.get_location()).map_err(Error::from) + } else { + Ok(()) + } + } else { + Ok(()) + } + }) + .collect() +} diff --git a/bin/domain/imag-bookmark/src/ui.rs b/bin/domain/imag-bookmark/src/ui.rs index e248a420..d1024764 100644 --- a/bin/domain/imag-bookmark/src/ui.rs +++ b/bin/domain/imag-bookmark/src/ui.rs @@ -79,6 +79,27 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .value_name("ID") .help("IDs of bookmarks to list")) ) + + .subcommand(SubCommand::with_name("find") + .about("Find a bookmark by substring of URL") + .version("0.1") + + .arg(Arg::with_name("substr") + .index(1) + .takes_value(true) + .required(true) + .multiple(false) + .value_name("str") + .help("Substring to search in the URL.")) + + .arg(Arg::with_name("ids") + .index(2) + .takes_value(true) + .required(false) + .multiple(true) + .value_name("IDs") + .help("IDs to search in (if not passed, searches all bookmarks. Can also be provided via STDIN")) + ) } pub struct PathProvider; @@ -102,6 +123,7 @@ impl IdPathProvider for PathProvider { ("add", _) => no_ids_error(), ("remove", Some(subm)) => get_id_paths("ids", subm), ("list", Some(subm)) => get_id_paths("ids", subm), + ("find", Some(subm)) => get_id_paths("ids", subm), (other, _) => Err(format_err!("Not a known command: {}", other)), } }