Revert "Remove stuff for the focus-shift"
This reverts commit 63a7f0d8a9
.
This commit is contained in:
parent
5ba2568415
commit
80fb6cc2bd
70 changed files with 4597 additions and 0 deletions
|
@ -15,6 +15,15 @@ homepage = "http://imag-pim.org"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
|
[dependencies.libimagbookmark]
|
||||||
|
path = "../libimagbookmark"
|
||||||
|
|
||||||
|
[dependencies.libimagcounter]
|
||||||
|
path = "../libimagcounter"
|
||||||
|
|
||||||
|
[dependencies.libimagdiary]
|
||||||
|
path = "../libimagdiary"
|
||||||
|
|
||||||
[dependencies.libimagentryfilter]
|
[dependencies.libimagentryfilter]
|
||||||
path = "../libimagentryfilter"
|
path = "../libimagentryfilter"
|
||||||
|
|
||||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -2,13 +2,21 @@
|
||||||
members = [
|
members = [
|
||||||
".imag-documentation",
|
".imag-documentation",
|
||||||
"bin",
|
"bin",
|
||||||
|
"imag-bookmark",
|
||||||
|
"imag-counter",
|
||||||
|
"imag-diary",
|
||||||
"imag-link",
|
"imag-link",
|
||||||
|
"imag-mail",
|
||||||
"imag-notes",
|
"imag-notes",
|
||||||
"imag-ref",
|
"imag-ref",
|
||||||
"imag-store",
|
"imag-store",
|
||||||
"imag-tag",
|
"imag-tag",
|
||||||
|
"imag-todo",
|
||||||
"imag-view",
|
"imag-view",
|
||||||
"libimagannotation",
|
"libimagannotation",
|
||||||
|
"libimagbookmark",
|
||||||
|
"libimagcounter",
|
||||||
|
"libimagdiary",
|
||||||
"libimagentryedit",
|
"libimagentryedit",
|
||||||
"libimagentryfilter",
|
"libimagentryfilter",
|
||||||
"libimagentrylink",
|
"libimagentrylink",
|
||||||
|
@ -18,11 +26,13 @@ members = [
|
||||||
"libimagentryview",
|
"libimagentryview",
|
||||||
"libimagerror",
|
"libimagerror",
|
||||||
"libimaginteraction",
|
"libimaginteraction",
|
||||||
|
"libimagmail",
|
||||||
"libimagnotes",
|
"libimagnotes",
|
||||||
"libimagref",
|
"libimagref",
|
||||||
"libimagrt",
|
"libimagrt",
|
||||||
"libimagstore",
|
"libimagstore",
|
||||||
"libimagstorestdhook",
|
"libimagstorestdhook",
|
||||||
"libimagtimeui",
|
"libimagtimeui",
|
||||||
|
"libimagtodo",
|
||||||
"libimagutil",
|
"libimagutil",
|
||||||
]
|
]
|
||||||
|
|
38
imag-bookmark/Cargo.toml
Normal file
38
imag-bookmark/Cargo.toml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
[package]
|
||||||
|
name = "imag-bookmark"
|
||||||
|
version = "0.2.0"
|
||||||
|
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
||||||
|
|
||||||
|
description = "Part of the imag core distribution: imag-bookmark command"
|
||||||
|
|
||||||
|
keywords = ["imag", "PIM", "personal", "information", "management"]
|
||||||
|
readme = "../README.md"
|
||||||
|
license = "LGPL-2.1"
|
||||||
|
|
||||||
|
documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html"
|
||||||
|
repository = "https://github.com/matthiasbeyer/imag"
|
||||||
|
homepage = "http://imag-pim.org"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = ">=2.17"
|
||||||
|
log = "0.3"
|
||||||
|
version = "2.0.1"
|
||||||
|
|
||||||
|
[dependencies.libimagrt]
|
||||||
|
path = "../libimagrt"
|
||||||
|
|
||||||
|
[dependencies.libimagutil]
|
||||||
|
path = "../libimagutil"
|
||||||
|
|
||||||
|
[dependencies.libimagbookmark]
|
||||||
|
path = "../libimagbookmark"
|
||||||
|
|
||||||
|
[dependencies.libimagerror]
|
||||||
|
path = "../libimagerror"
|
||||||
|
|
||||||
|
[dependencies.libimagentrylink]
|
||||||
|
path = "../libimagentrylink"
|
||||||
|
|
||||||
|
[dependencies.libimagentrytag]
|
||||||
|
path = "../libimagentrytag"
|
||||||
|
|
1
imag-bookmark/README.md
Symbolic link
1
imag-bookmark/README.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../doc/src/04020-module-bookmarks.md
|
158
imag-bookmark/src/main.rs
Normal file
158
imag-bookmark/src/main.rs
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#![deny(
|
||||||
|
non_camel_case_types,
|
||||||
|
non_snake_case,
|
||||||
|
path_statements,
|
||||||
|
trivial_numeric_casts,
|
||||||
|
unstable_features,
|
||||||
|
unused_allocation,
|
||||||
|
unused_import_braces,
|
||||||
|
unused_imports,
|
||||||
|
unused_must_use,
|
||||||
|
unused_mut,
|
||||||
|
unused_qualifications,
|
||||||
|
while_true,
|
||||||
|
)]
|
||||||
|
|
||||||
|
extern crate clap;
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
#[macro_use] extern crate version;
|
||||||
|
|
||||||
|
extern crate libimagbookmark;
|
||||||
|
extern crate libimagentrylink;
|
||||||
|
extern crate libimagentrytag;
|
||||||
|
extern crate libimagrt;
|
||||||
|
extern crate libimagerror;
|
||||||
|
extern crate libimagutil;
|
||||||
|
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
use libimagrt::runtime::Runtime;
|
||||||
|
use libimagrt::setup::generate_runtime_setup;
|
||||||
|
use libimagbookmark::collection::BookmarkCollection;
|
||||||
|
use libimagbookmark::link::Link as BookmarkLink;
|
||||||
|
use libimagerror::trace::{MapErrTrace, trace_error, trace_error_exit};
|
||||||
|
use libimagutil::info_result::*;
|
||||||
|
use libimagutil::iter::*;
|
||||||
|
|
||||||
|
mod ui;
|
||||||
|
|
||||||
|
use ui::build_ui;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let rt = generate_runtime_setup("imag-bookmark",
|
||||||
|
&version!()[..],
|
||||||
|
"Bookmark collection tool",
|
||||||
|
build_ui);
|
||||||
|
|
||||||
|
rt.cli()
|
||||||
|
.subcommand_name()
|
||||||
|
.map(|name| {
|
||||||
|
debug!("Call {}", name);
|
||||||
|
match name {
|
||||||
|
"add" => add(&rt),
|
||||||
|
"collection" => collection(&rt),
|
||||||
|
"list" => list(&rt),
|
||||||
|
"remove" => remove(&rt),
|
||||||
|
_ => {
|
||||||
|
debug!("Unknown command"); // More error handling
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add(rt: &Runtime) {
|
||||||
|
let scmd = rt.cli().subcommand_matches("add").unwrap();
|
||||||
|
let coll = scmd.value_of("collection").unwrap(); // enforced by clap
|
||||||
|
|
||||||
|
BookmarkCollection::get(rt.store(), coll)
|
||||||
|
.and_then(|mut collection| {
|
||||||
|
scmd.values_of("urls")
|
||||||
|
.unwrap() // enforced by clap
|
||||||
|
.fold_defresult(|url| collection.add_link(BookmarkLink::from(url)))
|
||||||
|
})
|
||||||
|
.map_err_trace()
|
||||||
|
.map_info_str("Ready")
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collection(rt: &Runtime) {
|
||||||
|
let scmd = rt.cli().subcommand_matches("collection").unwrap();
|
||||||
|
|
||||||
|
if scmd.is_present("add") { // adding a new collection
|
||||||
|
let name = scmd.value_of("add").unwrap();
|
||||||
|
if let Ok(_) = BookmarkCollection::new(rt.store(), name) {
|
||||||
|
info!("Created: {}", name);
|
||||||
|
} else {
|
||||||
|
warn!("Creating collection {} failed", name);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if scmd.is_present("remove") { // remove a collection
|
||||||
|
let name = scmd.value_of("remove").unwrap();
|
||||||
|
if let Ok(_) = BookmarkCollection::delete(rt.store(), name) {
|
||||||
|
info!("Deleted: {}", name);
|
||||||
|
} else {
|
||||||
|
warn!("Deleting collection {} failed", name);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list(rt: &Runtime) {
|
||||||
|
let scmd = rt.cli().subcommand_matches("list").unwrap();
|
||||||
|
let coll = scmd.value_of("collection").unwrap(); // enforced by clap
|
||||||
|
|
||||||
|
BookmarkCollection::get(rt.store(), coll)
|
||||||
|
.map(|collection| {
|
||||||
|
match collection.links() {
|
||||||
|
Ok(links) => {
|
||||||
|
debug!("Listing...");
|
||||||
|
for (i, link) in links.enumerate() {
|
||||||
|
match link {
|
||||||
|
Ok(link) => println!("{: >3}: {}", i, link),
|
||||||
|
Err(e) => trace_error(&e)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
debug!("... ready with listing");
|
||||||
|
},
|
||||||
|
Err(e) => trace_error_exit(&e, 1),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
info!("Ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove(rt: &Runtime) {
|
||||||
|
let scmd = rt.cli().subcommand_matches("remove").unwrap();
|
||||||
|
let coll = scmd.value_of("collection").unwrap(); // enforced by clap
|
||||||
|
|
||||||
|
BookmarkCollection::get(rt.store(), coll)
|
||||||
|
.map(|mut collection| {
|
||||||
|
for url in scmd.values_of("urls").unwrap() { // enforced by clap
|
||||||
|
collection.remove_link(BookmarkLink::from(url)).map_err(|e| trace_error(&e)).ok();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
info!("Ready");
|
||||||
|
}
|
||||||
|
|
122
imag-bookmark/src/ui.rs
Normal file
122
imag-bookmark/src/ui.rs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 clap::{Arg, App, SubCommand};
|
||||||
|
|
||||||
|
use libimagentrytag::ui::tag_add_arg;
|
||||||
|
use libimagutil::cli_validators::*;
|
||||||
|
|
||||||
|
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(true)
|
||||||
|
.multiple(false)
|
||||||
|
.value_name("COLLECTION")
|
||||||
|
.help("Add to this collection"))
|
||||||
|
.arg(Arg::with_name("urls")
|
||||||
|
.long("urls")
|
||||||
|
.short("u")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.multiple(true)
|
||||||
|
.value_name("URL")
|
||||||
|
.validator(is_url)
|
||||||
|
.help("Add this URL, multiple possible"))
|
||||||
|
.arg(tag_add_arg())
|
||||||
|
)
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("remove")
|
||||||
|
.about("Remove bookmarks")
|
||||||
|
.version("0.1")
|
||||||
|
.arg(Arg::with_name("collection")
|
||||||
|
.long("collection")
|
||||||
|
.short("c")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.multiple(false)
|
||||||
|
.value_name("COLLECTION")
|
||||||
|
.help("Remove from this collection"))
|
||||||
|
.arg(Arg::with_name("urls")
|
||||||
|
.long("urls")
|
||||||
|
.short("u")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.multiple(true)
|
||||||
|
.value_name("URL")
|
||||||
|
.validator(is_url)
|
||||||
|
.help("Remove these urls, regex supported"))
|
||||||
|
)
|
||||||
|
|
||||||
|
// .subcommand(SubCommand::with_name("open")
|
||||||
|
// .about("Open bookmarks (via xdg-open)")
|
||||||
|
// .version("0.1")
|
||||||
|
// .arg(Arg::with_name("collection")
|
||||||
|
// .long("collection")
|
||||||
|
// .short("c")
|
||||||
|
// .takes_value(true)
|
||||||
|
// .required(true)
|
||||||
|
// .multiple(false)
|
||||||
|
// .value_name("COLLECTION")
|
||||||
|
// .help("Select from this collection"))
|
||||||
|
// )
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("list")
|
||||||
|
.about("List bookmarks")
|
||||||
|
.version("0.1")
|
||||||
|
.arg(Arg::with_name("collection")
|
||||||
|
.long("collection")
|
||||||
|
.short("c")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.multiple(false)
|
||||||
|
.value_name("COLLECTION")
|
||||||
|
.help("Select from this collection"))
|
||||||
|
.arg(Arg::with_name("tags")
|
||||||
|
.long("tags")
|
||||||
|
.short("t")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.multiple(true)
|
||||||
|
.value_name("TAGS")
|
||||||
|
.help("Filter links to contain these tags. When multiple tags are specified, all of them must be set for the link to match."))
|
||||||
|
)
|
||||||
|
|
||||||
|
.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)"))
|
||||||
|
)
|
||||||
|
}
|
32
imag-counter/Cargo.toml
Normal file
32
imag-counter/Cargo.toml
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
[package]
|
||||||
|
name = "imag-counter"
|
||||||
|
version = "0.2.0"
|
||||||
|
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
||||||
|
|
||||||
|
description = "Part of the imag core distribution: imag-counter command"
|
||||||
|
|
||||||
|
keywords = ["imag", "PIM", "personal", "information", "management"]
|
||||||
|
readme = "../README.md"
|
||||||
|
license = "LGPL-2.1"
|
||||||
|
|
||||||
|
documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html"
|
||||||
|
repository = "https://github.com/matthiasbeyer/imag"
|
||||||
|
homepage = "http://imag-pim.org"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = ">=2.17"
|
||||||
|
log = "0.3"
|
||||||
|
version = "2.0.1"
|
||||||
|
|
||||||
|
[dependencies.libimagrt]
|
||||||
|
path = "../libimagrt"
|
||||||
|
|
||||||
|
[dependencies.libimagerror]
|
||||||
|
path = "../libimagerror"
|
||||||
|
|
||||||
|
[dependencies.libimagutil]
|
||||||
|
path = "../libimagutil"
|
||||||
|
|
||||||
|
[dependencies.libimagcounter]
|
||||||
|
path = "../libimagcounter"
|
||||||
|
|
1
imag-counter/README.md
Symbolic link
1
imag-counter/README.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../doc/src/04020-module-counter.md
|
50
imag-counter/src/create.rs
Normal file
50
imag-counter/src/create.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::str::FromStr;
|
||||||
|
|
||||||
|
use libimagrt::runtime::Runtime;
|
||||||
|
use libimagerror::trace::trace_error_exit;
|
||||||
|
use libimagcounter::counter::Counter;
|
||||||
|
use libimagcounter::counter::CounterUnit;
|
||||||
|
|
||||||
|
pub fn create(rt: &Runtime) {
|
||||||
|
rt.cli()
|
||||||
|
.subcommand_matches("create")
|
||||||
|
.map(|scmd| {
|
||||||
|
debug!("Found 'create' subcommand...");
|
||||||
|
|
||||||
|
let name = scmd.value_of("name").unwrap(); // safe because clap enforces
|
||||||
|
let init : i64 = scmd
|
||||||
|
.value_of("initval")
|
||||||
|
.and_then(|i| FromStr::from_str(i).ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
let unit = scmd
|
||||||
|
.value_of("unit")
|
||||||
|
.map(CounterUnit::new);
|
||||||
|
|
||||||
|
Counter::new(rt.store(), String::from(name), init)
|
||||||
|
.and_then(|c| c.with_unit(unit))
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
warn!("Could not create Counter '{}' with initial value '{}'", name, init);
|
||||||
|
trace_error_exit(&e, 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
39
imag-counter/src/delete.rs
Normal file
39
imag-counter/src/delete.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 libimagrt::runtime::Runtime;
|
||||||
|
use libimagerror::trace::trace_error_exit;
|
||||||
|
use libimagcounter::counter::Counter;
|
||||||
|
|
||||||
|
pub fn delete(rt: &Runtime) {
|
||||||
|
rt.cli()
|
||||||
|
.subcommand_matches("delete")
|
||||||
|
.map(|scmd| {
|
||||||
|
debug!("Found 'delete' subcommand...");
|
||||||
|
|
||||||
|
let name = String::from(scmd.value_of("name").unwrap()); // safe because clap enforces
|
||||||
|
|
||||||
|
if let Err(e) = Counter::delete(name, rt.store()) {
|
||||||
|
trace_error_exit(&e, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Ok");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
174
imag-counter/src/interactive.rs
Normal file
174
imag-counter/src/interactive.rs
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::collections::BTreeMap;
|
||||||
|
use std::fmt::{Display, Formatter, Error};
|
||||||
|
use std::io::Write;
|
||||||
|
use std::io::stderr;
|
||||||
|
use std::io::stdin;
|
||||||
|
use std::process::exit;
|
||||||
|
use std::result::Result as RResult;
|
||||||
|
|
||||||
|
use libimagcounter::counter::Counter;
|
||||||
|
use libimagcounter::error::CounterError;
|
||||||
|
use libimagrt::runtime::Runtime;
|
||||||
|
use libimagutil::key_value_split::IntoKeyValue;
|
||||||
|
use libimagutil::warn_exit::warn_exit;
|
||||||
|
use libimagerror::trace::{trace_error, trace_error_exit};
|
||||||
|
|
||||||
|
type Result<T> = RResult<T, CounterError>;
|
||||||
|
|
||||||
|
pub fn interactive(rt: &Runtime) {
|
||||||
|
let scmd = rt.cli().subcommand_matches("interactive");
|
||||||
|
if scmd.is_none() {
|
||||||
|
warn_exit("No subcommand", 1);
|
||||||
|
}
|
||||||
|
let scmd = scmd.unwrap();
|
||||||
|
debug!("Found 'interactive' command");
|
||||||
|
|
||||||
|
let mut pairs : BTreeMap<char, Binding> = BTreeMap::new();
|
||||||
|
|
||||||
|
for spec in scmd.values_of("spec").unwrap() {
|
||||||
|
match compute_pair(rt, &spec) {
|
||||||
|
Ok((k, v)) => { pairs.insert(k, v); },
|
||||||
|
Err(e) => { trace_error(&e); },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !has_quit_binding(&pairs) {
|
||||||
|
pairs.insert('q', Binding::Function(String::from("quit"), Box::new(quit)));
|
||||||
|
}
|
||||||
|
|
||||||
|
stderr().flush().ok();
|
||||||
|
loop {
|
||||||
|
println!("---");
|
||||||
|
for (k, v) in &pairs {
|
||||||
|
println!("\t[{}] => {}", k, v);
|
||||||
|
}
|
||||||
|
println!("---");
|
||||||
|
print!("counter > ");
|
||||||
|
|
||||||
|
let mut input = String::new();
|
||||||
|
if let Err(e) = stdin().read_line(&mut input) {
|
||||||
|
trace_error_exit(&e, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let cont = if !input.is_empty() {
|
||||||
|
let increment = match input.chars().next() { Some('-') => false, _ => true };
|
||||||
|
input.chars().all(|chr| {
|
||||||
|
match pairs.get_mut(&chr) {
|
||||||
|
Some(&mut Binding::Counter(ref mut ctr)) => {
|
||||||
|
if increment {
|
||||||
|
debug!("Incrementing");
|
||||||
|
if let Err(e) = ctr.inc() {
|
||||||
|
trace_error(&e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug!("Decrementing");
|
||||||
|
if let Err(e) = ctr.dec() {
|
||||||
|
trace_error(&e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
},
|
||||||
|
Some(&mut Binding::Function(ref name, ref f)) => {
|
||||||
|
debug!("Calling {}", name);
|
||||||
|
f()
|
||||||
|
},
|
||||||
|
None => true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
println!("No input...");
|
||||||
|
println!("\tUse a single character to increment the counter which is bound to it");
|
||||||
|
println!("\tUse 'q' (or the character bound to quit()) to exit");
|
||||||
|
println!("\tPrefix the line with '-' to decrement instead of increment the counters");
|
||||||
|
println!("");
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
if !cont {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_quit_binding(pairs: &BTreeMap<char, Binding>) -> bool {
|
||||||
|
pairs.iter()
|
||||||
|
.any(|(_, bind)| {
|
||||||
|
match *bind {
|
||||||
|
Binding::Function(ref name, _) => name == "quit",
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Binding<'a> {
|
||||||
|
Counter(Counter<'a>),
|
||||||
|
Function(String, Box<Fn() -> bool>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Binding<'a> {
|
||||||
|
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> RResult<(), Error> {
|
||||||
|
match *self {
|
||||||
|
Binding::Counter(ref c) => {
|
||||||
|
match c.name() {
|
||||||
|
Ok(name) => {
|
||||||
|
try!(write!(fmt, "{}", name));
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
trace_error(&e);
|
||||||
|
Ok(()) // TODO: Find a better way to escalate here.
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Binding::Function(ref name, _) => write!(fmt, "{}()", name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_pair<'a>(rt: &'a Runtime, spec: &str) -> Result<(char, Binding<'a>)> {
|
||||||
|
let kv = String::from(spec).into_kv();
|
||||||
|
if kv.is_none() {
|
||||||
|
warn_exit("Key-Value parsing failed!", 1);
|
||||||
|
}
|
||||||
|
let kv = kv.unwrap();
|
||||||
|
|
||||||
|
let (k, v) = kv.into();
|
||||||
|
if !k.len() == 1 {
|
||||||
|
// We have a key which is not only a single character!
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if v == "quit" {
|
||||||
|
// TODO uncaught unwrap()
|
||||||
|
Ok((k.chars().next().unwrap(), Binding::Function(String::from("quit"), Box::new(quit))))
|
||||||
|
} else {
|
||||||
|
// TODO uncaught unwrap()
|
||||||
|
Counter::load(v, rt.store()).and_then(|ctr| Ok((k.chars().next().unwrap(), Binding::Counter(ctr))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quit() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
54
imag-counter/src/list.rs
Normal file
54
imag-counter/src/list.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 libimagrt::runtime::Runtime;
|
||||||
|
use libimagerror::trace::{MapErrTrace, trace_error};
|
||||||
|
use libimagcounter::counter::Counter;
|
||||||
|
|
||||||
|
pub fn list(rt: &Runtime) {
|
||||||
|
rt.cli()
|
||||||
|
.subcommand_matches("list")
|
||||||
|
.map(|_| {
|
||||||
|
debug!("Found 'list' subcommand...");
|
||||||
|
|
||||||
|
Counter::all_counters(rt.store()).map(|iterator| {
|
||||||
|
for counter in iterator {
|
||||||
|
counter.map(|c| {
|
||||||
|
let name = c.name();
|
||||||
|
let value = c.value();
|
||||||
|
let unit = c.unit();
|
||||||
|
|
||||||
|
if name.is_err() {
|
||||||
|
trace_error(&name.unwrap_err());
|
||||||
|
} else if value.is_err() {
|
||||||
|
trace_error(&value.unwrap_err());
|
||||||
|
} else if unit.is_none() {
|
||||||
|
println!("{} - {}", name.unwrap(), value.unwrap());
|
||||||
|
} else {
|
||||||
|
println!("{} - {} {}", name.unwrap(), value.unwrap(), unit.unwrap());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err_trace()
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err_trace()
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
139
imag-counter/src/main.rs
Normal file
139
imag-counter/src/main.rs
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#![deny(
|
||||||
|
non_camel_case_types,
|
||||||
|
non_snake_case,
|
||||||
|
path_statements,
|
||||||
|
trivial_numeric_casts,
|
||||||
|
unstable_features,
|
||||||
|
unused_allocation,
|
||||||
|
unused_import_braces,
|
||||||
|
unused_imports,
|
||||||
|
unused_must_use,
|
||||||
|
unused_mut,
|
||||||
|
unused_qualifications,
|
||||||
|
while_true,
|
||||||
|
)]
|
||||||
|
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
#[macro_use] extern crate version;
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
extern crate libimagcounter;
|
||||||
|
extern crate libimagrt;
|
||||||
|
extern crate libimagerror;
|
||||||
|
extern crate libimagutil;
|
||||||
|
|
||||||
|
use std::process::exit;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use libimagrt::setup::generate_runtime_setup;
|
||||||
|
use libimagcounter::counter::Counter;
|
||||||
|
use libimagerror::trace::MapErrTrace;
|
||||||
|
use libimagutil::key_value_split::IntoKeyValue;
|
||||||
|
use libimagutil::info_result::*;
|
||||||
|
|
||||||
|
mod create;
|
||||||
|
mod delete;
|
||||||
|
mod interactive;
|
||||||
|
mod list;
|
||||||
|
mod ui;
|
||||||
|
|
||||||
|
use ui::build_ui;
|
||||||
|
use create::create;
|
||||||
|
use delete::delete;
|
||||||
|
use interactive::interactive;
|
||||||
|
use list::list;
|
||||||
|
|
||||||
|
enum Action {
|
||||||
|
Inc,
|
||||||
|
Dec,
|
||||||
|
Reset,
|
||||||
|
Set,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let rt = generate_runtime_setup("imag-counter",
|
||||||
|
&version!()[..],
|
||||||
|
"Counter tool to count things",
|
||||||
|
build_ui);
|
||||||
|
|
||||||
|
rt.cli()
|
||||||
|
.subcommand_name()
|
||||||
|
.map_or_else(|| {
|
||||||
|
let (action, name) = {
|
||||||
|
if rt.cli().is_present("increment") {
|
||||||
|
(Action::Inc, rt.cli().value_of("increment").unwrap())
|
||||||
|
} else if rt.cli().is_present("decrement") {
|
||||||
|
(Action::Dec, rt.cli().value_of("decrement").unwrap())
|
||||||
|
} else if rt.cli().is_present("reset") {
|
||||||
|
(Action::Reset, rt.cli().value_of("reset").unwrap())
|
||||||
|
} else /* rt.cli().is_present("set") */ {
|
||||||
|
(Action::Set, rt.cli().value_of("set").unwrap())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match action {
|
||||||
|
Action::Inc => {
|
||||||
|
Counter::load(String::from(name), rt.store())
|
||||||
|
.map(|mut c| c.inc().map_err_trace_exit(1).map_info_str("Ok"))
|
||||||
|
},
|
||||||
|
Action::Dec => {
|
||||||
|
Counter::load(String::from(name), rt.store())
|
||||||
|
.map(|mut c| c.dec().map_err_trace_exit(1).map_info_str("Ok"))
|
||||||
|
},
|
||||||
|
Action::Reset => {
|
||||||
|
Counter::load(String::from(name), rt.store())
|
||||||
|
.map(|mut c| c.reset().map_err_trace_exit(1).map_info_str("Ok"))
|
||||||
|
},
|
||||||
|
Action::Set => {
|
||||||
|
let kv = String::from(name).into_kv();
|
||||||
|
if kv.is_none() {
|
||||||
|
warn!("Not a key-value pair: '{}'", name);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
let (key, value) = kv.unwrap().into();
|
||||||
|
let value = FromStr::from_str(&value[..]);
|
||||||
|
if value.is_err() {
|
||||||
|
warn!("Not a integer: '{:?}'", value);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
let value : i64 = value.unwrap();
|
||||||
|
Counter::load(String::from(key), rt.store())
|
||||||
|
.map(|mut c| c.set(value).map_err_trace_exit(1).map_info_str("Ok"))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
.map_err_trace()
|
||||||
|
.ok();
|
||||||
|
},
|
||||||
|
|name| {
|
||||||
|
debug!("Call: {}", name);
|
||||||
|
match name {
|
||||||
|
"create" => create(&rt),
|
||||||
|
"delete" => delete(&rt),
|
||||||
|
"interactive" => interactive(&rt),
|
||||||
|
"list" => list(&rt),
|
||||||
|
_ => {
|
||||||
|
debug!("Unknown command"); // More error handling
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
139
imag-counter/src/ui.rs
Normal file
139
imag-counter/src/ui.rs
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 clap::{Arg, App, SubCommand};
|
||||||
|
|
||||||
|
pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
|
||||||
|
app
|
||||||
|
.arg(Arg::with_name("increment")
|
||||||
|
.long("inc")
|
||||||
|
.short("i")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.help("Increment a counter")
|
||||||
|
.value_name("COUNTER"))
|
||||||
|
|
||||||
|
.arg(Arg::with_name("decrement")
|
||||||
|
.long("dec")
|
||||||
|
.short("d")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.help("Decrement a counter")
|
||||||
|
.value_name("COUNTER"))
|
||||||
|
|
||||||
|
.arg(Arg::with_name("reset")
|
||||||
|
.long("reset")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.help("Reset a counter")
|
||||||
|
.value_name("COUNTER"))
|
||||||
|
|
||||||
|
.arg(Arg::with_name("set")
|
||||||
|
.long("set")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.help("Set a counter")
|
||||||
|
.value_name("COUNTER"))
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("create")
|
||||||
|
.about("Create a counter")
|
||||||
|
.version("0.1")
|
||||||
|
.arg(Arg::with_name("name")
|
||||||
|
.long("name")
|
||||||
|
.short("n")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("Create counter with this name")
|
||||||
|
.value_name("NAME"))
|
||||||
|
.arg(Arg::with_name("initval")
|
||||||
|
.long("init")
|
||||||
|
.short("i")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.help("Initial value")
|
||||||
|
.value_name("VALUE"))
|
||||||
|
.arg(Arg::with_name("unit")
|
||||||
|
.long("unit")
|
||||||
|
.short("u")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.help("measurement unit")
|
||||||
|
.value_name("UNIT")))
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("delete")
|
||||||
|
.about("Delete a counter")
|
||||||
|
.version("0.1")
|
||||||
|
.arg(Arg::with_name("name")
|
||||||
|
.long("name")
|
||||||
|
.short("n")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("Create counter with this name")
|
||||||
|
.value_name("NAME")))
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("list")
|
||||||
|
.about("List counters")
|
||||||
|
.version("0.1")
|
||||||
|
.arg(Arg::with_name("name")
|
||||||
|
.long("name")
|
||||||
|
.short("n")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.help("List counters with this name (foo/bar and baz/bar would match 'bar')")
|
||||||
|
.value_name("NAME"))
|
||||||
|
|
||||||
|
.arg(Arg::with_name("greater-than")
|
||||||
|
.long("greater")
|
||||||
|
.short("g")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.help("List counters which are greater than VALUE")
|
||||||
|
.value_name("VALUE"))
|
||||||
|
|
||||||
|
.arg(Arg::with_name("lower-than")
|
||||||
|
.long("lower")
|
||||||
|
.short("l")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.help("List counters which are lower than VALUE")
|
||||||
|
.value_name("VALUE"))
|
||||||
|
|
||||||
|
.arg(Arg::with_name("equals")
|
||||||
|
.long("equal")
|
||||||
|
.short("e")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.help("List counters which equal VALUE")
|
||||||
|
.value_name("VALUE"))
|
||||||
|
)
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("interactive")
|
||||||
|
.about("Interactively count things")
|
||||||
|
.version("0.1")
|
||||||
|
.arg(Arg::with_name("spec")
|
||||||
|
.long("spec")
|
||||||
|
.short("s")
|
||||||
|
.takes_value(true)
|
||||||
|
.multiple(true)
|
||||||
|
.required(true)
|
||||||
|
.help("Specification for key-bindings. Use <KEY>=<VALUE> where KEY is the
|
||||||
|
key to bind (single character) and VALUE is the path to the counter to bind
|
||||||
|
to.")
|
||||||
|
.value_name("KEY=VALUE")))
|
||||||
|
}
|
48
imag-diary/Cargo.toml
Normal file
48
imag-diary/Cargo.toml
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
[package]
|
||||||
|
name = "imag-diary"
|
||||||
|
version = "0.2.0"
|
||||||
|
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
||||||
|
|
||||||
|
description = "Part of the imag core distribution: imag-diary command"
|
||||||
|
|
||||||
|
keywords = ["imag", "PIM", "personal", "information", "management"]
|
||||||
|
readme = "../README.md"
|
||||||
|
license = "LGPL-2.1"
|
||||||
|
|
||||||
|
documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html"
|
||||||
|
repository = "https://github.com/matthiasbeyer/imag"
|
||||||
|
homepage = "http://imag-pim.org"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chrono = "0.2"
|
||||||
|
version = "2.0"
|
||||||
|
clap = "2.*"
|
||||||
|
log = "0.3"
|
||||||
|
|
||||||
|
[dependencies.libimagrt]
|
||||||
|
path = "../libimagrt"
|
||||||
|
|
||||||
|
[dependencies.libimagdiary]
|
||||||
|
path = "../libimagdiary"
|
||||||
|
|
||||||
|
[dependencies.libimagentryedit]
|
||||||
|
path = "../libimagentryedit"
|
||||||
|
|
||||||
|
[dependencies.libimagentrylist]
|
||||||
|
path = "../libimagentrylist"
|
||||||
|
|
||||||
|
[dependencies.libimagerror]
|
||||||
|
path = "../libimagerror"
|
||||||
|
|
||||||
|
[dependencies.libimaginteraction]
|
||||||
|
path = "../libimaginteraction"
|
||||||
|
|
||||||
|
[dependencies.libimagutil]
|
||||||
|
path = "../libimagutil"
|
||||||
|
|
||||||
|
[dependencies.libimagstore]
|
||||||
|
path = "../libimagstore"
|
||||||
|
|
||||||
|
[dependencies.libimagtimeui]
|
||||||
|
path = "../libimagtimeui"
|
||||||
|
|
1
imag-diary/README.md
Symbolic link
1
imag-diary/README.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../doc/src/04020-module-diary.md
|
123
imag-diary/src/create.rs
Normal file
123
imag-diary/src/create.rs
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::process::exit;
|
||||||
|
|
||||||
|
use libimagdiary::diary::Diary;
|
||||||
|
use libimagdiary::diaryid::DiaryId;
|
||||||
|
use libimagdiary::error::DiaryErrorKind as DEK;
|
||||||
|
use libimagdiary::error::MapErrInto;
|
||||||
|
use libimagentryedit::edit::Edit;
|
||||||
|
use libimagrt::runtime::Runtime;
|
||||||
|
use libimagerror::trace::trace_error;
|
||||||
|
use libimagdiary::entry::Entry;
|
||||||
|
use libimagdiary::result::Result;
|
||||||
|
use libimagutil::warn_exit::warn_exit;
|
||||||
|
|
||||||
|
use util::get_diary_name;
|
||||||
|
|
||||||
|
pub fn create(rt: &Runtime) {
|
||||||
|
let diaryname = get_diary_name(rt)
|
||||||
|
.unwrap_or_else( || warn_exit("No diary selected. Use either the configuration file or the commandline option", 1));
|
||||||
|
|
||||||
|
let prevent_edit = rt.cli().subcommand_matches("create").unwrap().is_present("no-edit");
|
||||||
|
|
||||||
|
fn create_entry<'a>(diary: &'a Diary, rt: &Runtime) -> Result<Entry<'a>> {
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
let create = rt.cli().subcommand_matches("create").unwrap();
|
||||||
|
if !create.is_present("timed") {
|
||||||
|
debug!("Creating non-timed entry");
|
||||||
|
diary.new_entry_today()
|
||||||
|
} else {
|
||||||
|
let id = match create.value_of("timed") {
|
||||||
|
Some("h") | Some("hourly") => {
|
||||||
|
debug!("Creating hourly-timed entry");
|
||||||
|
let time = DiaryId::now(String::from(diary.name()));
|
||||||
|
let hr = create
|
||||||
|
.value_of("hour")
|
||||||
|
.map(|v| { debug!("Creating hourly entry with hour = {:?}", v); v })
|
||||||
|
.and_then(|s| {
|
||||||
|
FromStr::from_str(s)
|
||||||
|
.map_err(|_| warn!("Could not parse hour: '{}'", s))
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
|
.unwrap_or(time.hour());
|
||||||
|
|
||||||
|
time.with_hour(hr).with_minute(0)
|
||||||
|
},
|
||||||
|
|
||||||
|
Some("m") | Some("minutely") => {
|
||||||
|
debug!("Creating minutely-timed entry");
|
||||||
|
let time = DiaryId::now(String::from(diary.name()));
|
||||||
|
let hr = create
|
||||||
|
.value_of("hour")
|
||||||
|
.map(|h| { debug!("hour = {:?}", h); h })
|
||||||
|
.and_then(|s| {
|
||||||
|
FromStr::from_str(s)
|
||||||
|
.map_err(|_| warn!("Could not parse hour: '{}'", s))
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
|
.unwrap_or(time.hour());
|
||||||
|
|
||||||
|
let min = create
|
||||||
|
.value_of("minute")
|
||||||
|
.map(|m| { debug!("minute = {:?}", m); m })
|
||||||
|
.and_then(|s| {
|
||||||
|
FromStr::from_str(s)
|
||||||
|
.map_err(|_| warn!("Could not parse minute: '{}'", s))
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
|
.unwrap_or(time.minute());
|
||||||
|
|
||||||
|
time.with_hour(hr).with_minute(min)
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(_) => {
|
||||||
|
warn!("Timed creation failed: Unknown spec '{}'",
|
||||||
|
create.value_of("timed").unwrap());
|
||||||
|
exit(1);
|
||||||
|
},
|
||||||
|
|
||||||
|
None => warn_exit("Unexpected error, cannot continue", 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
diary.new_entry_by_id(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let diary = Diary::open(rt.store(), &diaryname[..]);
|
||||||
|
let res = create_entry(&diary, rt)
|
||||||
|
.and_then(|mut entry| {
|
||||||
|
if prevent_edit {
|
||||||
|
debug!("Not editing new diary entry");
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
debug!("Editing new diary entry");
|
||||||
|
entry.edit_content(rt).map_err_into(DEK::DiaryEditError)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Err(e) = res {
|
||||||
|
trace_error(&e);
|
||||||
|
} else {
|
||||||
|
info!("Ok!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
73
imag-diary/src/delete.rs
Normal file
73
imag-diary/src/delete.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 chrono::naive::datetime::NaiveDateTime;
|
||||||
|
|
||||||
|
use libimagdiary::diary::Diary;
|
||||||
|
use libimagdiary::diaryid::DiaryId;
|
||||||
|
use libimagrt::runtime::Runtime;
|
||||||
|
use libimagerror::trace::trace_error_exit;
|
||||||
|
use libimagtimeui::datetime::DateTime;
|
||||||
|
use libimagtimeui::parse::Parse;
|
||||||
|
use libimagutil::warn_exit::warn_exit;
|
||||||
|
|
||||||
|
use util::get_diary_name;
|
||||||
|
|
||||||
|
pub fn delete(rt: &Runtime) {
|
||||||
|
use libimaginteraction::ask::ask_bool;
|
||||||
|
|
||||||
|
let diaryname = get_diary_name(rt)
|
||||||
|
.unwrap_or_else(|| warn_exit("No diary selected. Use either the configuration file or the commandline option", 1));
|
||||||
|
|
||||||
|
let diary = Diary::open(rt.store(), &diaryname[..]);
|
||||||
|
debug!("Diary opened: {:?}", diary);
|
||||||
|
|
||||||
|
let datetime : Option<NaiveDateTime> = rt
|
||||||
|
.cli()
|
||||||
|
.subcommand_matches("delete")
|
||||||
|
.unwrap()
|
||||||
|
.value_of("datetime")
|
||||||
|
.map(|dt| { debug!("DateTime = {:?}", dt); dt })
|
||||||
|
.and_then(DateTime::parse)
|
||||||
|
.map(|dt| dt.into());
|
||||||
|
|
||||||
|
let to_del = match datetime {
|
||||||
|
Some(dt) => Some(diary.retrieve(DiaryId::from_datetime(diaryname.clone(), dt))),
|
||||||
|
None => diary.get_youngest_entry(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let to_del = match to_del {
|
||||||
|
Some(Ok(e)) => e,
|
||||||
|
|
||||||
|
Some(Err(e)) => trace_error_exit(&e, 1),
|
||||||
|
None => warn_exit("No entry", 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
if !ask_bool(&format!("Deleting {:?}", to_del.get_location())[..], Some(true)) {
|
||||||
|
info!("Aborting delete action");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = diary.delete_entry(to_del) {
|
||||||
|
trace_error_exit(&e, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Ok!");
|
||||||
|
}
|
||||||
|
|
62
imag-diary/src/edit.rs
Normal file
62
imag-diary/src/edit.rs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 chrono::naive::datetime::NaiveDateTime;
|
||||||
|
|
||||||
|
use libimagdiary::diary::Diary;
|
||||||
|
use libimagdiary::diaryid::DiaryId;
|
||||||
|
use libimagdiary::error::DiaryErrorKind as DEK;
|
||||||
|
use libimagdiary::error::MapErrInto;
|
||||||
|
use libimagentryedit::edit::Edit;
|
||||||
|
use libimagrt::runtime::Runtime;
|
||||||
|
use libimagerror::trace::MapErrTrace;
|
||||||
|
use libimagerror::into::IntoError;
|
||||||
|
use libimagtimeui::datetime::DateTime;
|
||||||
|
use libimagtimeui::parse::Parse;
|
||||||
|
use libimagutil::warn_exit::warn_exit;
|
||||||
|
|
||||||
|
use util::get_diary_name;
|
||||||
|
|
||||||
|
pub fn edit(rt: &Runtime) {
|
||||||
|
let diaryname = get_diary_name(rt).unwrap_or_else(|| warn_exit("No diary name", 1));
|
||||||
|
let diary = Diary::open(rt.store(), &diaryname[..]);
|
||||||
|
|
||||||
|
let datetime : Option<NaiveDateTime> = rt
|
||||||
|
.cli()
|
||||||
|
.subcommand_matches("edit")
|
||||||
|
.unwrap()
|
||||||
|
.value_of("datetime")
|
||||||
|
.and_then(DateTime::parse)
|
||||||
|
.map(|dt| dt.into());
|
||||||
|
|
||||||
|
let to_edit = match datetime {
|
||||||
|
Some(dt) => Some(diary.retrieve(DiaryId::from_datetime(diaryname.clone(), dt))),
|
||||||
|
None => diary.get_youngest_entry(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match to_edit {
|
||||||
|
Some(Ok(mut e)) => e.edit_content(rt).map_err_into(DEK::IOError),
|
||||||
|
|
||||||
|
Some(Err(e)) => Err(e),
|
||||||
|
None => Err(DEK::EntryNotInDiary.into_error()),
|
||||||
|
}
|
||||||
|
.map_err_trace().ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
68
imag-diary/src/list.rs
Normal file
68
imag-diary/src/list.rs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 libimagdiary::diary::Diary;
|
||||||
|
use libimagdiary::error::DiaryErrorKind as DEK;
|
||||||
|
use libimagdiary::error::MapErrInto;
|
||||||
|
use libimagentrylist::listers::core::CoreLister;
|
||||||
|
use libimagentrylist::lister::Lister;
|
||||||
|
use libimagrt::runtime::Runtime;
|
||||||
|
use libimagstore::store::Entry;
|
||||||
|
use libimagutil::warn_exit::warn_exit;
|
||||||
|
use libimagerror::trace::MapErrTrace;
|
||||||
|
use libimagutil::debug_result::*;
|
||||||
|
|
||||||
|
use util::get_diary_name;
|
||||||
|
|
||||||
|
pub fn list(rt: &Runtime) {
|
||||||
|
let diaryname = get_diary_name(rt)
|
||||||
|
.unwrap_or_else(|| warn_exit("No diary selected. Use either the configuration file or the commandline option", 1));
|
||||||
|
|
||||||
|
fn entry_to_location_listing_string(e: &Entry) -> String {
|
||||||
|
e.get_location().clone()
|
||||||
|
.without_base()
|
||||||
|
.to_str()
|
||||||
|
.map_err_trace()
|
||||||
|
.unwrap_or(String::from("<<Path Parsing Error>>"))
|
||||||
|
}
|
||||||
|
|
||||||
|
let diary = Diary::open(rt.store(), &diaryname[..]);
|
||||||
|
debug!("Diary opened: {:?}", diary);
|
||||||
|
diary.entries()
|
||||||
|
.and_then(|es| {
|
||||||
|
debug!("Iterator for listing: {:?}", es);
|
||||||
|
|
||||||
|
let es = es
|
||||||
|
.filter_map(|entry| {
|
||||||
|
entry
|
||||||
|
.map_dbg(|e| format!("Filtering: {:?}", e))
|
||||||
|
.map_err_trace() // error tracing here
|
||||||
|
.ok() // so we can ignore errors here
|
||||||
|
})
|
||||||
|
.map(|e| e.into());
|
||||||
|
|
||||||
|
CoreLister::new(&entry_to_location_listing_string)
|
||||||
|
.list(es)
|
||||||
|
.map_err_into(DEK::IOError)
|
||||||
|
})
|
||||||
|
.map_dbg_str("Ok")
|
||||||
|
.map_err_trace()
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
101
imag-diary/src/main.rs
Normal file
101
imag-diary/src/main.rs
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#![deny(
|
||||||
|
non_camel_case_types,
|
||||||
|
non_snake_case,
|
||||||
|
path_statements,
|
||||||
|
trivial_numeric_casts,
|
||||||
|
unstable_features,
|
||||||
|
unused_allocation,
|
||||||
|
unused_import_braces,
|
||||||
|
unused_imports,
|
||||||
|
unused_must_use,
|
||||||
|
unused_mut,
|
||||||
|
unused_qualifications,
|
||||||
|
while_true,
|
||||||
|
)]
|
||||||
|
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
#[macro_use] extern crate version;
|
||||||
|
extern crate clap;
|
||||||
|
extern crate chrono;
|
||||||
|
|
||||||
|
extern crate libimagdiary;
|
||||||
|
extern crate libimagentryedit;
|
||||||
|
extern crate libimagentrylist;
|
||||||
|
extern crate libimaginteraction;
|
||||||
|
extern crate libimagrt;
|
||||||
|
extern crate libimagstore;
|
||||||
|
extern crate libimagutil;
|
||||||
|
extern crate libimagtimeui;
|
||||||
|
#[macro_use] extern crate libimagerror;
|
||||||
|
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
use libimagrt::runtime::Runtime;
|
||||||
|
|
||||||
|
mod create;
|
||||||
|
mod delete;
|
||||||
|
mod edit;
|
||||||
|
mod list;
|
||||||
|
mod ui;
|
||||||
|
mod util;
|
||||||
|
mod view;
|
||||||
|
|
||||||
|
use create::create;
|
||||||
|
use delete::delete;
|
||||||
|
use edit::edit;
|
||||||
|
use list::list;
|
||||||
|
use ui::build_ui;
|
||||||
|
use view::view;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let name = "imag-diary";
|
||||||
|
let version = &version!()[..];
|
||||||
|
let about = "Personal Diary/Diaries";
|
||||||
|
let ui = build_ui(Runtime::get_default_cli_builder(name, version, about));
|
||||||
|
let rt = {
|
||||||
|
let rt = Runtime::new(ui);
|
||||||
|
if rt.is_ok() {
|
||||||
|
rt.unwrap()
|
||||||
|
} else {
|
||||||
|
println!("Could not set up Runtime");
|
||||||
|
println!("{:?}", rt.err().unwrap());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
rt.cli()
|
||||||
|
.subcommand_name()
|
||||||
|
.map(|name| {
|
||||||
|
debug!("Call {}", name);
|
||||||
|
match name {
|
||||||
|
"create" => create(&rt),
|
||||||
|
"delete" => delete(&rt),
|
||||||
|
"edit" => edit(&rt),
|
||||||
|
"list" => list(&rt),
|
||||||
|
"view" => view(&rt),
|
||||||
|
_ => {
|
||||||
|
debug!("Unknown command"); // More error handling
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
126
imag-diary/src/ui.rs
Normal file
126
imag-diary/src/ui.rs
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 clap::{Arg, ArgGroup, App, SubCommand};
|
||||||
|
|
||||||
|
pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
|
||||||
|
app
|
||||||
|
.arg(Arg::with_name("diaryname")
|
||||||
|
.long("diary")
|
||||||
|
.short("d")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.help("Use other than default diary"))
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("create")
|
||||||
|
.about("Create a diary entry")
|
||||||
|
.version("0.1")
|
||||||
|
.arg(Arg::with_name("no-edit")
|
||||||
|
.long("no-edit")
|
||||||
|
.short("e")
|
||||||
|
.takes_value(false)
|
||||||
|
.required(false)
|
||||||
|
.help("Do not edit after creating"))
|
||||||
|
|
||||||
|
.arg(Arg::with_name("timed")
|
||||||
|
.long("timed")
|
||||||
|
.short("t")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.help("By default, one entry is created per day. With --timed=h[ourly] or
|
||||||
|
--timed=m[inutely] one can create per-hour and per-minute entries (more like
|
||||||
|
a microblog then"))
|
||||||
|
|
||||||
|
.arg(Arg::with_name("hour")
|
||||||
|
.long("hour")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.help("When using --timed, override the hour component"))
|
||||||
|
.arg(Arg::with_name("minute")
|
||||||
|
.long("minute")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.help("When using --timed, override the minute component"))
|
||||||
|
|
||||||
|
// When using --hour or --minute, --timed must be present
|
||||||
|
.group(ArgGroup::with_name("timing-hourly")
|
||||||
|
.args(&["hour"])
|
||||||
|
.requires("timed"))
|
||||||
|
.group(ArgGroup::with_name("timing-minutely")
|
||||||
|
.args(&["minute"])
|
||||||
|
.requires("timed"))
|
||||||
|
)
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("edit")
|
||||||
|
.about("Edit a diary entry")
|
||||||
|
.version("0.1")
|
||||||
|
.arg(Arg::with_name("datetime")
|
||||||
|
.long("datetime")
|
||||||
|
.short("d")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.help("Specify the date and time which entry should be edited. If none is
|
||||||
|
specified, the last entry is edited. If the diary entry does not exist for
|
||||||
|
this time, this fails. Format: YYYY-MM-DDT[HH[:mm[:ss]]]"))
|
||||||
|
)
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("list")
|
||||||
|
.about("List diary entries")
|
||||||
|
.version("0.1"))
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("delete")
|
||||||
|
.about("Delete a diary entry")
|
||||||
|
.version("0.1")
|
||||||
|
.arg(Arg::with_name("datetime")
|
||||||
|
.long("datetime")
|
||||||
|
.short("d")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false)
|
||||||
|
.help("Specify the date and time which entry should be deleted. If none is
|
||||||
|
specified, the last entry is deleted. If the diary entry does not exist for
|
||||||
|
this time, this fails. Format: YYYY-MM-DDT[HH[:mm[:ss]]]"))
|
||||||
|
|
||||||
|
.arg(Arg::with_name("select")
|
||||||
|
.long("select")
|
||||||
|
.short("s")
|
||||||
|
.takes_value(false)
|
||||||
|
.required(false)
|
||||||
|
.help("Use interactive selection"))
|
||||||
|
|
||||||
|
.arg(Arg::with_name("yes")
|
||||||
|
.long("yes")
|
||||||
|
.short("y")
|
||||||
|
.takes_value(false)
|
||||||
|
.required(false)
|
||||||
|
.help("Do not ask for confirmation."))
|
||||||
|
)
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("view")
|
||||||
|
.about("View entries, currently only supports plain viewing")
|
||||||
|
.version("0.1")
|
||||||
|
|
||||||
|
.arg(Arg::with_name("show-header")
|
||||||
|
.long("header")
|
||||||
|
.takes_value(false)
|
||||||
|
.required(false)
|
||||||
|
.help("Show the header when printing the entries"))
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
28
imag-diary/src/util.rs
Normal file
28
imag-diary/src/util.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 libimagrt::runtime::Runtime;
|
||||||
|
|
||||||
|
pub fn get_diary_name(rt: &Runtime) -> Option<String> {
|
||||||
|
use libimagdiary::config::get_default_diary_name;
|
||||||
|
|
||||||
|
get_default_diary_name(rt)
|
||||||
|
.or(rt.cli().value_of("diaryname").map(String::from))
|
||||||
|
}
|
||||||
|
|
38
imag-diary/src/view.rs
Normal file
38
imag-diary/src/view.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 libimagdiary::diary::Diary;
|
||||||
|
use libimagdiary::viewer::DiaryViewer as DV;
|
||||||
|
use libimagrt::runtime::Runtime;
|
||||||
|
use libimagerror::trace::MapErrTrace;
|
||||||
|
use libimagutil::warn_exit::warn_exit;
|
||||||
|
|
||||||
|
use util::get_diary_name;
|
||||||
|
|
||||||
|
pub fn view(rt: &Runtime) {
|
||||||
|
let diaryname = get_diary_name(rt).unwrap_or_else(|| warn_exit("No diary name", 1));
|
||||||
|
let diary = Diary::open(rt.store(), &diaryname[..]);
|
||||||
|
let hdr = rt.cli().subcommand_matches("view").unwrap().is_present("show-header");
|
||||||
|
|
||||||
|
diary.entries()
|
||||||
|
.and_then(|entries| DV::new(hdr).view_entries(entries.into_iter().filter_map(Result::ok)))
|
||||||
|
.map_err_trace()
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
28
imag-mail/Cargo.toml
Normal file
28
imag-mail/Cargo.toml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
[package]
|
||||||
|
name = "imag-mail"
|
||||||
|
version = "0.2.0"
|
||||||
|
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
semver = "0.5"
|
||||||
|
clap = "2.*"
|
||||||
|
log = "0.3"
|
||||||
|
version = "2.0.1"
|
||||||
|
toml = "0.2.*"
|
||||||
|
url = "1.2"
|
||||||
|
|
||||||
|
[dependencies.libimagrt]
|
||||||
|
path = "../libimagrt"
|
||||||
|
|
||||||
|
[dependencies.libimagmail]
|
||||||
|
path = "../libimagmail"
|
||||||
|
|
||||||
|
[dependencies.libimagerror]
|
||||||
|
path = "../libimagerror"
|
||||||
|
|
||||||
|
[dependencies.libimagutil]
|
||||||
|
path = "../libimagutil"
|
||||||
|
|
||||||
|
[dependencies.libimagref]
|
||||||
|
path = "../libimagref"
|
||||||
|
|
1
imag-mail/README.md
Symbolic link
1
imag-mail/README.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../doc/src/04020-module-mails.md
|
151
imag-mail/src/main.rs
Normal file
151
imag-mail/src/main.rs
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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
|
||||||
|
//
|
||||||
|
|
||||||
|
extern crate semver;
|
||||||
|
extern crate clap;
|
||||||
|
extern crate toml;
|
||||||
|
extern crate url;
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
#[macro_use] extern crate version;
|
||||||
|
|
||||||
|
extern crate libimagrt;
|
||||||
|
extern crate libimagmail;
|
||||||
|
extern crate libimagerror;
|
||||||
|
extern crate libimagutil;
|
||||||
|
extern crate libimagref;
|
||||||
|
|
||||||
|
use libimagerror::trace::{MapErrTrace, trace_error, trace_error_exit};
|
||||||
|
use libimagmail::mail::Mail;
|
||||||
|
use libimagref::reference::Ref;
|
||||||
|
use libimagrt::runtime::Runtime;
|
||||||
|
use libimagrt::setup::generate_runtime_setup;
|
||||||
|
use libimagutil::debug_result::*;
|
||||||
|
use libimagutil::info_result::*;
|
||||||
|
|
||||||
|
mod ui;
|
||||||
|
|
||||||
|
use ui::build_ui;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let rt = generate_runtime_setup("imag-mail",
|
||||||
|
&version!()[..],
|
||||||
|
"Mail collection tool",
|
||||||
|
build_ui);
|
||||||
|
|
||||||
|
rt.cli()
|
||||||
|
.subcommand_name()
|
||||||
|
.map(|name| {
|
||||||
|
debug!("Call {}", name);
|
||||||
|
match name {
|
||||||
|
"import-mail" => import_mail(&rt),
|
||||||
|
"list" => list(&rt),
|
||||||
|
"mail-store" => mail_store(&rt),
|
||||||
|
_ => debug!("Unknown command") // More error handling
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import_mail(rt: &Runtime) {
|
||||||
|
let scmd = rt.cli().subcommand_matches("import-mail").unwrap();
|
||||||
|
let path = scmd.value_of("path").unwrap(); // enforced by clap
|
||||||
|
|
||||||
|
Mail::import_from_path(rt.store(), path)
|
||||||
|
.map_err_trace()
|
||||||
|
.map_info_str("Ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list(rt: &Runtime) {
|
||||||
|
use libimagmail::error::MailErrorKind as MEK;
|
||||||
|
use libimagmail::error::MapErrInto;
|
||||||
|
|
||||||
|
let scmd = rt.cli().subcommand_matches("list").unwrap();
|
||||||
|
let do_check_dead = scmd.is_present("check-dead");
|
||||||
|
let do_check_changed = scmd.is_present("check-changed");
|
||||||
|
let do_check_changed_content = scmd.is_present("check-changed-content");
|
||||||
|
let do_check_changed_permiss = scmd.is_present("check-changed-permissions");
|
||||||
|
let store = rt.store();
|
||||||
|
|
||||||
|
let iter = match store.retrieve_for_module("ref") {
|
||||||
|
Ok(iter) => iter.filter_map(|id| {
|
||||||
|
Ref::get(store, id)
|
||||||
|
.map_err_into(MEK::RefHandlingError)
|
||||||
|
.and_then(|rf| Mail::from_ref(rf))
|
||||||
|
.map_err_trace()
|
||||||
|
.ok()
|
||||||
|
}),
|
||||||
|
Err(e) => trace_error_exit(&e, 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
fn list_mail(m: Mail) {
|
||||||
|
let id = match m.get_message_id() {
|
||||||
|
Ok(Some(f)) => f,
|
||||||
|
Ok(None) => "<no id>".to_owned(),
|
||||||
|
Err(e) => {
|
||||||
|
trace_error(&e);
|
||||||
|
"<error>".to_owned()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let from = match m.get_from() {
|
||||||
|
Ok(Some(f)) => f,
|
||||||
|
Ok(None) => "<no from>".to_owned(),
|
||||||
|
Err(e) => {
|
||||||
|
trace_error(&e);
|
||||||
|
"<error>".to_owned()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let to = match m.get_to() {
|
||||||
|
Ok(Some(f)) => f,
|
||||||
|
Ok(None) => "<no to>".to_owned(),
|
||||||
|
Err(e) => {
|
||||||
|
trace_error(&e);
|
||||||
|
"<error>".to_owned()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let subject = match m.get_subject() {
|
||||||
|
Ok(Some(f)) => f,
|
||||||
|
Ok(None) => "<no subject>".to_owned(),
|
||||||
|
Err(e) => {
|
||||||
|
trace_error(&e);
|
||||||
|
"<error>".to_owned()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Mail: {id}\n\tFrom: {from}\n\tTo: {to}\n\t{subj}\n",
|
||||||
|
from = from,
|
||||||
|
id = id,
|
||||||
|
subj = subject,
|
||||||
|
to = to
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement lister type in libimagmail for this
|
||||||
|
for mail in iter {
|
||||||
|
list_mail(mail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mail_store(rt: &Runtime) {
|
||||||
|
let scmd = rt.cli().subcommand_matches("mail-store").unwrap();
|
||||||
|
error!("This feature is currently not implemented.");
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
74
imag-mail/src/ui.rs
Normal file
74
imag-mail/src/ui.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 clap::{Arg, ArgGroup, App, SubCommand};
|
||||||
|
|
||||||
|
pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
|
||||||
|
app
|
||||||
|
.subcommand(SubCommand::with_name("import-mail")
|
||||||
|
.about("Import a mail (create a reference to it) (Maildir)")
|
||||||
|
.version("0.1")
|
||||||
|
.arg(Arg::with_name("path")
|
||||||
|
.long("path")
|
||||||
|
.short("p")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("Path to the mail file or a directory which is then searched recursively")
|
||||||
|
.value_name("PATH"))
|
||||||
|
)
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("list")
|
||||||
|
.about("List all stored references to mails")
|
||||||
|
.version("0.1")
|
||||||
|
|
||||||
|
// TODO: Thee following four arguments are the same as in imag-ref.
|
||||||
|
// We should make these importable from libimagref.
|
||||||
|
|
||||||
|
.arg(Arg::with_name("check-dead")
|
||||||
|
.long("check-dead")
|
||||||
|
.short("d")
|
||||||
|
.help("Check each reference whether it is dead"))
|
||||||
|
|
||||||
|
.arg(Arg::with_name("check-changed")
|
||||||
|
.long("check-changed")
|
||||||
|
.short("c")
|
||||||
|
.help("Check whether a reference had changed (content or permissions)"))
|
||||||
|
|
||||||
|
.arg(Arg::with_name("check-changed-content")
|
||||||
|
.long("check-changed-content")
|
||||||
|
.short("C")
|
||||||
|
.help("Check whether the content of the referenced file changed"))
|
||||||
|
|
||||||
|
.arg(Arg::with_name("check-changed-permissions")
|
||||||
|
.long("check-changed-perms")
|
||||||
|
.short("P")
|
||||||
|
.help("Check whether the permissions of the referenced file changed"))
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("mail-store")
|
||||||
|
.about("Operations on (subsets of) all mails")
|
||||||
|
.version("0.1")
|
||||||
|
.subcommand(SubCommand::with_name("update-refs")
|
||||||
|
.about("Create references based on Message-IDs for all loaded mails")
|
||||||
|
.version("0.1"))
|
||||||
|
// TODO: We really should be able to filter here.
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
36
imag-todo/Cargo.toml
Normal file
36
imag-todo/Cargo.toml
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
[package]
|
||||||
|
authors = ["mario <mario-krehl@gmx.de>"]
|
||||||
|
name = "imag-todo"
|
||||||
|
version = "0.2.0"
|
||||||
|
|
||||||
|
description = "Part of the imag core distribution: imag-todo command"
|
||||||
|
|
||||||
|
keywords = ["imag", "PIM", "personal", "information", "management"]
|
||||||
|
readme = "../README.md"
|
||||||
|
license = "LGPL-2.1"
|
||||||
|
|
||||||
|
documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html"
|
||||||
|
repository = "https://github.com/matthiasbeyer/imag"
|
||||||
|
homepage = "http://imag-pim.org"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = ">=2.17"
|
||||||
|
glob = "0.2.11"
|
||||||
|
log = "0.3.6"
|
||||||
|
semver = "0.5.1"
|
||||||
|
serde_json = "0.8.3"
|
||||||
|
task-hookrs = "0.2.2"
|
||||||
|
toml = "0.2.*"
|
||||||
|
version = "2.0.1"
|
||||||
|
|
||||||
|
[dependencies.libimagrt]
|
||||||
|
path = "../libimagrt"
|
||||||
|
|
||||||
|
[dependencies.libimagstore]
|
||||||
|
path = "../libimagstore"
|
||||||
|
|
||||||
|
[dependencies.libimagtodo]
|
||||||
|
path = "../libimagtodo"
|
||||||
|
|
||||||
|
[dependencies.libimagerror]
|
||||||
|
path = "../libimagerror"
|
4
imag-todo/etc/on-add.sh
Normal file
4
imag-todo/etc/on-add.sh
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#/!usr/bin/env bash
|
||||||
|
|
||||||
|
imag todo tw-hook --add
|
||||||
|
|
4
imag-todo/etc/on-modify.sh
Normal file
4
imag-todo/etc/on-modify.sh
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#/!usr/bin/env bash
|
||||||
|
|
||||||
|
imag todo tw-hook --delete
|
||||||
|
|
138
imag-todo/src/main.rs
Normal file
138
imag-todo/src/main.rs
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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
|
||||||
|
//
|
||||||
|
|
||||||
|
extern crate clap;
|
||||||
|
extern crate glob;
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
extern crate serde_json;
|
||||||
|
extern crate semver;
|
||||||
|
extern crate toml;
|
||||||
|
#[macro_use] extern crate version;
|
||||||
|
|
||||||
|
extern crate task_hookrs;
|
||||||
|
|
||||||
|
extern crate libimagrt;
|
||||||
|
extern crate libimagstore;
|
||||||
|
extern crate libimagerror;
|
||||||
|
extern crate libimagtodo;
|
||||||
|
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
use std::io::stdin;
|
||||||
|
|
||||||
|
use toml::Value;
|
||||||
|
|
||||||
|
use libimagrt::runtime::Runtime;
|
||||||
|
use libimagrt::setup::generate_runtime_setup;
|
||||||
|
use libimagtodo::task::Task;
|
||||||
|
use libimagerror::trace::{MapErrTrace, trace_error, trace_error_exit};
|
||||||
|
|
||||||
|
mod ui;
|
||||||
|
|
||||||
|
use ui::build_ui;
|
||||||
|
fn main() {
|
||||||
|
let rt = generate_runtime_setup("imag-todo",
|
||||||
|
&version!()[..],
|
||||||
|
"Interface with taskwarrior",
|
||||||
|
build_ui);
|
||||||
|
|
||||||
|
match rt.cli().subcommand_name() {
|
||||||
|
Some("tw-hook") => tw_hook(&rt),
|
||||||
|
Some("list") => list(&rt),
|
||||||
|
None => {
|
||||||
|
warn!("No command");
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
} // end match scmd
|
||||||
|
} // end main
|
||||||
|
|
||||||
|
fn tw_hook(rt: &Runtime) {
|
||||||
|
let subcmd = rt.cli().subcommand_matches("tw-hook").unwrap();
|
||||||
|
if subcmd.is_present("add") {
|
||||||
|
let stdin = stdin();
|
||||||
|
let stdin = stdin.lock(); // implements BufRead which is required for `Task::import()`
|
||||||
|
|
||||||
|
match Task::import(rt.store(), stdin) {
|
||||||
|
Ok((_, line, uuid)) => println!("{}\nTask {} stored in imag", line, uuid),
|
||||||
|
Err(e) => trace_error_exit(&e, 1),
|
||||||
|
}
|
||||||
|
} else if subcmd.is_present("delete") {
|
||||||
|
// The used hook is "on-modify". This hook gives two json-objects
|
||||||
|
// per usage und wants one (the second one) back.
|
||||||
|
let stdin = stdin();
|
||||||
|
Task::delete_by_imports(rt.store(), stdin.lock()).map_err_trace().ok();
|
||||||
|
} else {
|
||||||
|
// Should not be possible, as one argument is required via
|
||||||
|
// ArgGroup
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list(rt: &Runtime) {
|
||||||
|
let subcmd = rt.cli().subcommand_matches("list").unwrap();
|
||||||
|
let verbose = subcmd.is_present("verbose");
|
||||||
|
|
||||||
|
let res = Task::all(rt.store()) // get all tasks
|
||||||
|
.map(|iter| { // and if this succeeded
|
||||||
|
// filter out the ones were we can read the uuid
|
||||||
|
let uuids : Vec<_> = iter.filter_map(|t| match t {
|
||||||
|
Ok(v) => match v.get_header().read("todo.uuid") {
|
||||||
|
Ok(Some(Value::String(ref u))) => Some(u.clone()),
|
||||||
|
Ok(Some(_)) => {
|
||||||
|
warn!("Header type error");
|
||||||
|
None
|
||||||
|
},
|
||||||
|
Ok(None) => None,
|
||||||
|
Err(e) => {
|
||||||
|
trace_error(&e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
trace_error(&e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// compose a `task` call with them, ...
|
||||||
|
let outstring = if verbose { // ... if verbose
|
||||||
|
let output = Command::new("task")
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.args(&uuids)
|
||||||
|
.spawn()
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
trace_error(&e);
|
||||||
|
panic!("Failed to execute `task` on the commandline. I'm dying now.");
|
||||||
|
})
|
||||||
|
.wait_with_output()
|
||||||
|
.unwrap_or_else(|e| panic!("failed to unwrap output: {}", e));
|
||||||
|
|
||||||
|
String::from_utf8(output.stdout)
|
||||||
|
.unwrap_or_else(|e| panic!("failed to execute: {}", e))
|
||||||
|
} else { // ... else just join them
|
||||||
|
uuids.join("\n")
|
||||||
|
};
|
||||||
|
|
||||||
|
// and then print that
|
||||||
|
println!("{}", outstring);
|
||||||
|
});
|
||||||
|
|
||||||
|
res.map_err_trace().ok();
|
||||||
|
}
|
||||||
|
|
61
imag-todo/src/ui.rs
Normal file
61
imag-todo/src/ui.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 clap::{Arg, App, ArgGroup, SubCommand};
|
||||||
|
|
||||||
|
pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
|
||||||
|
app
|
||||||
|
.subcommand(SubCommand::with_name("tw-hook")
|
||||||
|
.about("For use in a taskwarrior hook")
|
||||||
|
.version("0.1")
|
||||||
|
|
||||||
|
.arg(Arg::with_name("add")
|
||||||
|
.long("add")
|
||||||
|
.short("a")
|
||||||
|
.takes_value(false)
|
||||||
|
.required(false)
|
||||||
|
.help("For use in an on-add hook"))
|
||||||
|
|
||||||
|
.arg(Arg::with_name("delete")
|
||||||
|
.long("delete")
|
||||||
|
.short("d")
|
||||||
|
.takes_value(false)
|
||||||
|
.required(false)
|
||||||
|
.help("For use in an on-delete hook"))
|
||||||
|
|
||||||
|
.group(ArgGroup::with_name("taskwarrior hooks")
|
||||||
|
.args(&[ "add",
|
||||||
|
"delete",
|
||||||
|
])
|
||||||
|
.required(true))
|
||||||
|
)
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("list")
|
||||||
|
.about("List all tasks")
|
||||||
|
.version("0.1")
|
||||||
|
|
||||||
|
.arg(Arg::with_name("verbose")
|
||||||
|
.long("verbose")
|
||||||
|
.short("v")
|
||||||
|
.takes_value(false)
|
||||||
|
.required(false)
|
||||||
|
.help("Asks taskwarrior for all the details")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
30
libimagbookmark/Cargo.toml
Normal file
30
libimagbookmark/Cargo.toml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
[package]
|
||||||
|
name = "libimagbookmark"
|
||||||
|
version = "0.2.0"
|
||||||
|
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
||||||
|
|
||||||
|
description = "Library for the imag core distribution"
|
||||||
|
|
||||||
|
keywords = ["imag", "PIM", "personal", "information", "management"]
|
||||||
|
readme = "../README.md"
|
||||||
|
license = "LGPL-2.1"
|
||||||
|
|
||||||
|
documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html"
|
||||||
|
repository = "https://github.com/matthiasbeyer/imag"
|
||||||
|
homepage = "http://imag-pim.org"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.3"
|
||||||
|
semver = "0.5"
|
||||||
|
url = "1.2"
|
||||||
|
regex = "0.1"
|
||||||
|
|
||||||
|
[dependencies.libimagstore]
|
||||||
|
path = "../libimagstore"
|
||||||
|
|
||||||
|
[dependencies.libimagerror]
|
||||||
|
path = "../libimagerror"
|
||||||
|
|
||||||
|
[dependencies.libimagentrylink]
|
||||||
|
path = "../libimagentrylink"
|
||||||
|
|
1
libimagbookmark/README.md
Symbolic link
1
libimagbookmark/README.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../doc/src/05100-lib-bookmark.md
|
222
libimagbookmark/src/collection.rs
Normal file
222
libimagbookmark/src/collection.rs
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 libimagentrylink::external::ExternalLinker trait on this to generate external links.
|
||||||
|
//!
|
||||||
|
//! The BookmarkCollection type offers helper functions to get all links or such things.
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
use error::BookmarkErrorKind as BEK;
|
||||||
|
use error::MapErrInto;
|
||||||
|
use result::Result;
|
||||||
|
use module_path::ModuleEntryPath;
|
||||||
|
|
||||||
|
use libimagstore::store::Store;
|
||||||
|
use libimagstore::storeid::IntoStoreId;
|
||||||
|
use libimagstore::store::FileLockEntry;
|
||||||
|
use libimagentrylink::external::ExternalLinker;
|
||||||
|
use libimagentrylink::external::iter::UrlIter;
|
||||||
|
use libimagentrylink::internal::InternalLinker;
|
||||||
|
use libimagentrylink::internal::Link as StoreLink;
|
||||||
|
use libimagerror::into::IntoError;
|
||||||
|
|
||||||
|
use link::Link;
|
||||||
|
|
||||||
|
use self::iter::LinksMatchingRegexIter;
|
||||||
|
|
||||||
|
pub struct BookmarkCollection<'a> {
|
||||||
|
fle: FileLockEntry<'a>,
|
||||||
|
store: &'a Store,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// {Internal, External}Linker is implemented as Deref is implemented
|
||||||
|
impl<'a> Deref for BookmarkCollection<'a> {
|
||||||
|
type Target = FileLockEntry<'a>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &FileLockEntry<'a> {
|
||||||
|
&self.fle
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DerefMut for BookmarkCollection<'a> {
|
||||||
|
|
||||||
|
fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
|
||||||
|
&mut self.fle
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> BookmarkCollection<'a> {
|
||||||
|
|
||||||
|
pub fn new(store: &'a Store, name: &str) -> Result<BookmarkCollection<'a>> {
|
||||||
|
ModuleEntryPath::new(name)
|
||||||
|
.into_storeid()
|
||||||
|
.and_then(|id| store.create(id))
|
||||||
|
.map(|fle| {
|
||||||
|
BookmarkCollection {
|
||||||
|
fle: fle,
|
||||||
|
store: store,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err_into(BEK::StoreReadError)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(store: &'a Store, name: &str) -> Result<BookmarkCollection<'a>> {
|
||||||
|
ModuleEntryPath::new(name)
|
||||||
|
.into_storeid()
|
||||||
|
.and_then(|id| store.get(id))
|
||||||
|
.map_err_into(BEK::StoreReadError)
|
||||||
|
.and_then(|fle| {
|
||||||
|
match fle {
|
||||||
|
None => Err(BEK::CollectionNotFound.into_error()),
|
||||||
|
Some(e) => Ok(BookmarkCollection {
|
||||||
|
fle: e,
|
||||||
|
store: store,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete(store: &Store, name: &str) -> Result<()> {
|
||||||
|
ModuleEntryPath::new(name)
|
||||||
|
.into_storeid()
|
||||||
|
.and_then(|id| store.delete(id))
|
||||||
|
.map_err_into(BEK::StoreReadError)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn links(&self) -> Result<UrlIter> {
|
||||||
|
self.fle.get_external_links(&self.store).map_err_into(BEK::LinkError)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn link_entries(&self) -> Result<Vec<StoreLink>> {
|
||||||
|
use libimagentrylink::external::is_external_link_storeid;
|
||||||
|
|
||||||
|
self.fle
|
||||||
|
.get_internal_links()
|
||||||
|
.map(|v| v.filter(|id| is_external_link_storeid(id)).collect())
|
||||||
|
.map_err_into(BEK::StoreReadError)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_link(&mut self, l: Link) -> Result<()> {
|
||||||
|
use link::IntoUrl;
|
||||||
|
|
||||||
|
l.into_url()
|
||||||
|
.and_then(|url| self.add_external_link(self.store, url).map_err_into(BEK::LinkingError))
|
||||||
|
.map_err_into(BEK::LinkError)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_links_matching(&self, r: Regex) -> Result<LinksMatchingRegexIter<'a>> {
|
||||||
|
use self::iter::IntoLinksMatchingRegexIter;
|
||||||
|
|
||||||
|
self.get_external_links(self.store)
|
||||||
|
.map_err_into(BEK::LinkError)
|
||||||
|
.map(|iter| iter.matching_regex(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_link(&mut self, l: Link) -> Result<()> {
|
||||||
|
use link::IntoUrl;
|
||||||
|
|
||||||
|
l.into_url()
|
||||||
|
.and_then(|url| {
|
||||||
|
self.remove_external_link(self.store, url).map_err_into(BEK::LinkingError)
|
||||||
|
})
|
||||||
|
.map_err_into(BEK::LinkError)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod iter {
|
||||||
|
use link::Link;
|
||||||
|
use result::Result;
|
||||||
|
use error::{MapErrInto, BookmarkErrorKind as BEK};
|
||||||
|
|
||||||
|
pub struct LinkIter<I>(I)
|
||||||
|
where I: Iterator<Item = Link>;
|
||||||
|
|
||||||
|
impl<I: Iterator<Item = Link>> LinkIter<I> {
|
||||||
|
pub fn new(i: I) -> LinkIter<I> {
|
||||||
|
LinkIter(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Iterator<Item = Link>> Iterator for LinkIter<I> {
|
||||||
|
type Item = Link;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.0.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> From<I> for LinkIter<I> where I: Iterator<Item = Link> {
|
||||||
|
fn from(i: I) -> LinkIter<I> {
|
||||||
|
LinkIter(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use libimagentrylink::external::iter::UrlIter;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
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<Link>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
loop {
|
||||||
|
let n = match self.0.next() {
|
||||||
|
Some(Ok(n)) => n,
|
||||||
|
Some(Err(e)) => return Some(Err(e).map_err_into(BEK::LinkError)),
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
33
libimagbookmark/src/error.rs
Normal file
33
libimagbookmark/src/error.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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
|
||||||
|
//
|
||||||
|
|
||||||
|
generate_error_module!(
|
||||||
|
generate_error_types!(BookmarkError, BookmarkErrorKind,
|
||||||
|
StoreReadError => "Store read error",
|
||||||
|
LinkError => "Link error",
|
||||||
|
LinkParsingError => "Link parsing error",
|
||||||
|
LinkingError => "Error while linking",
|
||||||
|
CollectionNotFound => "Link-Collection not found"
|
||||||
|
);
|
||||||
|
);
|
||||||
|
|
||||||
|
pub use self::error::BookmarkError;
|
||||||
|
pub use self::error::BookmarkErrorKind;
|
||||||
|
pub use self::error::MapErrInto;
|
||||||
|
|
48
libimagbookmark/src/lib.rs
Normal file
48
libimagbookmark/src/lib.rs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#![deny(
|
||||||
|
non_camel_case_types,
|
||||||
|
non_snake_case,
|
||||||
|
path_statements,
|
||||||
|
trivial_numeric_casts,
|
||||||
|
unstable_features,
|
||||||
|
unused_allocation,
|
||||||
|
unused_import_braces,
|
||||||
|
unused_imports,
|
||||||
|
unused_mut,
|
||||||
|
unused_qualifications,
|
||||||
|
while_true,
|
||||||
|
)]
|
||||||
|
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
extern crate semver;
|
||||||
|
extern crate url;
|
||||||
|
extern crate regex;
|
||||||
|
|
||||||
|
#[macro_use] extern crate libimagstore;
|
||||||
|
#[macro_use] extern crate libimagerror;
|
||||||
|
extern crate libimagentrylink;
|
||||||
|
|
||||||
|
module_entry_path_mod!("bookmark");
|
||||||
|
|
||||||
|
pub mod collection;
|
||||||
|
pub mod error;
|
||||||
|
pub mod link;
|
||||||
|
pub mod result;
|
76
libimagbookmark/src/link.rs
Normal file
76
libimagbookmark/src/link.rs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 result::Result;
|
||||||
|
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Link(String);
|
||||||
|
|
||||||
|
impl From<String> 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<Url>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoUrl for Link {
|
||||||
|
|
||||||
|
fn into_url(self) -> Result<Url> {
|
||||||
|
use error::BookmarkErrorKind as BEK;
|
||||||
|
use error::MapErrInto;
|
||||||
|
|
||||||
|
Url::parse(&self[..]).map_err_into(BEK::LinkParsingError)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
25
libimagbookmark/src/result.rs
Normal file
25
libimagbookmark/src/result.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::result::Result as RResult;
|
||||||
|
|
||||||
|
use error::BookmarkError;
|
||||||
|
|
||||||
|
pub type Result<T> = RResult<T, BookmarkError>;
|
||||||
|
|
26
libimagcounter/Cargo.toml
Normal file
26
libimagcounter/Cargo.toml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
[package]
|
||||||
|
name = "libimagcounter"
|
||||||
|
version = "0.2.0"
|
||||||
|
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
||||||
|
|
||||||
|
description = "Library for the imag core distribution"
|
||||||
|
|
||||||
|
keywords = ["imag", "PIM", "personal", "information", "management"]
|
||||||
|
readme = "../README.md"
|
||||||
|
license = "LGPL-2.1"
|
||||||
|
|
||||||
|
documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html"
|
||||||
|
repository = "https://github.com/matthiasbeyer/imag"
|
||||||
|
homepage = "http://imag-pim.org"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.3"
|
||||||
|
toml = "0.2.*"
|
||||||
|
semver = "0.5"
|
||||||
|
|
||||||
|
[dependencies.libimagstore]
|
||||||
|
path = "../libimagstore"
|
||||||
|
|
||||||
|
[dependencies.libimagerror]
|
||||||
|
path = "../libimagerror"
|
||||||
|
|
1
libimagcounter/README.md
Symbolic link
1
libimagcounter/README.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../doc/src/05100-lib-counter.md
|
257
libimagcounter/src/counter.rs
Normal file
257
libimagcounter/src/counter.rs
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::DerefMut;
|
||||||
|
|
||||||
|
use toml::Value;
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use libimagstore::store::Store;
|
||||||
|
use libimagstore::storeid::StoreIdIterator;
|
||||||
|
use libimagstore::store::FileLockEntry;
|
||||||
|
use libimagstore::storeid::StoreId;
|
||||||
|
use libimagstore::storeid::IntoStoreId;
|
||||||
|
use libimagerror::into::IntoError;
|
||||||
|
|
||||||
|
use module_path::ModuleEntryPath;
|
||||||
|
use result::Result;
|
||||||
|
use error::CounterError as CE;
|
||||||
|
use error::CounterErrorKind as CEK;
|
||||||
|
use error::error::MapErrInto;
|
||||||
|
|
||||||
|
pub type CounterName = String;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
|
pub struct CounterUnit(String);
|
||||||
|
|
||||||
|
impl Display for CounterUnit {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "({})", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CounterUnit {
|
||||||
|
pub fn new<S: Into<String>>(unit: S) -> CounterUnit {
|
||||||
|
CounterUnit(unit.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Counter<'a> {
|
||||||
|
fle: FileLockEntry<'a>,
|
||||||
|
unit: Option<CounterUnit>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Counter<'a> {
|
||||||
|
|
||||||
|
pub fn new(store: &Store, name: CounterName, init: i64) -> Result<Counter> {
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
|
debug!("Creating new counter: '{}' with value: {}", name, init);
|
||||||
|
let fle = {
|
||||||
|
let id = try!(ModuleEntryPath::new(name.clone())
|
||||||
|
.into_storeid()
|
||||||
|
.map_err_into(CEK::StoreWriteError));
|
||||||
|
let mut lockentry = try!(store.create(id).map_err_into(CEK::StoreWriteError));
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut entry = lockentry.deref_mut();
|
||||||
|
let mut header = entry.get_header_mut();
|
||||||
|
let setres = header.set("counter", Value::Table(BTreeMap::new()));
|
||||||
|
if setres.is_err() {
|
||||||
|
return Err(CEK::StoreWriteError.into_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
let setres = header.set("counter.name", Value::String(name));
|
||||||
|
if setres.is_err() {
|
||||||
|
return Err(CEK::StoreWriteError.into_error())
|
||||||
|
}
|
||||||
|
|
||||||
|
let setres = header.set("counter.value", Value::Integer(init));
|
||||||
|
if setres.is_err() {
|
||||||
|
return Err(CEK::StoreWriteError.into_error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lockentry
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Counter { fle: fle, unit: None })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_unit(mut self, unit: Option<CounterUnit>) -> Result<Counter<'a>> {
|
||||||
|
self.unit = unit;
|
||||||
|
|
||||||
|
if let Some(u) = self.unit.clone() {
|
||||||
|
let mut header = self.fle.deref_mut().get_header_mut();
|
||||||
|
let setres = header.set("counter.unit", Value::String(u.0));
|
||||||
|
if setres.is_err() {
|
||||||
|
self.unit = None;
|
||||||
|
return Err(CEK::StoreWriteError.into_error())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inc(&mut self) -> Result<()> {
|
||||||
|
let mut header = self.fle.deref_mut().get_header_mut();
|
||||||
|
match header.read("counter.value") {
|
||||||
|
Ok(Some(Value::Integer(i))) => {
|
||||||
|
header.set("counter.value", Value::Integer(i + 1))
|
||||||
|
.map_err_into(CEK::StoreWriteError)
|
||||||
|
.map(|_| ())
|
||||||
|
},
|
||||||
|
Err(e) => Err(CE::new(CEK::StoreReadError, Some(Box::new(e)))),
|
||||||
|
_ => Err(CE::new(CEK::StoreReadError, None)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dec(&mut self) -> Result<()> {
|
||||||
|
let mut header = self.fle.deref_mut().get_header_mut();
|
||||||
|
match header.read("counter.value") {
|
||||||
|
Ok(Some(Value::Integer(i))) => {
|
||||||
|
header.set("counter.value", Value::Integer(i - 1))
|
||||||
|
.map_err_into(CEK::StoreWriteError)
|
||||||
|
.map(|_| ())
|
||||||
|
},
|
||||||
|
Err(e) => Err(CE::new(CEK::StoreReadError, Some(Box::new(e)))),
|
||||||
|
_ => Err(CE::new(CEK::StoreReadError, None)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self) -> Result<()> {
|
||||||
|
self.set(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, v: i64) -> Result<()> {
|
||||||
|
let mut header = self.fle.deref_mut().get_header_mut();
|
||||||
|
header.set("counter.value", Value::Integer(v))
|
||||||
|
.map_err_into(CEK::StoreWriteError)
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> Result<CounterName> {
|
||||||
|
self.read_header_at("counter.name", |v| match v {
|
||||||
|
Some(Value::String(s)) => Ok(s),
|
||||||
|
_ => Err(CEK::HeaderTypeError.into_error()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value(&self) -> Result<i64> {
|
||||||
|
self.read_header_at("counter.value", |v| match v {
|
||||||
|
Some(Value::Integer(i)) => Ok(i),
|
||||||
|
_ => Err(CEK::HeaderTypeError.into_error()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unit(&self) -> Option<&CounterUnit> {
|
||||||
|
self.unit.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_unit(&self) -> Result<Option<CounterUnit>> {
|
||||||
|
self.read_header_at("counter.unit", |s| match s {
|
||||||
|
Some(Value::String(s)) => Ok(Some(CounterUnit::new(s))),
|
||||||
|
Some(_) => Err(CEK::HeaderTypeError.into_error()),
|
||||||
|
None => Ok(None),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_header_at<T, F>(&self, name: &str, f: F) -> Result<T>
|
||||||
|
where F: FnOnce(Option<Value>) -> Result<T>
|
||||||
|
{
|
||||||
|
self.fle.get_header().read(name).map_err_into(CEK::StoreWriteError).and_then(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(name: CounterName, store: &Store) -> Result<Counter> {
|
||||||
|
debug!("Loading counter: '{}'", name);
|
||||||
|
let id = try!(ModuleEntryPath::new(name)
|
||||||
|
.into_storeid()
|
||||||
|
.map_err_into(CEK::StoreWriteError));
|
||||||
|
Counter::from_storeid(store, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete(name: CounterName, store: &Store) -> Result<()> {
|
||||||
|
debug!("Deleting counter: '{}'", name);
|
||||||
|
let id = try!(ModuleEntryPath::new(name)
|
||||||
|
.into_storeid()
|
||||||
|
.map_err_into(CEK::StoreWriteError));
|
||||||
|
store.delete(id).map_err_into(CEK::StoreWriteError)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_counters(store: &Store) -> Result<CounterIterator> {
|
||||||
|
store.retrieve_for_module("counter")
|
||||||
|
.map(|iter| CounterIterator::new(store, iter))
|
||||||
|
.map_err_into(CEK::StoreReadError)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
trait FromStoreId {
|
||||||
|
fn from_storeid(&Store, StoreId) -> Result<Counter>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FromStoreId for Counter<'a> {
|
||||||
|
|
||||||
|
fn from_storeid(store: &Store, id: StoreId) -> Result<Counter> {
|
||||||
|
debug!("Loading counter from storeid: '{:?}'", id);
|
||||||
|
match store.retrieve(id) {
|
||||||
|
Err(e) => Err(CE::new(CEK::StoreReadError, Some(Box::new(e)))),
|
||||||
|
Ok(c) => {
|
||||||
|
let mut counter = Counter { fle: c, unit: None };
|
||||||
|
counter.read_unit()
|
||||||
|
.map_err_into(CEK::StoreReadError)
|
||||||
|
.and_then(|u| {
|
||||||
|
counter.unit = u;
|
||||||
|
Ok(counter)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CounterIterator<'a> {
|
||||||
|
store: &'a Store,
|
||||||
|
iditer: StoreIdIterator,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CounterIterator<'a> {
|
||||||
|
|
||||||
|
pub fn new(store: &'a Store, iditer: StoreIdIterator) -> CounterIterator<'a> {
|
||||||
|
CounterIterator {
|
||||||
|
store: store,
|
||||||
|
iditer: iditer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for CounterIterator<'a> {
|
||||||
|
type Item = Result<Counter<'a>>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Result<Counter<'a>>> {
|
||||||
|
self.iditer
|
||||||
|
.next()
|
||||||
|
.map(|id| Counter::from_storeid(self.store, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
32
libimagcounter/src/error.rs
Normal file
32
libimagcounter/src/error.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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
|
||||||
|
//
|
||||||
|
|
||||||
|
generate_error_module!(
|
||||||
|
generate_error_types!(CounterError, CounterErrorKind,
|
||||||
|
StoreIdError => "StoreId error",
|
||||||
|
StoreReadError => "Store read error",
|
||||||
|
StoreWriteError => "Store write error",
|
||||||
|
HeaderTypeError => "Header type error",
|
||||||
|
HeaderFieldMissingError => "Header field missing error"
|
||||||
|
);
|
||||||
|
);
|
||||||
|
|
||||||
|
pub use self::error::CounterError;
|
||||||
|
pub use self::error::CounterErrorKind;
|
||||||
|
|
46
libimagcounter/src/lib.rs
Normal file
46
libimagcounter/src/lib.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#![deny(
|
||||||
|
non_camel_case_types,
|
||||||
|
non_snake_case,
|
||||||
|
path_statements,
|
||||||
|
trivial_numeric_casts,
|
||||||
|
unstable_features,
|
||||||
|
unused_allocation,
|
||||||
|
unused_import_braces,
|
||||||
|
unused_imports,
|
||||||
|
unused_mut,
|
||||||
|
unused_qualifications,
|
||||||
|
while_true,
|
||||||
|
)]
|
||||||
|
|
||||||
|
extern crate toml;
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
#[macro_use] extern crate semver;
|
||||||
|
|
||||||
|
#[macro_use] extern crate libimagstore;
|
||||||
|
#[macro_use] extern crate libimagerror;
|
||||||
|
|
||||||
|
module_entry_path_mod!("counter");
|
||||||
|
|
||||||
|
pub mod counter;
|
||||||
|
pub mod error;
|
||||||
|
pub mod result;
|
||||||
|
|
25
libimagcounter/src/result.rs
Normal file
25
libimagcounter/src/result.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::result::Result as RResult;
|
||||||
|
|
||||||
|
use error::CounterError;
|
||||||
|
|
||||||
|
pub type Result<T> = RResult<T, CounterError>;
|
||||||
|
|
42
libimagdiary/Cargo.toml
Normal file
42
libimagdiary/Cargo.toml
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
[package]
|
||||||
|
name = "libimagdiary"
|
||||||
|
version = "0.2.0"
|
||||||
|
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
||||||
|
|
||||||
|
description = "Library for the imag core distribution"
|
||||||
|
|
||||||
|
keywords = ["imag", "PIM", "personal", "information", "management"]
|
||||||
|
readme = "../README.md"
|
||||||
|
license = "LGPL-2.1"
|
||||||
|
|
||||||
|
documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html"
|
||||||
|
repository = "https://github.com/matthiasbeyer/imag"
|
||||||
|
homepage = "http://imag-pim.org"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chrono = "0.2"
|
||||||
|
log = "0.3"
|
||||||
|
semver = "0.5"
|
||||||
|
toml = "0.2.*"
|
||||||
|
regex = "0.1"
|
||||||
|
lazy_static = "0.2"
|
||||||
|
itertools = "0.5"
|
||||||
|
|
||||||
|
[dependencies.libimagstore]
|
||||||
|
path = "../libimagstore"
|
||||||
|
|
||||||
|
[dependencies.libimagerror]
|
||||||
|
path = "../libimagerror"
|
||||||
|
|
||||||
|
[dependencies.libimagutil]
|
||||||
|
path = "../libimagutil"
|
||||||
|
|
||||||
|
[dependencies.libimagrt]
|
||||||
|
path = "../libimagrt"
|
||||||
|
|
||||||
|
[dependencies.libimagentryedit]
|
||||||
|
path = "../libimagentryedit"
|
||||||
|
|
||||||
|
[dependencies.libimagentryview]
|
||||||
|
path = "../libimagentryview"
|
||||||
|
|
1
libimagdiary/README.md
Symbolic link
1
libimagdiary/README.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../doc/src/05100-lib-diary.md
|
38
libimagdiary/src/config.rs
Normal file
38
libimagdiary/src/config.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 toml::Value;
|
||||||
|
|
||||||
|
use libimagrt::runtime::Runtime;
|
||||||
|
|
||||||
|
pub fn get_default_diary_name(rt: &Runtime) -> Option<String> {
|
||||||
|
get_diary_config_section(rt)
|
||||||
|
.and_then(|config| {
|
||||||
|
match config.lookup("default_diary") {
|
||||||
|
Some(&Value::String(ref s)) => Some(s.clone()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_diary_config_section<'a>(rt: &'a Runtime) -> Option<&'a Value> {
|
||||||
|
rt.config()
|
||||||
|
.map(|config| config.config())
|
||||||
|
.and_then(|config| config.lookup("diary"))
|
||||||
|
}
|
128
libimagdiary/src/diary.rs
Normal file
128
libimagdiary/src/diary.rs
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::cmp::Ordering;
|
||||||
|
|
||||||
|
use libimagstore::store::Store;
|
||||||
|
use libimagstore::storeid::IntoStoreId;
|
||||||
|
use libimagerror::trace::trace_error;
|
||||||
|
|
||||||
|
use chrono::offset::local::Local;
|
||||||
|
use chrono::Datelike;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use chrono::naive::datetime::NaiveDateTime;
|
||||||
|
|
||||||
|
use entry::Entry;
|
||||||
|
use diaryid::DiaryId;
|
||||||
|
use error::DiaryError as DE;
|
||||||
|
use error::DiaryErrorKind as DEK;
|
||||||
|
use result::Result;
|
||||||
|
use iter::DiaryEntryIterator;
|
||||||
|
use is_in_diary::IsInDiary;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Diary<'a> {
|
||||||
|
store: &'a Store,
|
||||||
|
name: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Diary<'a> {
|
||||||
|
|
||||||
|
pub fn open(store: &'a Store, name: &'a str) -> Diary<'a> {
|
||||||
|
Diary {
|
||||||
|
store: store,
|
||||||
|
name: name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create or get a new entry for today
|
||||||
|
pub fn new_entry_today(&self) -> Result<Entry> {
|
||||||
|
let dt = Local::now();
|
||||||
|
let ndt = dt.naive_local();
|
||||||
|
let id = DiaryId::new(String::from(self.name), ndt.year(), ndt.month(), ndt.day(), 0, 0);
|
||||||
|
self.new_entry_by_id(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_entry_by_id(&self, id: DiaryId) -> Result<Entry> {
|
||||||
|
self.retrieve(id.with_diary_name(String::from(self.name)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn retrieve(&self, id: DiaryId) -> Result<Entry> {
|
||||||
|
id.into_storeid()
|
||||||
|
.and_then(|id| self.store.retrieve(id))
|
||||||
|
.map(|fle| Entry::new(fle))
|
||||||
|
.map_err(|e| DE::new(DEK::StoreWriteError, Some(Box::new(e))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an iterator for iterating over all entries
|
||||||
|
pub fn entries(&self) -> Result<DiaryEntryIterator<'a>> {
|
||||||
|
self.store
|
||||||
|
.retrieve_for_module("diary")
|
||||||
|
.map(|iter| DiaryEntryIterator::new(self.name, self.store, iter))
|
||||||
|
.map_err(|e| DE::new(DEK::StoreReadError, Some(Box::new(e))))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_entry(&self, entry: Entry) -> Result<()> {
|
||||||
|
if !entry.is_in_diary(self.name) {
|
||||||
|
return Err(DE::new(DEK::EntryNotInDiary, None));
|
||||||
|
}
|
||||||
|
let id = entry.get_location().clone();
|
||||||
|
drop(entry);
|
||||||
|
|
||||||
|
self.store.delete(id)
|
||||||
|
.map_err(|e| DE::new(DEK::StoreWriteError, Some(Box::new(e))))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_youngest_entry(&self) -> Option<Result<Entry>> {
|
||||||
|
match self.entries() {
|
||||||
|
Err(e) => Some(Err(e)),
|
||||||
|
Ok(entries) => {
|
||||||
|
entries.sorted_by(|a, b| {
|
||||||
|
match (a, b) {
|
||||||
|
(&Ok(ref a), &Ok(ref b)) => {
|
||||||
|
let a : NaiveDateTime = a.diary_id().into();
|
||||||
|
let b : NaiveDateTime = b.diary_id().into();
|
||||||
|
|
||||||
|
a.cmp(&b)
|
||||||
|
},
|
||||||
|
|
||||||
|
(&Ok(_), &Err(ref e)) => {
|
||||||
|
trace_error(e);
|
||||||
|
Ordering::Less
|
||||||
|
},
|
||||||
|
(&Err(ref e), &Ok(_)) => {
|
||||||
|
trace_error(e);
|
||||||
|
Ordering::Greater
|
||||||
|
},
|
||||||
|
(&Err(ref e1), &Err(ref e2)) => {
|
||||||
|
trace_error(e1);
|
||||||
|
trace_error(e2);
|
||||||
|
Ordering::Equal
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}).into_iter().next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &'a str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
257
libimagdiary/src/diaryid.rs
Normal file
257
libimagdiary/src/diaryid.rs
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::convert::Into;
|
||||||
|
use std::fmt::{Display, Formatter, Error as FmtError};
|
||||||
|
|
||||||
|
use chrono::naive::datetime::NaiveDateTime;
|
||||||
|
use chrono::naive::time::NaiveTime;
|
||||||
|
use chrono::naive::date::NaiveDate;
|
||||||
|
use chrono::Datelike;
|
||||||
|
use chrono::Timelike;
|
||||||
|
|
||||||
|
use libimagstore::storeid::StoreId;
|
||||||
|
use libimagstore::storeid::IntoStoreId;
|
||||||
|
use libimagstore::store::Result as StoreResult;
|
||||||
|
|
||||||
|
use error::DiaryError as DE;
|
||||||
|
use error::DiaryErrorKind as DEK;
|
||||||
|
use error::MapErrInto;
|
||||||
|
use libimagerror::into::IntoError;
|
||||||
|
|
||||||
|
use module_path::ModuleEntryPath;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct DiaryId {
|
||||||
|
name: String,
|
||||||
|
year: i32,
|
||||||
|
month: u32,
|
||||||
|
day: u32,
|
||||||
|
hour: u32,
|
||||||
|
minute: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiaryId {
|
||||||
|
|
||||||
|
pub fn new(name: String, y: i32, m: u32, d: u32, h: u32, min: u32) -> DiaryId {
|
||||||
|
DiaryId {
|
||||||
|
name: name,
|
||||||
|
year: y,
|
||||||
|
month: m,
|
||||||
|
day: d,
|
||||||
|
hour: h,
|
||||||
|
minute: min,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_datetime<DT: Datelike + Timelike>(diary_name: String, dt: DT) -> DiaryId {
|
||||||
|
DiaryId::new(diary_name, dt.year(), dt.month(), dt.day(), dt.hour(), dt.minute())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn diary_name(&self) -> &String {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn year(&self) -> i32 {
|
||||||
|
self.year
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn month(&self) -> u32 {
|
||||||
|
self.month
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn day(&self) -> u32 {
|
||||||
|
self.day
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hour(&self) -> u32 {
|
||||||
|
self.hour
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn minute(&self) -> u32 {
|
||||||
|
self.minute
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_diary_name(mut self, name: String) -> DiaryId {
|
||||||
|
self.name = name;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_year(mut self, year: i32) -> DiaryId {
|
||||||
|
self.year = year;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_month(mut self, month: u32) -> DiaryId {
|
||||||
|
self.month = month;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_day(mut self, day: u32) -> DiaryId {
|
||||||
|
self.day = day;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_hour(mut self, hour: u32) -> DiaryId {
|
||||||
|
self.hour = hour;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_minute(mut self, minute: u32) -> DiaryId {
|
||||||
|
self.minute = minute;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn now(name: String) -> DiaryId {
|
||||||
|
use chrono::offset::local::Local;
|
||||||
|
|
||||||
|
let now = Local::now();
|
||||||
|
let now_date = now.date().naive_local();
|
||||||
|
let now_time = now.time();
|
||||||
|
let dt = NaiveDateTime::new(now_date, now_time);
|
||||||
|
|
||||||
|
DiaryId::new(name, dt.year(), dt.month(), dt.day(), dt.hour(), dt.minute())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DiaryId {
|
||||||
|
|
||||||
|
/// Create a default DiaryId which is a diaryid for a diary named "default" with
|
||||||
|
/// time = 0000-00-00 00:00:00
|
||||||
|
fn default() -> DiaryId {
|
||||||
|
let dt = NaiveDateTime::new(NaiveDate::from_ymd(0, 0, 0), NaiveTime::from_hms(0, 0, 0));
|
||||||
|
DiaryId::from_datetime(String::from("default"), dt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoStoreId for DiaryId {
|
||||||
|
|
||||||
|
fn into_storeid(self) -> StoreResult<StoreId> {
|
||||||
|
let s : String = self.into();
|
||||||
|
ModuleEntryPath::new(s).into_storeid()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<String> for DiaryId {
|
||||||
|
|
||||||
|
fn into(self) -> String {
|
||||||
|
format!("{}/{:0>4}/{:0>2}/{:0>2}/{:0>2}:{:0>2}",
|
||||||
|
self.name, self.year, self.month, self.day, self.hour, self.minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for DiaryId {
|
||||||
|
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
|
||||||
|
write!(fmt, "{}/{:0>4}/{:0>2}/{:0>2}/{:0>2}:{:0>2}",
|
||||||
|
self.name, self.year, self.month, self.day, self.hour, self.minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<NaiveDateTime> for DiaryId {
|
||||||
|
|
||||||
|
fn into(self) -> NaiveDateTime {
|
||||||
|
let d = NaiveDate::from_ymd(self.year, self.month, self.day);
|
||||||
|
let t = NaiveTime::from_hms(self.hour, self.minute, 0);
|
||||||
|
NaiveDateTime::new(d, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FromStoreId : Sized {
|
||||||
|
|
||||||
|
fn from_storeid(&StoreId) -> Result<Self, DE>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::path::Component;
|
||||||
|
|
||||||
|
fn component_to_str<'a>(com: Component<'a>) -> Result<&'a str, DE> {
|
||||||
|
match com {
|
||||||
|
Component::Normal(s) => Some(s),
|
||||||
|
_ => None,
|
||||||
|
}.and_then(|s| s.to_str())
|
||||||
|
.ok_or(DEK::IdParseError.into_error())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStoreId for DiaryId {
|
||||||
|
|
||||||
|
fn from_storeid(s: &StoreId) -> Result<DiaryId, DE> {
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use std::path::Components;
|
||||||
|
use std::iter::Rev;
|
||||||
|
|
||||||
|
fn next_component<'a>(components: &'a mut Rev<Components>) -> Result<&'a str, DE> {
|
||||||
|
components.next()
|
||||||
|
.ok_or(DEK::IdParseError.into_error())
|
||||||
|
.and_then(component_to_str)
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cmps = s.components().rev();
|
||||||
|
|
||||||
|
let (hour, minute) = try!(next_component(&mut cmps).and_then(|time| {
|
||||||
|
let mut time = time.split(":");
|
||||||
|
let hour = time.next().and_then(|s| FromStr::from_str(s).ok());
|
||||||
|
let minute = time.next()
|
||||||
|
.and_then(|s| s.split("~").next())
|
||||||
|
.and_then(|s| FromStr::from_str(s).ok());
|
||||||
|
|
||||||
|
debug!("Hour = {:?}", hour);
|
||||||
|
debug!("Minute = {:?}", minute);
|
||||||
|
|
||||||
|
match (hour, minute) {
|
||||||
|
(Some(h), Some(m)) => Ok((h, m)),
|
||||||
|
_ => return Err(DE::new(DEK::IdParseError, None)),
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
let day: Result<u32,_> = next_component(&mut cmps)
|
||||||
|
.and_then(|s| s.parse::<u32>()
|
||||||
|
.map_err_into(DEK::IdParseError));
|
||||||
|
|
||||||
|
let month: Result<u32,_> = next_component(&mut cmps)
|
||||||
|
.and_then(|s| s.parse::<u32>()
|
||||||
|
.map_err_into(DEK::IdParseError));
|
||||||
|
|
||||||
|
let year: Result<i32,_> = next_component(&mut cmps)
|
||||||
|
.and_then(|s| s.parse::<i32>()
|
||||||
|
.map_err_into(DEK::IdParseError));
|
||||||
|
|
||||||
|
let name = next_component(&mut cmps).map(String::from);
|
||||||
|
|
||||||
|
debug!("Day = {:?}", day);
|
||||||
|
debug!("Month = {:?}", month);
|
||||||
|
debug!("Year = {:?}", year);
|
||||||
|
debug!("Name = {:?}", name);
|
||||||
|
|
||||||
|
let day = try!(day);
|
||||||
|
let month = try!(month);
|
||||||
|
let year = try!(year);
|
||||||
|
let name = try!(name);
|
||||||
|
|
||||||
|
Ok(DiaryId::new(name, year, month, day, hour, minute))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
90
libimagdiary/src/entry.rs
Normal file
90
libimagdiary/src/entry.rs
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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;
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
|
use libimagstore::store::FileLockEntry;
|
||||||
|
use libimagentryedit::edit::Edit;
|
||||||
|
use libimagentryedit::result::Result as EditResult;
|
||||||
|
use libimagrt::runtime::Runtime;
|
||||||
|
|
||||||
|
use diaryid::DiaryId;
|
||||||
|
use diaryid::FromStoreId;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Entry<'a>(FileLockEntry<'a>);
|
||||||
|
|
||||||
|
impl<'a> Deref for Entry<'a> {
|
||||||
|
type Target = FileLockEntry<'a>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &FileLockEntry<'a> {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DerefMut for Entry<'a> {
|
||||||
|
|
||||||
|
fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Entry<'a> {
|
||||||
|
|
||||||
|
pub fn new(fle: FileLockEntry<'a>) -> Entry<'a> {
|
||||||
|
Entry(fle)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the diary id for this entry.
|
||||||
|
///
|
||||||
|
/// TODO: calls Option::unwrap() as it assumes that an existing Entry has an ID that is parsable
|
||||||
|
pub fn diary_id(&self) -> DiaryId {
|
||||||
|
DiaryId::from_storeid(&self.0.get_location().clone()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Into<FileLockEntry<'a>> for Entry<'a> {
|
||||||
|
|
||||||
|
fn into(self) -> FileLockEntry<'a> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<FileLockEntry<'a>> for Entry<'a> {
|
||||||
|
|
||||||
|
fn from(fle: FileLockEntry<'a>) -> Entry<'a> {
|
||||||
|
Entry::new(fle)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Edit for Entry<'a> {
|
||||||
|
|
||||||
|
fn edit_content(&mut self, rt: &Runtime) -> EditResult<()> {
|
||||||
|
self.0.edit_content(rt)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
38
libimagdiary/src/error.rs
Normal file
38
libimagdiary/src/error.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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
|
||||||
|
//
|
||||||
|
|
||||||
|
generate_error_module!(
|
||||||
|
generate_error_types!(DiaryError, DiaryErrorKind,
|
||||||
|
StoreWriteError => "Error writing store",
|
||||||
|
StoreReadError => "Error reading store",
|
||||||
|
CannotFindDiary => "Cannot find diary",
|
||||||
|
CannotCreateNote => "Cannot create Note object for diary entry",
|
||||||
|
DiaryEditError => "Cannot edit diary entry",
|
||||||
|
PathConversionError => "Error while converting paths internally",
|
||||||
|
EntryNotInDiary => "Entry not in Diary",
|
||||||
|
IOError => "IO Error",
|
||||||
|
ViewError => "Error viewing diary entry",
|
||||||
|
IdParseError => "Error while parsing ID"
|
||||||
|
);
|
||||||
|
);
|
||||||
|
|
||||||
|
pub use self::error::DiaryError;
|
||||||
|
pub use self::error::DiaryErrorKind;
|
||||||
|
pub use self::error::MapErrInto;
|
||||||
|
|
44
libimagdiary/src/is_in_diary.rs
Normal file
44
libimagdiary/src/is_in_diary.rs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 libimagstore::store::Entry;
|
||||||
|
use libimagstore::storeid::StoreId;
|
||||||
|
|
||||||
|
pub trait IsInDiary {
|
||||||
|
|
||||||
|
fn is_in_diary(&self, name: &str) -> bool;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IsInDiary for Entry {
|
||||||
|
|
||||||
|
fn is_in_diary(&self, name: &str) -> bool {
|
||||||
|
self.get_location().clone().is_in_diary(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IsInDiary for StoreId {
|
||||||
|
|
||||||
|
fn is_in_diary(&self, name: &str) -> bool {
|
||||||
|
self.local().starts_with(format!("diary/{}", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
132
libimagdiary/src/iter.rs
Normal file
132
libimagdiary/src/iter.rs
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::fmt::{Debug, Formatter, Error as FmtError};
|
||||||
|
use std::result::Result as RResult;
|
||||||
|
|
||||||
|
use libimagstore::store::Store;
|
||||||
|
use libimagstore::storeid::StoreIdIterator;
|
||||||
|
|
||||||
|
use diaryid::DiaryId;
|
||||||
|
use diaryid::FromStoreId;
|
||||||
|
use is_in_diary::IsInDiary;
|
||||||
|
use entry::Entry as DiaryEntry;
|
||||||
|
use error::DiaryError as DE;
|
||||||
|
use error::DiaryErrorKind as DEK;
|
||||||
|
use result::Result;
|
||||||
|
use libimagerror::trace::trace_error;
|
||||||
|
|
||||||
|
/// A iterator for iterating over diary entries
|
||||||
|
pub struct DiaryEntryIterator<'a> {
|
||||||
|
store: &'a Store,
|
||||||
|
name: &'a str,
|
||||||
|
iter: StoreIdIterator,
|
||||||
|
|
||||||
|
year: Option<i32>,
|
||||||
|
month: Option<u32>,
|
||||||
|
day: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Debug for DiaryEntryIterator<'a> {
|
||||||
|
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
|
||||||
|
write!(fmt, "DiaryEntryIterator<name = {}, year = {:?}, month = {:?}, day = {:?}>",
|
||||||
|
self.name, self.year, self.month, self.day)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DiaryEntryIterator<'a> {
|
||||||
|
|
||||||
|
pub fn new(diaryname: &'a str, store: &'a Store, iter: StoreIdIterator) -> DiaryEntryIterator<'a> {
|
||||||
|
DiaryEntryIterator {
|
||||||
|
store: store,
|
||||||
|
name: diaryname,
|
||||||
|
iter: iter,
|
||||||
|
|
||||||
|
year: None,
|
||||||
|
month: None,
|
||||||
|
day: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by year, get all diary entries for this year
|
||||||
|
pub fn year(mut self, year: i32) -> DiaryEntryIterator<'a> {
|
||||||
|
self.year = Some(year);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by month, get all diary entries for this month (every year)
|
||||||
|
pub fn month(mut self, month: u32) -> DiaryEntryIterator<'a> {
|
||||||
|
self.month = Some(month);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by day, get all diary entries for this day (every year, every year)
|
||||||
|
pub fn day(mut self, day: u32) -> DiaryEntryIterator<'a> {
|
||||||
|
self.day = Some(day);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for DiaryEntryIterator<'a> {
|
||||||
|
type Item = Result<DiaryEntry<'a>>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Result<DiaryEntry<'a>>> {
|
||||||
|
loop {
|
||||||
|
let next = match self.iter.next() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
debug!("Next element: {:?}", next);
|
||||||
|
|
||||||
|
if next.is_in_diary(self.name) {
|
||||||
|
debug!("Seems to be in diary: {:?}", next);
|
||||||
|
let id = match DiaryId::from_storeid(&next) {
|
||||||
|
Ok(i) => i,
|
||||||
|
Err(e) => {
|
||||||
|
trace_error(&e);
|
||||||
|
debug!("Couldn't parse {:?} into DiaryId: {:?}", next, e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
debug!("Success parsing id = {:?}", id);
|
||||||
|
|
||||||
|
let y = match self.year { None => true, Some(y) => y == id.year() };
|
||||||
|
let m = match self.month { None => true, Some(m) => m == id.month() };
|
||||||
|
let d = match self.day { None => true, Some(d) => d == id.day() };
|
||||||
|
|
||||||
|
if y && m && d {
|
||||||
|
debug!("Return = {:?}", id);
|
||||||
|
return Some(self
|
||||||
|
.store
|
||||||
|
.retrieve(next)
|
||||||
|
.map(|fle| DiaryEntry::new(fle))
|
||||||
|
.map_err(|e| DE::new(DEK::StoreReadError, Some(Box::new(e))))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug!("Not in the requested diary ({}): {:?}", self.name, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
62
libimagdiary/src/lib.rs
Normal file
62
libimagdiary/src/lib.rs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#![deny(
|
||||||
|
dead_code,
|
||||||
|
non_camel_case_types,
|
||||||
|
non_snake_case,
|
||||||
|
path_statements,
|
||||||
|
trivial_numeric_casts,
|
||||||
|
unstable_features,
|
||||||
|
unused_allocation,
|
||||||
|
unused_import_braces,
|
||||||
|
unused_imports,
|
||||||
|
unused_must_use,
|
||||||
|
unused_mut,
|
||||||
|
unused_qualifications,
|
||||||
|
while_true,
|
||||||
|
)]
|
||||||
|
|
||||||
|
extern crate chrono;
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
#[macro_use] extern crate lazy_static;
|
||||||
|
extern crate semver;
|
||||||
|
extern crate toml;
|
||||||
|
extern crate regex;
|
||||||
|
extern crate itertools;
|
||||||
|
|
||||||
|
#[macro_use] extern crate libimagstore;
|
||||||
|
#[macro_use] extern crate libimagutil;
|
||||||
|
#[macro_use] extern crate libimagerror;
|
||||||
|
extern crate libimagentryedit;
|
||||||
|
extern crate libimagentryview;
|
||||||
|
extern crate libimagrt;
|
||||||
|
|
||||||
|
module_entry_path_mod!("diary");
|
||||||
|
|
||||||
|
pub mod config;
|
||||||
|
pub mod error;
|
||||||
|
pub mod diaryid;
|
||||||
|
pub mod diary;
|
||||||
|
pub mod is_in_diary;
|
||||||
|
pub mod entry;
|
||||||
|
pub mod iter;
|
||||||
|
pub mod result;
|
||||||
|
pub mod viewer;
|
||||||
|
|
24
libimagdiary/src/result.rs
Normal file
24
libimagdiary/src/result.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::result::Result as RResult;
|
||||||
|
|
||||||
|
use error::DiaryError;
|
||||||
|
|
||||||
|
pub type Result<T> = RResult<T, DiaryError>;
|
64
libimagdiary/src/viewer.rs
Normal file
64
libimagdiary/src/viewer.rs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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
|
||||||
|
//
|
||||||
|
|
||||||
|
//! A diary viewer built on libimagentryview.
|
||||||
|
|
||||||
|
use entry::Entry;
|
||||||
|
use error::DiaryErrorKind as DEK;
|
||||||
|
use error::MapErrInto;
|
||||||
|
use result::Result;
|
||||||
|
|
||||||
|
use libimagentryview::viewer::Viewer;
|
||||||
|
use libimagentryview::builtin::plain::PlainViewer;
|
||||||
|
|
||||||
|
/// This viewer does _not_ implement libimagentryview::viewer::Viewer because we need to be able to
|
||||||
|
/// call some diary-type specific functions on the entries passed to this.
|
||||||
|
///
|
||||||
|
/// This type is mainly just written to be constructed-called-deleted in one go:
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// DiaryViewer::new(show_header).view_entries(entries);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
pub struct DiaryViewer(PlainViewer);
|
||||||
|
|
||||||
|
impl DiaryViewer {
|
||||||
|
|
||||||
|
pub fn new(show_header: bool) -> DiaryViewer {
|
||||||
|
DiaryViewer(PlainViewer::new(show_header))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// View all entries from the iterator, or stop immediately if an error occurs, returning that
|
||||||
|
/// error.
|
||||||
|
pub fn view_entries<'a, I: Iterator<Item = Entry<'a>>>(&self, entries: I) -> Result<()> {
|
||||||
|
for entry in entries {
|
||||||
|
let id = entry.diary_id();
|
||||||
|
println!("{} :\n", id);
|
||||||
|
let _ = try!(self.0
|
||||||
|
.view_entry(&entry)
|
||||||
|
.map_err_into(DEK::ViewError)
|
||||||
|
.map_err_into(DEK::IOError));
|
||||||
|
println!("\n---\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
31
libimagmail/Cargo.toml
Normal file
31
libimagmail/Cargo.toml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
[package]
|
||||||
|
name = "libimagmail"
|
||||||
|
version = "0.2.0"
|
||||||
|
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
||||||
|
|
||||||
|
description = "Library for the imag core distribution"
|
||||||
|
|
||||||
|
keywords = ["imag", "PIM", "personal", "information", "management"]
|
||||||
|
readme = "../README.md"
|
||||||
|
license = "LGPL-2.1"
|
||||||
|
|
||||||
|
documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html"
|
||||||
|
repository = "https://github.com/matthiasbeyer/imag"
|
||||||
|
homepage = "http://imag-pim.org"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.3"
|
||||||
|
mailparse = "0.4"
|
||||||
|
semver = "0.5"
|
||||||
|
toml = "0.2.*"
|
||||||
|
filters = "0.1.*"
|
||||||
|
|
||||||
|
[dependencies.libimagstore]
|
||||||
|
path = "../libimagstore"
|
||||||
|
|
||||||
|
[dependencies.libimagerror]
|
||||||
|
path = "../libimagerror"
|
||||||
|
|
||||||
|
[dependencies.libimagref]
|
||||||
|
path = "../libimagref"
|
||||||
|
|
16
libimagmail/src/error.rs
Normal file
16
libimagmail/src/error.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
generate_error_module!(
|
||||||
|
generate_error_types!(MailError, MailErrorKind,
|
||||||
|
RefCreationError => "Error creating a reference to a file/directory",
|
||||||
|
RefHandlingError => "Error while handling the internal reference object",
|
||||||
|
MailParsingError => "Error while parsing mail",
|
||||||
|
|
||||||
|
FetchByHashError => "Error fetching mail from Store by hash",
|
||||||
|
FetchError => "Error fetching mail from Store",
|
||||||
|
IOError => "IO Error"
|
||||||
|
);
|
||||||
|
);
|
||||||
|
|
||||||
|
pub use self::error::MailError;
|
||||||
|
pub use self::error::MailErrorKind;
|
||||||
|
pub use self::error::MapErrInto;
|
||||||
|
|
67
libimagmail/src/hasher.rs
Normal file
67
libimagmail/src/hasher.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use std::io::Read;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use mailparse::{MailHeader, parse_mail};
|
||||||
|
|
||||||
|
use libimagref::hasher::Hasher;
|
||||||
|
use libimagref::hasher::DefaultHasher;
|
||||||
|
use libimagref::error::RefErrorKind as REK;
|
||||||
|
use libimagref::error::MapErrInto;
|
||||||
|
use libimagref::result::Result as RResult;
|
||||||
|
use libimagerror::into::IntoError;
|
||||||
|
|
||||||
|
use error::MailErrorKind as MEK;
|
||||||
|
|
||||||
|
pub struct MailHasher {
|
||||||
|
defaulthasher: DefaultHasher,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MailHasher {
|
||||||
|
|
||||||
|
pub fn new() -> MailHasher {
|
||||||
|
MailHasher { defaulthasher: DefaultHasher::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hasher for MailHasher {
|
||||||
|
|
||||||
|
fn hash_name(&self) -> &'static str {
|
||||||
|
"default_mail_hasher"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_hash<R: Read>(&mut self, pb: &PathBuf, c: &mut R) -> RResult<String> {
|
||||||
|
use filters::filter::Filter;
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
try!(c.read_to_string(&mut s).map_err_into(REK::UTF8Error).map_err_into(REK::IOError));
|
||||||
|
|
||||||
|
parse_mail(&s.as_bytes())
|
||||||
|
.map_err(Box::new)
|
||||||
|
.map_err(|e| MEK::MailParsingError.into_error_with_cause(e))
|
||||||
|
.map_err_into(REK::RefHashingError)
|
||||||
|
.and_then(|mail| {
|
||||||
|
let has_key = |hdr: &MailHeader, exp: &str|
|
||||||
|
hdr.get_key().map(|s| s == exp).unwrap_or(false);
|
||||||
|
|
||||||
|
let subject_filter = |hdr: &MailHeader| has_key(hdr, "Subject");
|
||||||
|
let from_filter = |hdr: &MailHeader| has_key(hdr, "From");
|
||||||
|
let to_filter = |hdr: &MailHeader| has_key(hdr, "To");
|
||||||
|
|
||||||
|
let filter = subject_filter.or(from_filter).or(to_filter);
|
||||||
|
|
||||||
|
let mut v = vec![];
|
||||||
|
for hdr in mail.headers.iter().filter(|item| filter.filter(item)) {
|
||||||
|
let s = try!(hdr.get_value()
|
||||||
|
.map_err(Box::new)
|
||||||
|
.map_err(|e| REK::RefHashingError.into_error_with_cause(e)));
|
||||||
|
|
||||||
|
v.push(s);
|
||||||
|
}
|
||||||
|
let s : String = v.join("");
|
||||||
|
|
||||||
|
self.defaulthasher.create_hash(pb, &mut s.as_bytes())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
libimagmail/src/iter.rs
Normal file
37
libimagmail/src/iter.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
//! Module for the MailIter
|
||||||
|
//!
|
||||||
|
//! MailIter is a iterator which takes an Iterator that yields `Ref` and yields itself
|
||||||
|
//! `Result<Mail>`, where `Err(_)` is returned if the Ref is not a Mail or parsing of the
|
||||||
|
//! referenced mail file failed.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use mail::Mail;
|
||||||
|
use result::Result;
|
||||||
|
|
||||||
|
use libimagref::reference::Ref;
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
struct MailIter<'a, I: 'a + Iterator<Item = Ref<'a>>> {
|
||||||
|
_marker: PhantomData<&'a I>,
|
||||||
|
i: I,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, I: Iterator<Item = Ref<'a>>> MailIter<'a, I> {
|
||||||
|
|
||||||
|
pub fn new(i: I) -> MailIter<'a, I> {
|
||||||
|
MailIter { _marker: PhantomData, i: i }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, I: Iterator<Item = Ref<'a>>> Iterator for MailIter<'a, I> {
|
||||||
|
|
||||||
|
type Item = Result<Mail<'a>>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Result<Mail<'a>>> {
|
||||||
|
self.i.next().map(Mail::from_ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
16
libimagmail/src/lib.rs
Normal file
16
libimagmail/src/lib.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
extern crate mailparse;
|
||||||
|
extern crate semver;
|
||||||
|
extern crate toml;
|
||||||
|
extern crate filters;
|
||||||
|
|
||||||
|
#[macro_use] extern crate libimagerror;
|
||||||
|
extern crate libimagstore;
|
||||||
|
extern crate libimagref;
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
pub mod hasher;
|
||||||
|
pub mod iter;
|
||||||
|
pub mod mail;
|
||||||
|
pub mod result;
|
||||||
|
|
120
libimagmail/src/mail.rs
Normal file
120
libimagmail/src/mail.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
use std::result::Result as RResult;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
use libimagstore::store::{FileLockEntry, Store};
|
||||||
|
use libimagref::reference::Ref;
|
||||||
|
use libimagref::flags::RefFlags;
|
||||||
|
|
||||||
|
use mailparse::{MailParseError, ParsedMail, parse_mail};
|
||||||
|
|
||||||
|
use hasher::MailHasher;
|
||||||
|
use result::Result;
|
||||||
|
use error::{MapErrInto, MailErrorKind as MEK};
|
||||||
|
|
||||||
|
struct Buffer(String);
|
||||||
|
|
||||||
|
impl Buffer {
|
||||||
|
pub fn parsed<'a>(&'a self) -> RResult<ParsedMail<'a>, MailParseError> {
|
||||||
|
parse_mail(self.0.as_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Buffer {
|
||||||
|
fn from(data: String) -> Buffer {
|
||||||
|
Buffer(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mail<'a>(Ref<'a>, Buffer);
|
||||||
|
|
||||||
|
impl<'a> Mail<'a> {
|
||||||
|
|
||||||
|
/// Imports a mail from the Path passed
|
||||||
|
pub fn import_from_path<P: AsRef<Path>>(store: &Store, p: P) -> Result<Mail> {
|
||||||
|
let h = MailHasher::new();
|
||||||
|
let f = RefFlags::default().with_content_hashing(true).with_permission_tracking(false);
|
||||||
|
let p = PathBuf::from(p.as_ref());
|
||||||
|
|
||||||
|
Ref::create_with_hasher(store, p, f, h)
|
||||||
|
.map_err_into(MEK::RefCreationError)
|
||||||
|
.and_then(|reference| {
|
||||||
|
reference.fs_file()
|
||||||
|
.map_err_into(MEK::RefHandlingError)
|
||||||
|
.and_then(|path| File::open(path).map_err_into(MEK::IOError))
|
||||||
|
.and_then(|mut file| {
|
||||||
|
let mut s = String::new();
|
||||||
|
file.read_to_string(&mut s)
|
||||||
|
.map(|_| s)
|
||||||
|
.map_err_into(MEK::IOError)
|
||||||
|
})
|
||||||
|
.map(Buffer::from)
|
||||||
|
.map(|buffer| Mail(reference, buffer))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opens a mail by the passed hash
|
||||||
|
pub fn open<S: AsRef<str>>(store: &Store, hash: S) -> Result<Option<Mail>> {
|
||||||
|
Ref::get_by_hash(store, String::from(hash.as_ref()))
|
||||||
|
.map_err_into(MEK::FetchByHashError)
|
||||||
|
.map_err_into(MEK::FetchError)
|
||||||
|
.and_then(|o| match o {
|
||||||
|
Some(r) => Mail::from_ref(r).map(Some),
|
||||||
|
None => Ok(None),
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement me as TryFrom as soon as it is stable
|
||||||
|
pub fn from_ref(r: Ref<'a>) -> Result<Mail> {
|
||||||
|
r.fs_file()
|
||||||
|
.map_err_into(MEK::RefHandlingError)
|
||||||
|
.and_then(|path| File::open(path).map_err_into(MEK::IOError))
|
||||||
|
.and_then(|mut file| {
|
||||||
|
let mut s = String::new();
|
||||||
|
file.read_to_string(&mut s)
|
||||||
|
.map(|_| s)
|
||||||
|
.map_err_into(MEK::IOError)
|
||||||
|
})
|
||||||
|
.map(Buffer::from)
|
||||||
|
.map(|buffer| Mail(r, buffer))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_field(&self, field: &str) -> Result<Option<String>> {
|
||||||
|
use mailparse::MailHeader;
|
||||||
|
|
||||||
|
self.1
|
||||||
|
.parsed()
|
||||||
|
.map_err_into(MEK::MailParsingError)
|
||||||
|
.map(|parsed| {
|
||||||
|
parsed.headers
|
||||||
|
.iter()
|
||||||
|
.filter(|hdr| hdr.get_key().map(|n| n == field).unwrap_or(false))
|
||||||
|
.next()
|
||||||
|
.and_then(|field| field.get_value().ok())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_from(&self) -> Result<Option<String>> {
|
||||||
|
self.get_field("From")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_to(&self) -> Result<Option<String>> {
|
||||||
|
self.get_field("To")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_subject(&self) -> Result<Option<String>> {
|
||||||
|
self.get_field("Subject")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_message_id(&self) -> Result<Option<String>> {
|
||||||
|
self.get_field("Message-ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_in_reply_to(&self) -> Result<Option<String>> {
|
||||||
|
self.get_field("In-Reply-To")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
6
libimagmail/src/result.rs
Normal file
6
libimagmail/src/result.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
use std::result::Result as RResult;
|
||||||
|
|
||||||
|
use error::MailError;
|
||||||
|
|
||||||
|
pub type Result<T> = RResult<T, MailError>;
|
||||||
|
|
32
libimagtodo/Cargo.toml
Normal file
32
libimagtodo/Cargo.toml
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
[package]
|
||||||
|
name = "libimagtodo"
|
||||||
|
version = "0.2.0"
|
||||||
|
authors = ["mario <mario-krehl@gmx.de>"]
|
||||||
|
|
||||||
|
description = "Library for the imag core distribution"
|
||||||
|
|
||||||
|
keywords = ["imag", "PIM", "personal", "information", "management"]
|
||||||
|
readme = "../README.md"
|
||||||
|
license = "LGPL-2.1"
|
||||||
|
|
||||||
|
documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html"
|
||||||
|
repository = "https://github.com/matthiasbeyer/imag"
|
||||||
|
homepage = "http://imag-pim.org"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
semver = "0.2"
|
||||||
|
task-hookrs = "0.2.2"
|
||||||
|
uuid = "0.3"
|
||||||
|
toml = "0.2.*"
|
||||||
|
log = "0.3"
|
||||||
|
serde_json = "0.8"
|
||||||
|
|
||||||
|
[dependencies.libimagstore]
|
||||||
|
path = "../libimagstore"
|
||||||
|
|
||||||
|
[dependencies.libimagerror]
|
||||||
|
path = "../libimagerror"
|
||||||
|
|
||||||
|
[dependencies.libimagutil]
|
||||||
|
path = "../libimagutil"
|
||||||
|
|
32
libimagtodo/src/error.rs
Normal file
32
libimagtodo/src/error.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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
|
||||||
|
//
|
||||||
|
|
||||||
|
generate_error_module!(
|
||||||
|
generate_error_types!(TodoError, TodoErrorKind,
|
||||||
|
ConversionError => "Conversion Error",
|
||||||
|
StoreError => "Store Error",
|
||||||
|
StoreIdError => "Store Id handling error",
|
||||||
|
ImportError => "Error importing"
|
||||||
|
);
|
||||||
|
);
|
||||||
|
|
||||||
|
pub use self::error::TodoError;
|
||||||
|
pub use self::error::TodoErrorKind;
|
||||||
|
pub use self::error::MapErrInto;
|
||||||
|
|
50
libimagtodo/src/lib.rs
Normal file
50
libimagtodo/src/lib.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#![deny(
|
||||||
|
non_camel_case_types,
|
||||||
|
non_snake_case,
|
||||||
|
path_statements,
|
||||||
|
trivial_numeric_casts,
|
||||||
|
unstable_features,
|
||||||
|
unused_allocation,
|
||||||
|
unused_import_braces,
|
||||||
|
unused_imports,
|
||||||
|
unused_mut,
|
||||||
|
unused_qualifications,
|
||||||
|
while_true,
|
||||||
|
)]
|
||||||
|
|
||||||
|
extern crate semver;
|
||||||
|
extern crate uuid;
|
||||||
|
extern crate toml;
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
|
#[macro_use] extern crate libimagstore;
|
||||||
|
#[macro_use] extern crate libimagerror;
|
||||||
|
extern crate libimagutil;
|
||||||
|
extern crate task_hookrs;
|
||||||
|
|
||||||
|
module_entry_path_mod!("todo");
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
pub mod result;
|
||||||
|
pub mod task;
|
||||||
|
|
24
libimagtodo/src/result.rs
Normal file
24
libimagtodo/src/result.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 error::TodoError;
|
||||||
|
|
||||||
|
use std::result::Result as RResult;
|
||||||
|
|
||||||
|
pub type Result<T> = RResult<T, TodoError>;
|
293
libimagtodo/src/task.rs
Normal file
293
libimagtodo/src/task.rs
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::collections::BTreeMap;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::io::BufRead;
|
||||||
|
use std::result::Result as RResult;
|
||||||
|
|
||||||
|
use toml::Value;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use task_hookrs::task::Task as TTask;
|
||||||
|
use task_hookrs::import::{import_task, import_tasks};
|
||||||
|
|
||||||
|
use libimagstore::store::{FileLockEntry, Store};
|
||||||
|
use libimagstore::storeid::{IntoStoreId, StoreIdIterator, StoreId};
|
||||||
|
use libimagerror::trace::MapErrTrace;
|
||||||
|
use libimagutil::debug_result::DebugResult;
|
||||||
|
use module_path::ModuleEntryPath;
|
||||||
|
|
||||||
|
use error::{TodoError, TodoErrorKind, MapErrInto};
|
||||||
|
use result::Result;
|
||||||
|
|
||||||
|
/// Task struct containing a `FileLockEntry`
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Task<'a>(FileLockEntry<'a>);
|
||||||
|
|
||||||
|
impl<'a> Task<'a> {
|
||||||
|
|
||||||
|
/// Concstructs a new `Task` with a `FileLockEntry`
|
||||||
|
pub fn new(fle: FileLockEntry<'a>) -> Task<'a> {
|
||||||
|
Task(fle)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn import<R: BufRead>(store: &'a Store, mut r: R) -> Result<(Task<'a>, String, Uuid)> {
|
||||||
|
let mut line = String::new();
|
||||||
|
r.read_line(&mut line);
|
||||||
|
import_task(&line.as_str())
|
||||||
|
.map_err_into(TodoErrorKind::ImportError)
|
||||||
|
.map_dbg_err_str("Error while importing task")
|
||||||
|
.map_err_dbg_trace()
|
||||||
|
.and_then(|t| {
|
||||||
|
let uuid = t.uuid().clone();
|
||||||
|
t.into_task(store).map(|t| (t, line, uuid))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a task from an import string. That is: read the imported string, get the UUID from it
|
||||||
|
/// and try to load this UUID from store.
|
||||||
|
///
|
||||||
|
/// Possible return values are:
|
||||||
|
///
|
||||||
|
/// * Ok(Ok(Task))
|
||||||
|
/// * Ok(Err(String)) - where the String is the String read from the `r` parameter
|
||||||
|
/// * Err(_) - where the error is an error that happened during evaluation
|
||||||
|
///
|
||||||
|
pub fn get_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<RResult<Task<'a>, String>>
|
||||||
|
{
|
||||||
|
let mut line = String::new();
|
||||||
|
r.read_line(&mut line);
|
||||||
|
Task::get_from_string(store, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a task from a String. The String is expected to contain the JSON-representation of the
|
||||||
|
/// Task to get from the store (only the UUID really matters in this case)
|
||||||
|
///
|
||||||
|
/// For an explanation on the return values see `Task::get_from_import()`.
|
||||||
|
pub fn get_from_string(store: &'a Store, s: String) -> Result<RResult<Task<'a>, String>> {
|
||||||
|
import_task(s.as_str())
|
||||||
|
.map_err_into(TodoErrorKind::ImportError)
|
||||||
|
.map_dbg_err_str("Error while importing task")
|
||||||
|
.map_err_dbg_trace()
|
||||||
|
.map(|t| t.uuid().clone())
|
||||||
|
.and_then(|uuid| Task::get_from_uuid(store, uuid))
|
||||||
|
.and_then(|o| match o {
|
||||||
|
None => Ok(Err(s)),
|
||||||
|
Some(t) => Ok(Ok(t)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a task from an UUID.
|
||||||
|
///
|
||||||
|
/// If there is no task with this UUID, this returns `Ok(None)`.
|
||||||
|
pub fn get_from_uuid(store: &'a Store, uuid: Uuid) -> Result<Option<Task<'a>>> {
|
||||||
|
ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
|
||||||
|
.into_storeid()
|
||||||
|
.and_then(|store_id| store.get(store_id))
|
||||||
|
.map(|o| o.map(Task::new))
|
||||||
|
.map_err_into(TodoErrorKind::StoreError)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as Task::get_from_import() but uses Store::retrieve() rather than Store::get(), to
|
||||||
|
/// implicitely create the task if it does not exist.
|
||||||
|
pub fn retrieve_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<Task<'a>> {
|
||||||
|
let mut line = String::new();
|
||||||
|
r.read_line(&mut line);
|
||||||
|
Task::retrieve_from_string(store, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve a task from a String. The String is expected to contain the JSON-representation of
|
||||||
|
/// the Task to retrieve from the store (only the UUID really matters in this case)
|
||||||
|
pub fn retrieve_from_string(store: &'a Store, s: String) -> Result<Task<'a>> {
|
||||||
|
Task::get_from_string(store, s)
|
||||||
|
.and_then(|opt| match opt {
|
||||||
|
Ok(task) => Ok(task),
|
||||||
|
Err(string) => import_task(string.as_str())
|
||||||
|
.map_err_into(TodoErrorKind::ImportError)
|
||||||
|
.map_dbg_err_str("Error while importing task")
|
||||||
|
.map_err_dbg_trace()
|
||||||
|
.and_then(|t| t.into_task(store)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_by_imports<R: BufRead>(store: &Store, r: R) -> Result<()> {
|
||||||
|
use serde_json::ser::to_string as serde_to_string;
|
||||||
|
use task_hookrs::status::TaskStatus;
|
||||||
|
|
||||||
|
for (counter, res_ttask) in import_tasks(r).into_iter().enumerate() {
|
||||||
|
match res_ttask {
|
||||||
|
Ok(ttask) => {
|
||||||
|
if counter % 2 == 1 {
|
||||||
|
// Only every second task is needed, the first one is the
|
||||||
|
// task before the change, and the second one after
|
||||||
|
// the change. The (maybe modified) second one is
|
||||||
|
// expected by taskwarrior.
|
||||||
|
match serde_to_string(&ttask).map_err_into(TodoErrorKind::ImportError) {
|
||||||
|
// use println!() here, as we talk with TW
|
||||||
|
Ok(val) => println!("{}", val),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Taskwarrior does not have the concept of deleted tasks, but only modified
|
||||||
|
// ones.
|
||||||
|
//
|
||||||
|
// Here we check if the status of a task is deleted and if yes, we delete it
|
||||||
|
// from the store.
|
||||||
|
if *ttask.status() == TaskStatus::Deleted {
|
||||||
|
match Task::delete_by_uuid(store, *ttask.uuid()) {
|
||||||
|
Ok(_) => info!("Deleted task {}", *ttask.uuid()),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // end if c % 2
|
||||||
|
},
|
||||||
|
Err(e) => return Err(e).map_err_into(TodoErrorKind::ImportError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_by_uuid(store: &Store, uuid: Uuid) -> Result<()> {
|
||||||
|
ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
|
||||||
|
.into_storeid()
|
||||||
|
.and_then(|id| store.delete(id))
|
||||||
|
.map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_as_ids(store: &Store) -> Result<StoreIdIterator> {
|
||||||
|
store.retrieve_for_module("todo/taskwarrior")
|
||||||
|
.map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all(store: &Store) -> Result<TaskIterator> {
|
||||||
|
Task::all_as_ids(store)
|
||||||
|
.map(|iter| TaskIterator::new(store, iter))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Deref for Task<'a> {
|
||||||
|
type Target = FileLockEntry<'a>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &FileLockEntry<'a> {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DerefMut for Task<'a> {
|
||||||
|
|
||||||
|
fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait to get a `libimagtodo::task::Task` out of the implementing object.
|
||||||
|
pub trait IntoTask<'a> {
|
||||||
|
|
||||||
|
/// # Usage
|
||||||
|
/// ```ignore
|
||||||
|
/// use std::io::stdin;
|
||||||
|
///
|
||||||
|
/// use task_hookrs::task::Task;
|
||||||
|
/// use task_hookrs::import::import;
|
||||||
|
/// use libimagstore::store::{Store, FileLockEntry};
|
||||||
|
///
|
||||||
|
/// if let Ok(task_hookrs_task) = import(stdin()) {
|
||||||
|
/// // Store is given at runtime
|
||||||
|
/// let task = task_hookrs_task.into_filelockentry(store);
|
||||||
|
/// println!("Task with uuid: {}", task.flentry.get_header().get("todo.uuid"));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
fn into_task(self, store : &'a Store) -> Result<Task<'a>>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoTask<'a> for TTask {
|
||||||
|
|
||||||
|
fn into_task(self, store : &'a Store) -> Result<Task<'a>> {
|
||||||
|
let uuid = self.uuid();
|
||||||
|
ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
|
||||||
|
.into_storeid()
|
||||||
|
.map_err_into(TodoErrorKind::StoreIdError)
|
||||||
|
.and_then(|id| {
|
||||||
|
store.retrieve(id)
|
||||||
|
.map_err_into(TodoErrorKind::StoreError)
|
||||||
|
.and_then(|mut fle| {
|
||||||
|
{
|
||||||
|
let mut hdr = fle.get_header_mut();
|
||||||
|
let read = hdr.read("todo").map_err_into(TodoErrorKind::StoreError);
|
||||||
|
if try!(read).is_none() {
|
||||||
|
try!(hdr
|
||||||
|
.set("todo", Value::Table(BTreeMap::new()))
|
||||||
|
.map_err_into(TodoErrorKind::StoreError));
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(hdr.set("todo.uuid", Value::String(format!("{}",uuid)))
|
||||||
|
.map_err_into(TodoErrorKind::StoreError));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If none of the errors above have returned the function, everything is fine
|
||||||
|
Ok(Task::new(fle))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
trait FromStoreId {
|
||||||
|
fn from_storeid<'a>(&'a Store, StoreId) -> Result<Task<'a>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FromStoreId for Task<'a> {
|
||||||
|
|
||||||
|
fn from_storeid<'b>(store: &'b Store, id: StoreId) -> Result<Task<'b>> {
|
||||||
|
match store.retrieve(id) {
|
||||||
|
Err(e) => Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
|
||||||
|
Ok(c) => Ok(Task::new( c )),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TaskIterator<'a> {
|
||||||
|
store: &'a Store,
|
||||||
|
iditer: StoreIdIterator,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TaskIterator<'a> {
|
||||||
|
|
||||||
|
pub fn new(store: &'a Store, iditer: StoreIdIterator) -> TaskIterator<'a> {
|
||||||
|
TaskIterator {
|
||||||
|
store: store,
|
||||||
|
iditer: iditer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for TaskIterator<'a> {
|
||||||
|
type Item = Result<Task<'a>>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Result<Task<'a>>> {
|
||||||
|
self.iditer.next().map(|id| Task::from_storeid(self.store, id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue