Revert "Remove stuff for the focus-shift"

This reverts commit 63a7f0d8a9.
This commit is contained in:
Matthias Beyer 2017-05-30 20:25:00 +02:00
parent 5ba2568415
commit 80fb6cc2bd
70 changed files with 4597 additions and 0 deletions

View file

@ -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"

View file

@ -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
View 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
View file

@ -0,0 +1 @@
../doc/src/04020-module-bookmarks.md

158
imag-bookmark/src/main.rs Normal file
View 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
View 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
View 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
View file

@ -0,0 +1 @@
../doc/src/04020-module-counter.md

View 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);
});
});
}

View 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");
});
}

View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1 @@
../doc/src/04020-module-diary.md

123
imag-diary/src/create.rs Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1 @@
../doc/src/04020-module-mails.md

151
imag-mail/src/main.rs Normal file
View 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
View 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
View 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
View file

@ -0,0 +1,4 @@
#/!usr/bin/env bash
imag todo tw-hook --add

View file

@ -0,0 +1,4 @@
#/!usr/bin/env bash
imag todo tw-hook --delete

138
imag-todo/src/main.rs Normal file
View 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
View 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")
)
)
}

View 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
View file

@ -0,0 +1 @@
../doc/src/05100-lib-bookmark.md

View 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)
}
}
}

View 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;

View 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;

View 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)
}
}

View 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
View 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
View file

@ -0,0 +1 @@
../doc/src/05100-lib-counter.md

View 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))
}
}

View 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
View 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;

View 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
View 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
View file

@ -0,0 +1 @@
../doc/src/05100-lib-diary.md

View 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
View 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
View 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
View 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
View 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;

View 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
View 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
View 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;

View 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>;

View 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
View 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
View 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
View 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
View 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
View 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
View 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")
}
}

View 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
View 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
View 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
View 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
View 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
View 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))
}
}