From 937530b41f142daf06ada32bc34ef160e9d173c2 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 31 Mar 2016 15:39:07 +0200 Subject: [PATCH 01/12] diary: initial import --- imag-diary/Cargo.toml | 6 ++++++ imag-diary/src/main.rs | 3 +++ 2 files changed, 9 insertions(+) create mode 100644 imag-diary/Cargo.toml create mode 100644 imag-diary/src/main.rs diff --git a/imag-diary/Cargo.toml b/imag-diary/Cargo.toml new file mode 100644 index 00000000..3f937114 --- /dev/null +++ b/imag-diary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "imag-diary" +version = "0.1.0" +authors = ["Matthias Beyer "] + +[dependencies] diff --git a/imag-diary/src/main.rs b/imag-diary/src/main.rs new file mode 100644 index 00000000..e7a11a96 --- /dev/null +++ b/imag-diary/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} From c5beb34614a2eeef6718137733bc043106c7bad4 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 31 Mar 2016 15:40:24 +0200 Subject: [PATCH 02/12] Add dependencies --- imag-diary/Cargo.toml | 29 +++++++++++++++++++++++++++++ imag-diary/src/main.rs | 12 ++++++++++++ libimagdiary/src/lib.rs | 2 +- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/imag-diary/Cargo.toml b/imag-diary/Cargo.toml index 3f937114..725c1364 100644 --- a/imag-diary/Cargo.toml +++ b/imag-diary/Cargo.toml @@ -4,3 +4,32 @@ version = "0.1.0" authors = ["Matthias Beyer "] [dependencies] +chrono = "0.2" +version = "2.0" +clap = "2.1.1" +log = "0.3.5" + +[dependencies.libimagrt] +path = "../libimagrt" + +[dependencies.libimagdiary] +path = "../libimagdiary" + +[dependencies.libimagentrylist] +path = "../libimagentrylist" + +[dependencies.libimagerror] +path = "../libimagerror" + +[dependencies.libimaginteraction] +path = "../libimaginteraction" + +[dependencies.libimagutil] +path = "../libimagutil" + +[dependencies.libimagstore] +path = "../libimagstore" + +[dependencies.libimagtimeui] +path = "../libimagtimeui" + diff --git a/imag-diary/src/main.rs b/imag-diary/src/main.rs index e7a11a96..ffcf5b61 100644 --- a/imag-diary/src/main.rs +++ b/imag-diary/src/main.rs @@ -1,3 +1,15 @@ +#[macro_use] extern crate log; +#[macro_use] extern crate version; +extern crate clap; +extern crate chrono; + +extern crate libimagdiary; +extern crate libimagentrylist; +extern crate libimaginteraction; +extern crate libimagrt; +extern crate libimagutil; +#[macro_use] extern crate libimagerror; + fn main() { println!("Hello, world!"); } diff --git a/libimagdiary/src/lib.rs b/libimagdiary/src/lib.rs index 4f80a277..a13dc428 100644 --- a/libimagdiary/src/lib.rs +++ b/libimagdiary/src/lib.rs @@ -23,7 +23,7 @@ extern crate regex; extern crate itertools; #[macro_use] extern crate libimagstore; -extern crate libimagutil; +#[macro_use] extern crate libimagutil; #[macro_use] extern crate libimagerror; extern crate libimagrt; From 55c833226cd56008f024fb589c748784e39c6ce2 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 22 Apr 2016 17:10:44 +0200 Subject: [PATCH 03/12] Add user interface definition --- imag-diary/src/ui.rs | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 imag-diary/src/ui.rs diff --git a/imag-diary/src/ui.rs b/imag-diary/src/ui.rs new file mode 100644 index 00000000..ac0b70bc --- /dev/null +++ b/imag-diary/src/ui.rs @@ -0,0 +1,45 @@ +use clap::{Arg, 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")) + ) + + .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")) + + // TODO: Support deleting diary entries + // .subcommand(SubCommand::with_name("delete") + // .about("Delete a diary entry") + // .version("0.1") +} + From 6f077f8ce40adc813dacf51f1a42a7f3c4b8f90a Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 6 Jun 2016 19:52:41 +0200 Subject: [PATCH 04/12] Add specification for delete subcommand --- imag-diary/src/ui.rs | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/imag-diary/src/ui.rs b/imag-diary/src/ui.rs index ac0b70bc..005c644f 100644 --- a/imag-diary/src/ui.rs +++ b/imag-diary/src/ui.rs @@ -37,9 +37,31 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .about("List diary entries") .version("0.1")) - // TODO: Support deleting diary entries - // .subcommand(SubCommand::with_name("delete") - // .about("Delete a diary entry") - // .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.")) + ) } From cf6d03ca0354287229c070bfdd0d3c500e347678 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 22 Apr 2016 17:10:50 +0200 Subject: [PATCH 05/12] Add main.rs --- imag-diary/src/main.rs | 169 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 1 deletion(-) diff --git a/imag-diary/src/main.rs b/imag-diary/src/main.rs index ffcf5b61..e93ea3f2 100644 --- a/imag-diary/src/main.rs +++ b/imag-diary/src/main.rs @@ -7,9 +7,176 @@ extern crate libimagdiary; 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::path::PathBuf; +use std::process::exit; +use chrono::naive::datetime::NaiveDateTime; + +use libimagdiary::diary::Diary; +use libimagdiary::diaryid::DiaryId; +use libimagdiary::error::DiaryError as DE; +use libimagdiary::error::DiaryErrorKind as DEK; +use libimagentrylist::listers::core::CoreLister; +use libimagentrylist::lister::Lister; +use libimagrt::edit::Edit; +use libimagrt::runtime::Runtime; +use libimagstore::storeid::StoreId; +use libimagerror::trace::trace_error; +use libimagtimeui::datetime::DateTime; +use libimagtimeui::parse::Parse; + +mod ui; + +use ui::build_ui; + fn main() { - println!("Hello, world!"); + 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), + "diary" => diary(&rt), + _ => { + debug!("Unknown command"); // More error handling + }, + } + }); +} + +fn create(rt: &Runtime) { + let diaryname = get_diary_name(rt); + if diaryname.is_none() { + warn!("No diary selected. Use either the configuration file or the commandline option"); + exit(1); + } + let diaryname = diaryname.unwrap(); + + let prevent_edit = rt.cli().subcommand_matches("create").unwrap().is_present("no-edit"); + + let diary = Diary::open(rt.store(), &diaryname[..]); + let res = diary.new_entry_today() + .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(|e| DE::new(DEK::DiaryEditError, Some(Box::new(e)))) + } + }); + + if let Err(e) = res { + trace_error(&e); + } else { + info!("Ok!"); + } +} + +fn list(rt: &Runtime) { + let diaryname = get_diary_name(rt); + if diaryname.is_none() { + warn!("No diary selected. Use either the configuration file or the commandline option"); + exit(1); + } + let diaryname = diaryname.unwrap(); + + fn location_to_listing_string(id: &StoreId, base: &PathBuf) -> String { + id.strip_prefix(base) + .map_err(|e| trace_error(&e)) + .ok() + .and_then(|p| p.to_str().map(String::from)) + .unwrap_or(String::from("<>")) + } + + 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(|a| { + debug!("Filtering: {:?}", a); + a.ok() + }).map(|e| e.into()); + + let base = rt.store().path(); + + CoreLister::new(&move |e| location_to_listing_string(e.get_location(), base)) + .list(es) // TODO: Do not ignore non-ok()s + .map_err(|e| DE::new(DEK::IOError, Some(Box::new(e)))) + }) + .map(|_| debug!("Ok")) + .map_err(|e| trace_error(&e)) + .ok(); +} + +fn delete(rt: &Runtime) { + unimplemented!() +} + +fn edit(rt: &Runtime) { + let diaryname = get_diary_name(rt); + if diaryname.is_none() { + warn!("No diary name"); + exit(1); + } + let diaryname = diaryname.unwrap(); + let diary = Diary::open(rt.store(), &diaryname[..]); + + let datetime : Option = 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(|e| DE::new(DEK::IOError, Some(Box::new(e)))), + + Some(Err(e)) => Err(e), + None => Err(DE::new(DEK::EntryNotInDiary, None)), + } + .map_err(|e| trace_error(&e)).ok(); +} + +fn diary(rt: &Runtime) { + unimplemented!() +} + + +fn get_diary_name(rt: &Runtime) -> Option { + use libimagdiary::config::get_default_diary_name; + + get_default_diary_name(rt) + .or(rt.cli().value_of("diaryname").map(String::from)) } From 4a134144d124b7e2bb2812255ef00e44671730a9 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 28 May 2016 17:10:18 +0200 Subject: [PATCH 06/12] Add debugging in lib-iter --- libimagdiary/src/iter.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libimagdiary/src/iter.rs b/libimagdiary/src/iter.rs index ce7aec78..1b2b16e5 100644 --- a/libimagdiary/src/iter.rs +++ b/libimagdiary/src/iter.rs @@ -82,15 +82,18 @@ impl<'a> Iterator for DiaryEntryIterator<'a> { debug!("Seems to be in diary: {:?}", next); let id = DiaryId::from_storeid(&next); if id.is_none() { + debug!("Couldn't parse {:?} into DiaryId", next); continue; } let id = id.unwrap(); + 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) From 19a37e6f9f053d8373d3e9bdba122c48262ded73 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 6 Jun 2016 19:54:19 +0200 Subject: [PATCH 07/12] Implement delete() --- imag-diary/src/main.rs | 54 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/imag-diary/src/main.rs b/imag-diary/src/main.rs index e93ea3f2..a3942051 100644 --- a/imag-diary/src/main.rs +++ b/imag-diary/src/main.rs @@ -55,7 +55,7 @@ fn main() { debug!("Call {}", name); match name { "create" => create(&rt), - // "delete" => delete(&rt), + "delete" => delete(&rt), "edit" => edit(&rt), "list" => list(&rt), "diary" => diary(&rt), @@ -135,7 +135,57 @@ fn list(rt: &Runtime) { } fn delete(rt: &Runtime) { - unimplemented!() + use libimaginteraction::ask::ask_bool; + + let diaryname = get_diary_name(rt); + if diaryname.is_none() { + warn!("No diary selected. Use either the configuration file or the commandline option"); + exit(1); + } + let diaryname = diaryname.unwrap(); + + let diary = Diary::open(rt.store(), &diaryname[..]); + debug!("Diary opened: {:?}", diary); + + let datetime : Option = 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(&e); + exit(1); + }, + None => { + warn!("No entry"); + exit(1); + }, + }; + + if !ask_bool(&format!("Deleting {:?}", to_del.get_location())[..], Some(true)) { + info!("Aborting delete action"); + return; + } + + match diary.delete_entry(to_del) { + Ok(_) => info!("Ok"), + Err(e) => { + trace_error(&e); + exit(1); + }, + } } fn edit(rt: &Runtime) { From 0f32957ce84ebd701023b6940a4c0ad77d3f6ac4 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 7 Jun 2016 20:21:03 +0200 Subject: [PATCH 08/12] UI spec fixup --- imag-diary/src/ui.rs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/imag-diary/src/ui.rs b/imag-diary/src/ui.rs index 005c644f..e923a25d 100644 --- a/imag-diary/src/ui.rs +++ b/imag-diary/src/ui.rs @@ -1,4 +1,4 @@ -use clap::{Arg, App, SubCommand}; +use clap::{Arg, ArgGroup, App, SubCommand}; pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { app @@ -18,6 +18,34 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .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") From d8071a56246493e012629358afa7159949840e98 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 8 Jun 2016 13:58:16 +0200 Subject: [PATCH 09/12] Add functionality so one can create diary entries hourly and minutely --- imag-diary/src/main.rs | 72 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/imag-diary/src/main.rs b/imag-diary/src/main.rs index a3942051..b696f552 100644 --- a/imag-diary/src/main.rs +++ b/imag-diary/src/main.rs @@ -67,6 +67,9 @@ fn main() { } fn create(rt: &Runtime) { + use libimagdiary::entry::Entry; + use libimagdiary::result::Result; + let diaryname = get_diary_name(rt); if diaryname.is_none() { warn!("No diary selected. Use either the configuration file or the commandline option"); @@ -76,8 +79,75 @@ fn create(rt: &Runtime) { let prevent_edit = rt.cli().subcommand_matches("create").unwrap().is_present("no-edit"); + fn create_entry<'a>(diary: &'a Diary, rt: &Runtime) -> Result> { + 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 mut 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 mut 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!("Unexpected error, cannot continue"); + exit(1); + }, + }; + + diary.new_entry_by_id(id) + } + } + let diary = Diary::open(rt.store(), &diaryname[..]); - let res = diary.new_entry_today() + let res = create_entry(&diary, rt) .and_then(|mut entry| { if prevent_edit { debug!("Not editing new diary entry"); From 7f4a721897625ceb85073d25ab5a068bde73c44d Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 8 Jun 2016 19:35:51 +0200 Subject: [PATCH 10/12] Add ui spec for viewing entries --- imag-diary/src/ui.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/imag-diary/src/ui.rs b/imag-diary/src/ui.rs index e923a25d..a38060bd 100644 --- a/imag-diary/src/ui.rs +++ b/imag-diary/src/ui.rs @@ -91,5 +91,17 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .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")) + ) + } From f6971c689605ed577561da0d94bd3daf842ee74c Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 8 Jun 2016 19:35:59 +0200 Subject: [PATCH 11/12] Implement view() --- imag-diary/Cargo.toml | 3 +++ imag-diary/src/main.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/imag-diary/Cargo.toml b/imag-diary/Cargo.toml index 725c1364..2edf2daa 100644 --- a/imag-diary/Cargo.toml +++ b/imag-diary/Cargo.toml @@ -18,6 +18,9 @@ path = "../libimagdiary" [dependencies.libimagentrylist] path = "../libimagentrylist" +[dependencies.libimagentryview] +path = "../libimagentryview" + [dependencies.libimagerror] path = "../libimagerror" diff --git a/imag-diary/src/main.rs b/imag-diary/src/main.rs index b696f552..6b009a39 100644 --- a/imag-diary/src/main.rs +++ b/imag-diary/src/main.rs @@ -5,6 +5,7 @@ extern crate chrono; extern crate libimagdiary; extern crate libimagentrylist; +extern crate libimagentryview; extern crate libimaginteraction; extern crate libimagrt; extern crate libimagstore; @@ -22,12 +23,15 @@ use libimagdiary::error::DiaryError as DE; use libimagdiary::error::DiaryErrorKind as DEK; use libimagentrylist::listers::core::CoreLister; use libimagentrylist::lister::Lister; +use libimagentryview::viewer::Viewer; +use libimagentryview::builtin::plain::PlainViewer; use libimagrt::edit::Edit; use libimagrt::runtime::Runtime; use libimagstore::storeid::StoreId; use libimagerror::trace::trace_error; use libimagtimeui::datetime::DateTime; use libimagtimeui::parse::Parse; +use libimagstore::store::FileLockEntry; mod ui; @@ -59,6 +63,7 @@ fn main() { "edit" => edit(&rt), "list" => list(&rt), "diary" => diary(&rt), + "view" => view(&rt), _ => { debug!("Unknown command"); // More error handling }, @@ -293,6 +298,31 @@ fn diary(rt: &Runtime) { unimplemented!() } +fn view(rt: &Runtime) { + let diaryname = get_diary_name(rt); + if diaryname.is_none() { + warn!("No diary name"); + exit(1); + } + let diaryname = diaryname.unwrap(); + let diary = Diary::open(rt.store(), &diaryname[..]); + let show_header = rt.cli().subcommand_matches("view").unwrap().is_present("show-header"); + + match diary.entries() { + Ok(entries) => { + let pv = PlainViewer::new(show_header); + for entry in entries.into_iter().filter_map(Result::ok) { + let id = entry.diary_id(); + println!("{} :\n", id); + pv.view_entry(&entry); + println!("\n---\n"); + } + }, + Err(e) => { + trace_error(&e); + }, + } +} fn get_diary_name(rt: &Runtime) -> Option { use libimagdiary::config::get_default_diary_name; From 5bde1cc2073baf929049dafc3c5f45ee4a69de75 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 8 Jun 2016 19:40:13 +0200 Subject: [PATCH 12/12] Split into several files --- imag-diary/src/create.rs | 112 ++++++++++++++++ imag-diary/src/delete.rs | 66 +++++++++ imag-diary/src/edit.rs | 47 +++++++ imag-diary/src/list.rs | 52 ++++++++ imag-diary/src/main.rs | 282 ++------------------------------------- imag-diary/src/util.rs | 9 ++ imag-diary/src/view.rs | 36 +++++ 7 files changed, 333 insertions(+), 271 deletions(-) create mode 100644 imag-diary/src/create.rs create mode 100644 imag-diary/src/delete.rs create mode 100644 imag-diary/src/edit.rs create mode 100644 imag-diary/src/list.rs create mode 100644 imag-diary/src/util.rs create mode 100644 imag-diary/src/view.rs diff --git a/imag-diary/src/create.rs b/imag-diary/src/create.rs new file mode 100644 index 00000000..0fb46cb9 --- /dev/null +++ b/imag-diary/src/create.rs @@ -0,0 +1,112 @@ +use std::process::exit; + +use libimagdiary::diary::Diary; +use libimagdiary::diaryid::DiaryId; +use libimagdiary::error::DiaryError as DE; +use libimagdiary::error::DiaryErrorKind as DEK; +use libimagrt::edit::Edit; +use libimagrt::runtime::Runtime; +use libimagerror::trace::trace_error; +use libimagdiary::entry::Entry; +use libimagdiary::result::Result; + +use util::get_diary_name; + +pub fn create(rt: &Runtime) { + + let diaryname = get_diary_name(rt); + if diaryname.is_none() { + warn!("No diary selected. Use either the configuration file or the commandline option"); + exit(1); + } + let diaryname = diaryname.unwrap(); + + let prevent_edit = rt.cli().subcommand_matches("create").unwrap().is_present("no-edit"); + + fn create_entry<'a>(diary: &'a Diary, rt: &Runtime) -> Result> { + 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 mut 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 mut 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!("Unexpected error, cannot continue"); + exit(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(|e| DE::new(DEK::DiaryEditError, Some(Box::new(e)))) + } + }); + + if let Err(e) = res { + trace_error(&e); + } else { + info!("Ok!"); + } +} + diff --git a/imag-diary/src/delete.rs b/imag-diary/src/delete.rs new file mode 100644 index 00000000..26e59900 --- /dev/null +++ b/imag-diary/src/delete.rs @@ -0,0 +1,66 @@ +use std::process::exit; +use chrono::naive::datetime::NaiveDateTime; + +use libimagdiary::diary::Diary; +use libimagdiary::diaryid::DiaryId; +use libimagrt::runtime::Runtime; +use libimagerror::trace::trace_error; +use libimagtimeui::datetime::DateTime; +use libimagtimeui::parse::Parse; + +use util::get_diary_name; + +pub fn delete(rt: &Runtime) { + use libimaginteraction::ask::ask_bool; + + let diaryname = get_diary_name(rt); + if diaryname.is_none() { + warn!("No diary selected. Use either the configuration file or the commandline option"); + exit(1); + } + let diaryname = diaryname.unwrap(); + + let diary = Diary::open(rt.store(), &diaryname[..]); + debug!("Diary opened: {:?}", diary); + + let datetime : Option = 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(&e); + exit(1); + }, + None => { + warn!("No entry"); + exit(1); + }, + }; + + if !ask_bool(&format!("Deleting {:?}", to_del.get_location())[..], Some(true)) { + info!("Aborting delete action"); + return; + } + + match diary.delete_entry(to_del) { + Ok(_) => info!("Ok"), + Err(e) => { + trace_error(&e); + exit(1); + }, + } +} + diff --git a/imag-diary/src/edit.rs b/imag-diary/src/edit.rs new file mode 100644 index 00000000..6b684855 --- /dev/null +++ b/imag-diary/src/edit.rs @@ -0,0 +1,47 @@ +use std::process::exit; +use chrono::naive::datetime::NaiveDateTime; + +use libimagdiary::diary::Diary; +use libimagdiary::diaryid::DiaryId; +use libimagdiary::error::DiaryError as DE; +use libimagdiary::error::DiaryErrorKind as DEK; +use libimagrt::edit::Edit; +use libimagrt::runtime::Runtime; +use libimagerror::trace::trace_error; +use libimagtimeui::datetime::DateTime; +use libimagtimeui::parse::Parse; + +use util::get_diary_name; + +pub fn edit(rt: &Runtime) { + let diaryname = get_diary_name(rt); + if diaryname.is_none() { + warn!("No diary name"); + exit(1); + } + let diaryname = diaryname.unwrap(); + let diary = Diary::open(rt.store(), &diaryname[..]); + + let datetime : Option = 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(|e| DE::new(DEK::IOError, Some(Box::new(e)))), + + Some(Err(e)) => Err(e), + None => Err(DE::new(DEK::EntryNotInDiary, None)), + } + .map_err(|e| trace_error(&e)).ok(); +} + + diff --git a/imag-diary/src/list.rs b/imag-diary/src/list.rs new file mode 100644 index 00000000..413213c0 --- /dev/null +++ b/imag-diary/src/list.rs @@ -0,0 +1,52 @@ +use std::path::PathBuf; +use std::process::exit; + +use libimagdiary::diary::Diary; +use libimagdiary::error::DiaryError as DE; +use libimagdiary::error::DiaryErrorKind as DEK; +use libimagentrylist::listers::core::CoreLister; +use libimagentrylist::lister::Lister; +use libimagrt::runtime::Runtime; +use libimagstore::storeid::StoreId; +use libimagerror::trace::trace_error; + +use util::get_diary_name; + +pub fn list(rt: &Runtime) { + let diaryname = get_diary_name(rt); + if diaryname.is_none() { + warn!("No diary selected. Use either the configuration file or the commandline option"); + exit(1); + } + let diaryname = diaryname.unwrap(); + + fn location_to_listing_string(id: &StoreId, base: &PathBuf) -> String { + id.strip_prefix(base) + .map_err(|e| trace_error(&e)) + .ok() + .and_then(|p| p.to_str().map(String::from)) + .unwrap_or(String::from("<>")) + } + + 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(|a| { + debug!("Filtering: {:?}", a); + a.ok() + }).map(|e| e.into()); + + let base = rt.store().path(); + + CoreLister::new(&move |e| location_to_listing_string(e.get_location(), base)) + .list(es) // TODO: Do not ignore non-ok()s + .map_err(|e| DE::new(DEK::IOError, Some(Box::new(e)))) + }) + .map(|_| debug!("Ok")) + .map_err(|e| trace_error(&e)) + .ok(); +} + diff --git a/imag-diary/src/main.rs b/imag-diary/src/main.rs index 6b009a39..647a9b25 100644 --- a/imag-diary/src/main.rs +++ b/imag-diary/src/main.rs @@ -13,29 +13,24 @@ extern crate libimagutil; extern crate libimagtimeui; #[macro_use] extern crate libimagerror; -use std::path::PathBuf; use std::process::exit; -use chrono::naive::datetime::NaiveDateTime; -use libimagdiary::diary::Diary; -use libimagdiary::diaryid::DiaryId; -use libimagdiary::error::DiaryError as DE; -use libimagdiary::error::DiaryErrorKind as DEK; -use libimagentrylist::listers::core::CoreLister; -use libimagentrylist::lister::Lister; -use libimagentryview::viewer::Viewer; -use libimagentryview::builtin::plain::PlainViewer; -use libimagrt::edit::Edit; use libimagrt::runtime::Runtime; -use libimagstore::storeid::StoreId; -use libimagerror::trace::trace_error; -use libimagtimeui::datetime::DateTime; -use libimagtimeui::parse::Parse; -use libimagstore::store::FileLockEntry; +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"; @@ -71,262 +66,7 @@ fn main() { }); } -fn create(rt: &Runtime) { - use libimagdiary::entry::Entry; - use libimagdiary::result::Result; - - let diaryname = get_diary_name(rt); - if diaryname.is_none() { - warn!("No diary selected. Use either the configuration file or the commandline option"); - exit(1); - } - let diaryname = diaryname.unwrap(); - - let prevent_edit = rt.cli().subcommand_matches("create").unwrap().is_present("no-edit"); - - fn create_entry<'a>(diary: &'a Diary, rt: &Runtime) -> Result> { - 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 mut 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 mut 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!("Unexpected error, cannot continue"); - exit(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(|e| DE::new(DEK::DiaryEditError, Some(Box::new(e)))) - } - }); - - if let Err(e) = res { - trace_error(&e); - } else { - info!("Ok!"); - } -} - -fn list(rt: &Runtime) { - let diaryname = get_diary_name(rt); - if diaryname.is_none() { - warn!("No diary selected. Use either the configuration file or the commandline option"); - exit(1); - } - let diaryname = diaryname.unwrap(); - - fn location_to_listing_string(id: &StoreId, base: &PathBuf) -> String { - id.strip_prefix(base) - .map_err(|e| trace_error(&e)) - .ok() - .and_then(|p| p.to_str().map(String::from)) - .unwrap_or(String::from("<>")) - } - - 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(|a| { - debug!("Filtering: {:?}", a); - a.ok() - }).map(|e| e.into()); - - let base = rt.store().path(); - - CoreLister::new(&move |e| location_to_listing_string(e.get_location(), base)) - .list(es) // TODO: Do not ignore non-ok()s - .map_err(|e| DE::new(DEK::IOError, Some(Box::new(e)))) - }) - .map(|_| debug!("Ok")) - .map_err(|e| trace_error(&e)) - .ok(); -} - -fn delete(rt: &Runtime) { - use libimaginteraction::ask::ask_bool; - - let diaryname = get_diary_name(rt); - if diaryname.is_none() { - warn!("No diary selected. Use either the configuration file or the commandline option"); - exit(1); - } - let diaryname = diaryname.unwrap(); - - let diary = Diary::open(rt.store(), &diaryname[..]); - debug!("Diary opened: {:?}", diary); - - let datetime : Option = 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(&e); - exit(1); - }, - None => { - warn!("No entry"); - exit(1); - }, - }; - - if !ask_bool(&format!("Deleting {:?}", to_del.get_location())[..], Some(true)) { - info!("Aborting delete action"); - return; - } - - match diary.delete_entry(to_del) { - Ok(_) => info!("Ok"), - Err(e) => { - trace_error(&e); - exit(1); - }, - } -} - -fn edit(rt: &Runtime) { - let diaryname = get_diary_name(rt); - if diaryname.is_none() { - warn!("No diary name"); - exit(1); - } - let diaryname = diaryname.unwrap(); - let diary = Diary::open(rt.store(), &diaryname[..]); - - let datetime : Option = 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(|e| DE::new(DEK::IOError, Some(Box::new(e)))), - - Some(Err(e)) => Err(e), - None => Err(DE::new(DEK::EntryNotInDiary, None)), - } - .map_err(|e| trace_error(&e)).ok(); -} - fn diary(rt: &Runtime) { unimplemented!() } -fn view(rt: &Runtime) { - let diaryname = get_diary_name(rt); - if diaryname.is_none() { - warn!("No diary name"); - exit(1); - } - let diaryname = diaryname.unwrap(); - let diary = Diary::open(rt.store(), &diaryname[..]); - let show_header = rt.cli().subcommand_matches("view").unwrap().is_present("show-header"); - - match diary.entries() { - Ok(entries) => { - let pv = PlainViewer::new(show_header); - for entry in entries.into_iter().filter_map(Result::ok) { - let id = entry.diary_id(); - println!("{} :\n", id); - pv.view_entry(&entry); - println!("\n---\n"); - } - }, - Err(e) => { - trace_error(&e); - }, - } -} - -fn get_diary_name(rt: &Runtime) -> Option { - use libimagdiary::config::get_default_diary_name; - - get_default_diary_name(rt) - .or(rt.cli().value_of("diaryname").map(String::from)) -} diff --git a/imag-diary/src/util.rs b/imag-diary/src/util.rs new file mode 100644 index 00000000..956062be --- /dev/null +++ b/imag-diary/src/util.rs @@ -0,0 +1,9 @@ +use libimagrt::runtime::Runtime; + +pub fn get_diary_name(rt: &Runtime) -> Option { + use libimagdiary::config::get_default_diary_name; + + get_default_diary_name(rt) + .or(rt.cli().value_of("diaryname").map(String::from)) +} + diff --git a/imag-diary/src/view.rs b/imag-diary/src/view.rs new file mode 100644 index 00000000..fd6e931c --- /dev/null +++ b/imag-diary/src/view.rs @@ -0,0 +1,36 @@ +use std::process::exit; + +use libimagdiary::diary::Diary; +use libimagentryview::viewer::Viewer; +use libimagentryview::builtin::plain::PlainViewer; +use libimagrt::runtime::Runtime; +use libimagerror::trace::trace_error; + +use util::get_diary_name; + +pub fn view(rt: &Runtime) { + let diaryname = get_diary_name(rt); + if diaryname.is_none() { + warn!("No diary name"); + exit(1); + } + let diaryname = diaryname.unwrap(); + let diary = Diary::open(rt.store(), &diaryname[..]); + let show_header = rt.cli().subcommand_matches("view").unwrap().is_present("show-header"); + + match diary.entries() { + Ok(entries) => { + let pv = PlainViewer::new(show_header); + for entry in entries.into_iter().filter_map(Result::ok) { + let id = entry.diary_id(); + println!("{} :\n", id); + pv.view_entry(&entry); + println!("\n---\n"); + } + }, + Err(e) => { + trace_error(&e); + }, + } +} +