summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2017-08-25 17:36:55 +0200
committerGitHub <noreply@github.com>2017-08-25 17:36:55 +0200
commit057d9192398e19f03297c094d9fbaf8932cadfa0 (patch)
tree5f283c6f79c4ff5fc8ce465f6a1553c5f946806f
parent4b07c21c34c047a7be2ae5041397842c811d80b3 (diff)
parent004b7887ff4728131ec1f5d39af5ed280e8d4457 (diff)
downloadimag-057d9192398e19f03297c094d9fbaf8932cadfa0.zip
imag-057d9192398e19f03297c094d9fbaf8932cadfa0.tar.gz
Merge pull request #996 from matthiasbeyer/imag-timetrack
imag timetrack
-rw-r--r--Cargo.toml1
-rw-r--r--imag-timetrack/Cargo.toml40
-rw-r--r--imag-timetrack/src/cont.rs112
-rw-r--r--imag-timetrack/src/day.rs119
-rw-r--r--imag-timetrack/src/list.rs124
-rw-r--r--imag-timetrack/src/main.rs93
-rw-r--r--imag-timetrack/src/month.rs135
-rw-r--r--imag-timetrack/src/start.rs56
-rw-r--r--imag-timetrack/src/stop.rs98
-rw-r--r--imag-timetrack/src/track.rs75
-rw-r--r--imag-timetrack/src/ui.rs178
-rw-r--r--imag-timetrack/src/week.rs126
-rw-r--r--imag-timetrack/src/year.rs126
-rw-r--r--libimagentrytimetrack/Cargo.toml1
-rw-r--r--libimagentrytimetrack/src/iter/filter.rs163
-rw-r--r--libimagentrytimetrack/src/lib.rs2
-rw-r--r--libimagentrytimetrack/src/tag.rs9
-rw-r--r--libimagentrytimetrack/src/timetracking.rs7
-rw-r--r--libimagentrytimetrack/src/timetrackingstore.rs4
19 files changed, 1374 insertions, 95 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 410576c..e612151 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,6 +11,7 @@ members = [
"imag-ref",
"imag-store",
"imag-tag",
+ "imag-timetrack",
"imag-todo",
"imag-view",
"libimagannotation",
diff --git a/imag-timetrack/Cargo.toml b/imag-timetrack/Cargo.toml
new file mode 100644
index 0000000..f9b4ab0
--- /dev/null
+++ b/imag-timetrack/Cargo.toml
@@ -0,0 +1,40 @@
+[package]
+name = "imag-timetrack"
+version = "0.3.0"
+authors = ["Matthias Beyer <mail@beyermatthias.de>"]
+
+description = "Part of the imag core distribution: imag-tag 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.*"
+log = "0.3"
+version = "2.0.1"
+semver = "0.2"
+toml = "^0.4"
+chrono = "^0.4"
+filters = "0.1.1"
+itertools = "0.6"
+
+[dependencies.libimagstore]
+path = "../libimagstore"
+
+[dependencies.libimagrt]
+path = "../libimagrt"
+
+[dependencies.libimagerror]
+path = "../libimagerror"
+
+[dependencies.libimagentrytimetrack]
+path = "../libimagentrytimetrack"
+
+[dependencies.libimagutil]
+path = "../libimagutil"
+
diff --git a/imag-timetrack/src/cont.rs b/imag-timetrack/src/cont.rs
new file mode 100644
index 0000000..8656367
--- /dev/null
+++ b/imag-timetrack/src/cont.rs
@@ -0,0 +1,112 @@
+//
+// 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::Ord;
+use std::cmp::Ordering;
+
+use filters::ops::not::Not;
+use filters::filter::Filter;
+use itertools::Itertools;
+use itertools::MinMaxResult;
+use chrono::NaiveDateTime;
+
+use libimagerror::trace::trace_error;
+use libimagerror::trace::MapErrTrace;
+use libimagerror::iter::TraceIterator;
+use libimagentrytimetrack::timetrackingstore::TimeTrackStore;
+use libimagentrytimetrack::timetracking::TimeTracking;
+use libimagentrytimetrack::iter::filter::*;
+
+use libimagrt::runtime::Runtime;
+
+pub fn cont(rt: &Runtime) -> i32 {
+ rt.store()
+ .get_timetrackings()
+ .and_then(|iter| {
+ let groups = iter
+ // unwrap everything, trace errors
+ .trace_unwrap()
+
+ // I want all entries with an end time
+ .filter(|e| has_end_time.filter(&e))
+
+ // Now group them by the end time
+ .group_by(|elem| match elem.get_end_datetime() {
+ Ok(Some(dt)) => dt,
+ Ok(None) => {
+ // error. We expect all of them having an end-time.
+ error!("Has no end time, but should be filtered out: {:?}", elem);
+ error!("This is a bug. Please report.");
+ error!("Will panic now");
+ panic!("Unknown bug")
+ }
+ Err(e) => {
+ trace_error(&e);
+ NaiveDateTime::from_timestamp(0, 0) // placeholder
+ }
+ });
+
+ // sort the trackings by key, so by end datetime
+ let elements = {
+ let mut v = vec![];
+ for (key, value) in groups.into_iter() {
+ v.push((key, value));
+ }
+
+ v.into_iter()
+ .sorted_by(|t1, t2| {
+ let (k1, _) = *t1;
+ let (k2, _) = *t2;
+ Ord::cmp(&k1, &k2)
+ })
+ .into_iter()
+
+ // get the last one, which should be the highest one
+ .last() // -> Option<_>
+ };
+
+ match elements {
+ Some((_, trackings)) => {
+ // and then, for all trackings
+ trackings
+ .fold(Ok(0), |acc, tracking| {
+ debug!("Having tracking: {:?}", tracking);
+
+ acc.and_then(|_| {
+ // create a new tracking with the same tag
+ tracking
+ .get_timetrack_tag()
+ .and_then(|tag| rt.store().create_timetracking_now(&tag))
+ .map(|_| 0)
+ .map_err_trace()
+ })
+ })
+ },
+
+ None => {
+ info!("No trackings to continue");
+ Ok(1)
+ },
+ }
+ })
+ .map(|_| 0)
+ .map_err_trace()
+ .unwrap_or(1)
+}
+
diff --git a/imag-timetrack/src/day.rs b/imag-timetrack/src/day.rs
new file mode 100644
index 0000000..04d3604
--- /dev/null
+++ b/imag-timetrack/src/day.rs
@@ -0,0 +1,119 @@
+//
+// 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::Ord;
+use std::cmp::Ordering;
+use std::str::FromStr;
+
+use filters::ops::not::Not;
+use filters::filter::Filter;
+use itertools::Itertools;
+use itertools::MinMaxResult;
+use chrono::NaiveDateTime;
+
+use libimagerror::trace::trace_error;
+use libimagerror::trace::MapErrTrace;
+use libimagerror::iter::TraceIterator;
+use libimagstore::store::FileLockEntry;
+use libimagentrytimetrack::timetrackingstore::TimeTrackStore;
+use libimagentrytimetrack::timetracking::TimeTracking;
+use libimagentrytimetrack::tag::TimeTrackingTag;
+use libimagentrytimetrack::iter::filter::*;
+
+use libimagrt::runtime::Runtime;
+
+
+pub fn day(rt: &Runtime) -> i32 {
+ let (_, cmd) = rt.cli().subcommand();
+ let cmd = cmd.unwrap(); // checked in main()
+
+ let filter = {
+ let start = match cmd.value_of("start").map(::chrono::naive::NaiveDateTime::from_str) {
+ None => ::chrono::offset::Local::today().and_hms(0, 0, 0).naive_local(),
+ Some(Ok(dt)) => dt,
+ Some(Err(e)) => {
+ trace_error(&e);
+ return 1
+ }
+ };
+
+ let end = match cmd.value_of("end").map(::chrono::naive::NaiveDateTime::from_str) {
+ None => ::chrono::offset::Local::today().and_hms(23, 59, 59).naive_local(),
+ Some(Ok(dt)) => dt,
+ Some(Err(e)) => {
+ trace_error(&e);
+ return 1
+ }
+ };
+
+ let tags = cmd
+ .values_of("tags")
+ .map(|ts| ts.into_iter().map(String::from).map(TimeTrackingTag::from).collect());
+
+ let start_time_filter = has_start_time_where(move |dt: &NaiveDateTime| {
+ start <= *dt
+ });
+
+ let end_time_filter = has_end_time_where(move |dt: &NaiveDateTime| {
+ end >= *dt
+ });
+
+ let tags_filter = move |fle: &FileLockEntry| {
+ match tags {
+ Some(ref tags) => has_one_of_tags(&tags).filter(fle),
+ None => true,
+ }
+ };
+
+ tags_filter.and(start_time_filter).and(end_time_filter)
+ };
+
+ rt.store()
+ .get_timetrackings()
+ .and_then(|iter| {
+ iter.trace_unwrap()
+ .filter(|e| filter.filter(e))
+ .fold(Ok(()), |acc, e| {
+ acc.and_then(|_| {
+ debug!("Processing {:?}", e.get_location());
+
+ let tag = try!(e.get_timetrack_tag());
+ debug!(" -> tag = {:?}", tag);
+
+ let start = try!(e.get_start_datetime());
+ debug!(" -> start = {:?}", start);
+
+ let end = try!(e.get_end_datetime());
+ debug!(" -> end = {:?}", end);
+
+ match (start, end) {
+ (None, _) => println!("{} has no start time.", tag),
+ (Some(s), None) => println!("{} | {} - ...", tag, s),
+ (Some(s), Some(e)) => println!("{} | {} - {}", tag, s, e),
+ }
+
+ Ok(())
+ })
+ })
+ })
+ .map(|_| 0)
+ .map_err_trace()
+ .unwrap_or(1)
+}
+
diff --git a/imag-timetrack/src/list.rs b/imag-timetrack/src/list.rs
new file mode 100644
index 0000000..b7d8dcd
--- /dev/null
+++ b/imag-timetrack/src/list.rs
@@ -0,0 +1,124 @@
+//
+// 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::Ord;
+use std::cmp::Ordering;
+use std::str::FromStr;
+
+use filters::ops::not::Not;
+use filters::filter::Filter;
+use itertools::Itertools;
+use itertools::MinMaxResult;
+use chrono::NaiveDateTime;
+
+use libimagerror::trace::trace_error;
+use libimagerror::trace::MapErrTrace;
+use libimagerror::iter::TraceIterator;
+use libimagstore::store::FileLockEntry;
+use libimagentrytimetrack::timetrackingstore::TimeTrackStore;
+use libimagentrytimetrack::timetracking::TimeTracking;
+use libimagentrytimetrack::iter::filter::*;
+
+use libimagrt::runtime::Runtime;
+
+pub fn list(rt: &Runtime) -> i32 {
+ let (_, cmd) = rt.cli().subcommand();
+ let cmd = cmd.unwrap(); // checked in main()
+
+ let start = match cmd.value_of("start-time").map(::chrono::naive::NaiveDateTime::from_str) {
+ None => None,
+ Some(Ok(dt)) => Some(dt),
+ Some(Err(e)) => {
+ trace_error(&e);
+ None
+ }
+ };
+ let end = match cmd.value_of("end-time").map(::chrono::naive::NaiveDateTime::from_str) {
+ None => None,
+ Some(Ok(dt)) => Some(dt),
+ Some(Err(e)) => {
+ trace_error(&e);
+ None
+ }
+ };
+
+ let list_not_ended = cmd.is_present("list-not-ended");
+
+ let start_time_filter = |timetracking: &FileLockEntry| {
+ start.map(|s| match timetracking.get_start_datetime() {
+ Ok(Some(dt)) => dt >= s,
+ Ok(None) => {
+ warn!("Funny things are happening: Timetracking has no start time");
+ false
+ }
+ Err(e) => {
+ trace_error(&e);
+ false
+ }
+ })
+ .unwrap_or(true)
+ };
+
+ let end_time_filter = |timetracking: &FileLockEntry| {
+ start.map(|s| match timetracking.get_end_datetime() {
+ Ok(Some(dt)) => dt <= s,
+ Ok(None) => list_not_ended,
+ Err(e) => {
+ trace_error(&e);
+ false
+ }
+ })
+ .unwrap_or(true)
+ };
+
+ let filter = start_time_filter.and(end_time_filter);
+
+ rt.store()
+ .get_timetrackings()
+ .and_then(|iter| {
+ iter.trace_unwrap()
+ .filter(|e| filter.filter(e))
+ .fold(Ok(()), |acc, e| {
+ acc.and_then(|_| {
+ debug!("Processing {:?}", e.get_location());
+
+ let tag = try!(e.get_timetrack_tag());
+ debug!(" -> tag = {:?}", tag);
+
+ let start = try!(e.get_start_datetime());
+ debug!(" -> start = {:?}", start);
+
+ let end = try!(e.get_end_datetime());
+ debug!(" -> end = {:?}", end);
+
+ match (start, end) {
+ (None, _) => println!("{} has no start time.", tag),
+ (Some(s), None) => println!("{} | {} - ...", tag, s),
+ (Some(s), Some(e)) => println!("{} | {} - {}", tag, s, e),
+ }
+
+ Ok(())
+ })
+ })
+ })
+ .map(|_| 0)
+ .map_err_trace()
+ .unwrap_or(1)
+}
+
diff --git a/imag-timetrack/src/main.rs b/imag-timetrack/src/main.rs
new file mode 100644
index 0000000..7546e6b
--- /dev/null
+++ b/imag-timetrack/src/main.rs
@@ -0,0 +1,93 @@
+//
+// 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
+//
+
+#[macro_use]
+extern crate log;
+
+#[macro_use]
+extern crate version;
+
+extern crate clap;
+extern crate semver;
+extern crate toml;
+extern crate chrono;
+extern crate filters;
+extern crate itertools;
+
+extern crate libimagerror;
+extern crate libimagstore;
+extern crate libimagrt;
+extern crate libimagentrytimetrack;
+extern crate libimagutil;
+
+mod cont;
+mod day;
+mod list;
+mod month;
+mod start;
+mod stop;
+mod track;
+mod ui;
+mod week;
+mod year;
+
+use cont::cont;
+use day::day;
+use list::list;
+use month::month;
+use start::start;
+use stop::stop;
+use track::track;
+use ui::build_ui;
+use week::week;
+use year::year;
+
+use libimagrt::setup::generate_runtime_setup;
+
+fn main() {
+ let rt = generate_runtime_setup("imag-timetrack",
+ &version!()[..],
+ "Time tracking module",
+ build_ui);
+
+ let command = rt.cli().subcommand_name();
+ let retval = if let Some(command) = command {
+ debug!("Call: {}", command);
+ match command {
+ "continue" => cont(&rt),
+ "day" => day(&rt),
+ "list" => list(&rt),
+ "month" => month(&rt),
+ "start" => start(&rt),
+ "stop" => stop(&rt),
+ "track" => track(&rt),
+ "week" => week(&rt),
+ "year" => year(&rt),
+ _ => {
+ error!("Unknown command");
+ 1
+ },
+ }
+ } else {
+ error!("No command");
+ 1
+ };
+
+ ::std::process::exit(retval);
+}
diff --git a/imag-timetrack/src/month.rs b/imag-timetrack/src/month.rs
new file mode 100644
index 0000000..0f9872c
--- /dev/null
+++ b/imag-timetrack/src/month.rs
@@ -0,0 +1,135 @@
+//
+// 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::Ord;
+use std::cmp::Ordering;
+use std::str::FromStr;
+
+use filters::ops::not::Not;
+use filters::filter::Filter;
+use itertools::Itertools;
+use itertools::MinMaxResult;
+use chrono::NaiveDateTime;
+
+use libimagerror::trace::trace_error;
+use libimagerror::trace::MapErrTrace;
+use libimagerror::iter::TraceIterator;
+use libimagstore::store::FileLockEntry;
+use libimagentrytimetrack::timetrackingstore::TimeTrackStore;
+use libimagentrytimetrack::timetracking::TimeTracking;
+use libimagentrytimetrack::tag::TimeTrackingTag;
+use libimagentrytimetrack::iter::filter::*;
+
+use libimagrt::runtime::Runtime;
+
+pub fn month(rt: &Runtime) -> i32 {
+ let cmd = rt.cli().subcommand().1.unwrap(); // checked in main
+
+ let filter = {
+ use chrono::offset::Local;
+ use chrono::naive::NaiveDate;
+ use chrono::Weekday;
+ use chrono::Datelike;
+
+ let now = Local::now();
+
+ let start = match cmd.value_of("start").map(::chrono::naive::NaiveDateTime::from_str) {
+ None => NaiveDate::from_ymd(now.year(), now.month(), 1).and_hms(0, 0, 0),
+ Some(Ok(dt)) => dt,
+ Some(Err(e)) => {
+ trace_error(&e);
+ return 1
+ }
+ };
+
+ let end = match cmd.value_of("end").map(::chrono::naive::NaiveDateTime::from_str) {
+ None => {
+
+ // Is it much harder to go to the last second of the current month than to the first
+ // second of the next month, right?
+ let (year, month) = if now.month() == 12 {
+ (now.year() + 1, 1)
+ } else {
+ (now.year(), now.month())
+ };
+
+ NaiveDate::from_ymd(year, month, 1).and_hms(0, 0, 0)
+ },
+ Some(Ok(dt)) => dt,
+ Some(Err(e)) => {
+ trace_error(&e);
+ return 1
+ }
+ };
+
+ let tags = cmd
+ .values_of("tags")
+ .map(|ts| ts.into_iter().map(String::from).map(TimeTrackingTag::from).collect());
+
+ let start_time_filter = has_start_time_where(move |dt: &NaiveDateTime| {
+ start <= *dt
+ });
+
+ let end_time_filter = has_end_time_where(move |dt: &NaiveDateTime| {
+ end >= *dt
+ });
+
+ let tags_filter = move |fle: &FileLockEntry| {
+ match tags {
+ Some(ref tags) => has_one_of_tags(&tags).filter(fle),
+ None => true,
+ }
+ };
+
+ tags_filter.and(start_time_filter).and(end_time_filter)
+ };
+
+ rt.store()
+ .get_timetrackings()
+ .and_then(|iter| {
+ iter.trace_unwrap()
+ .filter(|e| filter.filter(e))
+ .fold(Ok(()), |acc, e| {
+ acc.and_then(|_| {
+ debug!("Processing {:?}", e.get_location());
+
+ let tag = try!(e.get_timetrack_tag());
+ debug!(" -> tag = {:?}", tag);
+
+ let start = try!(e.get_start_datetime());
+ debug!(" -> start = {:?}", start);
+
+ let end = try!(e.get_end_datetime());
+ debug!(" -> end = {:?}", end);
+
+ match (start, end) {
+ (None, _) => println!("{} has no start time.", tag),
+ (Some(s), None) => println!("{} | {} - ...", tag, s),
+ (Some(s), Some(e)) => println!("{} | {} - {}", tag, s, e),
+ }
+
+ Ok(())
+ })
+ })
+ })
+ .map(|_| 0)
+ .map_err_trace()
+ .unwrap_or(1)
+}
+
diff --git a/imag-timetrack/src/start.rs b/imag-timetrack/src/start.rs
new file mode 100644
index 0000000..8518f56
--- /dev/null
+++ b/imag-timetrack/src/start.rs
@@ -0,0 +1,56 @@
+//
+// 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;
+use libimagentrytimetrack::tag::TimeTrackingTag;
+use libimagentrytimetrack::timetrackingstore::TimeTrackStore;
+use libimagerror::trace::MapErrTrace;
+
+pub fn start(rt: &Runtime) -> i32 {
+ let (_, cmd) = rt.cli().subcommand();
+ let cmd = cmd.unwrap(); // checked in main()
+
+ let start = match cmd.value_of("start-time") {
+ None | Some("now") => ::chrono::offset::Local::now().naive_local(),
+ Some(ndt) => match ::chrono::naive::NaiveDateTime::from_str(ndt) {
+ Ok(ndt) => ndt,
+ Err(e) => {
+ trace_error(&e);
+ error!("Cannot continue, not having start time");
+ return 1
+ },
+ }
+ };
+
+ cmd.values_of("tags")
+ .unwrap() // enforced by clap
+ .map(String::from)
+ .map(TimeTrackingTag::from)
+ .fold(0, |acc, ttt| {
+ rt.store()
+ .create_timetracking_at(&start, &ttt)
+ .map_err_trace()
+ .map(|_| acc)
+ .unwrap_or(1)
+ })
+}
+
diff --git a/imag-timetrack/src/stop.rs b/imag-timetrack/src/stop.rs
new file mode 100644
index 0000000..29b03a0
--- /dev/null
+++ b/imag-timetrack/src/stop.rs
@@ -0,0 +1,98 @@
+//
+// 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 filters::filter::Filter;
+
+use libimagerror::trace::trace_error;
+use libimagerror::iter::TraceIterator;
+use libimagrt::runtime::Runtime;
+use libimagrt::setup::generate_runtime_setup;
+
+use libimagentrytimetrack::timetracking::TimeTracking;
+use libimagentrytimetrack::tag::TimeTrackingTag;
+use libimagentrytimetrack::timetrackingstore::*;
+use libimagentrytimetrack::iter::get::GetTimeTrackIter;
+use libimagentrytimetrack::iter::filter::has_end_time;
+use libimagentrytimetrack::iter::filter::has_one_of_tags;
+
+pub fn stop(rt: &Runtime) -> i32 {
+ let (_, cmd) = rt.cli().subcommand();
+ let cmd = cmd.unwrap(); // checked in main()
+
+ let stop_time = match cmd.value_of("stop-time") {
+ None | Some("now") => ::chrono::offset::Local::now().naive_local(),
+ Some(ndt) => match ::chrono::naive::NaiveDateTime::from_str(ndt) {
+ Ok(ndt) => ndt,
+ Err(e) => {
+ trace_error(&e);
+ error!("Cannot continue, not having start time");
+ return 1
+ },
+ }
+ };
+
+
+ // TODO: We do not yet support stopping all tags by simply calling the "stop" subcommand!
+
+ let tags : Vec<TimeTrackingTag> = cmd.values_of("tags")
+ .unwrap() // enforced by clap
+ .map(String::from)
+ .map(TimeTrackingTag::from)
+ .collect();
+
+ let iter : GetTimeTrackIter = match rt.store().get_timetrackings() {
+ Ok(i) => i,
+ Err(e) => {
+ error!("Getting timetrackings failed");
+ trace_error(&e);
+ return 1
+ }
+
+ };
+
+ let filter = has_end_time.not().and(has_one_of_tags(&tags));
+
+ // Filter all timetrackings for the ones that are not yet ended.
+ iter.trace_unwrap()
+ .filter_map(|elem| {
+ if filter.filter(&elem) {
+ Some(elem)
+ } else {
+ None
+ }
+ })
+
+ // for each of these timetrackings, end them
+ // for each result, print the backtrace (if any)
+ .fold(0, |acc, mut elem| match elem.set_end_datetime(stop_time.clone()) {
+ Err(e) => { // if there was an error
+ trace_error(&e); // trace
+ 1 // set exit code to 1
+ },
+ Ok(_) => {
+ debug!("Setting end time worked: {:?}", elem);
+
+ // Keep the exit code
+ acc
+ }
+ })
+}
+
diff --git a/imag-timetrack/src/track.rs b/imag-timetrack/src/track.rs
new file mode 100644
index 0000000..a723b90
--- /dev/null
+++ b/imag-timetrack/src/track.rs
@@ -0,0 +1,75 @@
+//
+// 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 clap::ArgMatches;
+use chrono::naive::NaiveDateTime;
+
+use libimagrt::runtime::Runtime;
+use libimagerror::trace::trace_error;
+use libimagentrytimetrack::tag::TimeTrackingTag;
+use libimagentrytimetrack::timetrackingstore::TimeTrackStore;
+use libimagerror::trace::MapErrTrace;
+
+pub fn track(rt: &Runtime) -> i32 {
+ let (_, cmd) = rt.cli().subcommand();
+ let cmd = cmd.unwrap(); // checked in main()
+
+ // Gets the appropriate time from the commandline or None on error (errors already logged, so
+ // callee can directly return in case of error
+ fn get_time(cmd: &ArgMatches, clap_name: &str, errname: &str) -> Option<NaiveDateTime> {
+ let val = cmd
+ .value_of(clap_name)
+ .map(::chrono::naive::NaiveDateTime::from_str)
+ .unwrap(); // clap has our back
+
+ match val {
+ Ok(ndt) => Some(ndt),
+ Err(e) => {
+ trace_error(&e);
+ error!("Cannot continue, not having {} time", errname);
+ None
+ },
+ }
+ }
+
+ let start = match get_time(&cmd, "start-time", "start") {
+ Some(t) => t,
+ None => return 1,
+ };
+
+ let stop = match get_time(&cmd, "stop-time", "stop") {
+ Some(t) => t,
+ None => return 1,
+ };
+
+ cmd.values_of("tags")
+ .unwrap() // enforced by clap
+ .map(String::from)
+ .map(TimeTrackingTag::from)
+ .fold(0, |acc, ttt| {
+ rt.store()
+ .create_timetracking(&start, &stop, &ttt)
+ .map_err_trace()
+ .map(|_| acc)
+ .unwrap_or(1)
+ })
+}
+
diff --git a/imag-timetrack/src/ui.rs b/imag-timetrack/src/ui.rs
new file mode 100644
index 0000000..cefcef8
--- /dev/null
+++ b/imag-timetrack/src/ui.rs
@@ -0,0 +1,178 @@
+//
+// 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
+ .subcommand(SubCommand::with_name("list")
+ .about("List time trackings")
+ .version("0.1")
+ .arg(Arg::with_name("start-time")
+ .short("f")
+ .long("from")
+ .takes_value(true)
+ .multiple(false)
+ .required(false)
+ .help("Set earliest time from which on time trackings should be shown (use 'now' for current time)"))
+ .arg(Arg::with_name("end-time")
+ .short("t")
+ .long("to")
+ .takes_value(true)
+ .multiple(false)
+ .required(false)
+ .help("Set latest time of time trackings to be shown (use 'now' for current time)"))
+
+ .arg(Arg::with_name("list-not-ended")
+ .short("l")
+ .long("list-not-ended")
+ .takes_value(false)
+ .multiple(false)
+ .required(false)
+ .help("List not yet ended timetrackings even if after 'end-time'"))
+ )
+
+ .subcommand(SubCommand::with_name("start")
+ .about("Start time tracking")
+ .version("0.1")
+ .arg(Arg::with_name("start-time")
+ .index(1)
+ .required(true)
+ .help("Start-time when to start the timetracking (use 'now' for current time)"))
+ .arg(Arg::with_name("tags")
+ .index(2)
+ .required(true)
+ .multiple(true)
+ .help("Tags to start"))
+ )
+
+ .subcommand(SubCommand::with_name("stop")
+ .about("Stop time tracking")
+ .version("0.1")
+ .arg(Arg::with_name("end-time")
+ .index(1)
+ .required(true)
+ .help("End-time when to stop the timetracking (use 'now' for current time)"))
+ .arg(Arg::with_name("tags")
+ .index(2)
+ .required(true)
+ .multiple(true)
+ .help("Tags to stop"))
+ )
+
+ .subcommand(SubCommand::with_name("track")
+ .about("Track time in given range")
+ .version("0.1")
+ .arg(Arg::with_name("start-time")
+ .index(1)
+ .required(true)
+ .help("Start-time when to start the timetracking"))
+ .arg(Arg::with_name("end-time")
+ .index(2)
+ .required(true)
+ .help("End-time when to stop the timetracking"))
+ .arg(Arg::with_name("tags")
+ .index(3)
+ .required(true)
+ .multiple(true)
+ .help("Tags to stop"))
+ )
+
+ .subcommand(SubCommand::with_name("continue")
+ .about("Continue last stopped time tracking")
+ .version("0.1")
+ )
+
+ .subcommand(SubCommand::with_name("day")
+ .about("Print stats about day")
+ .version("0.1")
+ .arg(Arg::with_name("start")
+ .index(1)
+ .required(false)
+ .help("Limit to specific date and time, start time (default: today, 00:00:00)"))
+ .arg(Arg::with_name("end")
+ .index(2)
+ .required(false)
+ .help("Limit to specific date and time, end time (default: today, 23:59:59)"))
+ .arg(Arg::with_name("tags")
+ .long("tags")
+ .short("t")
+ .required(false)
+ .multiple(true)
+ .help("Limit to certain tags"))
+ )
+
+ .subcommand(SubCommand::with_name("week")
+ .about("Print stats about week")
+ .version("0.1")
+ .arg(Arg::with_name("start")
+ .index(1)
+ .required(false)
+ .help("Limit to specific date and time, start time (default: today, 00:00:00)"))
+ .arg(Arg::with_name("end")
+ .index(2)
+ .required(false)
+ .help("Limit to specific date and time, end time (default: today, 23:59:59)"))
+ .arg(Arg::with_name("tags")
+ .long("tags")
+ .short("t")
+ .required(false)
+ .multiple(true)
+ .help("Limit to certain tags"))
+ )
+
+ .subcommand(SubCommand::with_name("month")
+ .about("Print stats about month")
+ .version("0.1")
+ .arg(Arg::with_name("start")
+ .index(1)
+ .required(false)
+ .help("Limit to specific date and time, start time (default: today, 00:00:00)"))
+ .arg(Arg::with_name("end")
+ .index(2)
+ .required(false)
+ .help("Limit to specific date and time, end time (default: today, 23:59:59)"))
+ .arg(Arg::with_name("tags")
+ .long("tags")
+ .short("t")
+ .required(false)
+ .multiple(true)
+ .help("Limit to certain tags"))
+ )
+
+ .subcommand(SubCommand::with_name("year")
+ .about("Print stats about year")
+ .version("0.1")
+ .arg(Arg::with_name("start")
+ .index(1)
+ .required(false)
+ .help("Limit to specific date and time, start time (default: today, 00:00:00)"))
+ .arg(Arg::with_name("end")
+ .index(2)
+ .required(false)
+ .help("Limit to specific date and time, end time (default: today, 23:59:59)"))
+ .arg(Arg::with_name("tags")
+ .long("tags")
+ .short("t")
+ .required(false)
+ .multiple(true)
+ .help("Limit to certain tags"))
+ )
+
+}
diff --git a/imag-timetrack/src/week.rs b/imag-timetrack/src/week.rs
new file mode 100644
index 0000000..4793c4e
--- /dev/null
+++ b/imag-timetrack/src/week.rs
@@ -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 std::cmp::Ord;
+use std::cmp::Ordering;
+use std::str::FromStr;
+
+use filters::ops::not::Not;
+use filters::filter::Filter;
+use itertools::Itertools;
+use itertools::MinMaxResult;
+use chrono::NaiveDateTime;
+
+use libimagerror::trace::trace_error;
+use libimagerror::trace::MapErrTrace;
+use libimagerror::iter::TraceIterator;
+use libimagstore::store::FileLockEntry;
+use libimagentrytimetrack::timetrackingstore::TimeTrackStore;
+use libimagentrytimetrack::timetracking::TimeTracking;
+use libimagentrytimetrack::tag::TimeTrackingTag;
+use libimagentrytimetrack::iter::filter::*;
+
+use libimagrt::runtime::Runtime;
+
+pub fn week(rt: &Runtime) -> i32 {
+ let cmd = rt.cli().subcommand().1.unwrap(); // checked in main
+
+ let filter = {
+ use chrono::offset::Local;
+ use chrono::naive::NaiveDate;
+ use chrono::Weekday;
+ use chrono::Datelike;
+
+ let this_week = Local::now().iso_week();
+
+ let start = match cmd.value_of("start").map(::chrono::naive::NaiveDateTime::from_str) {
+ None => NaiveDate::from_isoywd(this_week.year(), this_week.week(), Weekday::Mon)
+ .and_hms(0, 0, 0),
+ Some(Ok(dt)) => dt,
+ Some(Err(e)) => {
+ trace_error(&e);
+ return 1
+ }
+ };
+
+ let end = match cmd.value_of("end").map(::chrono::naive::NaiveDateTime::from_str) {
+ None => NaiveDate::from_isoywd(this_week.year(), this_week.week(), Weekday::Sun)
+ .and_hms(23, 59, 59),
+ Some(Ok(dt)) => dt,
+ Some(Err(e)) => {
+ trace_error(&e);
+ return 1
+ }
+ };
+
+ let tags = cmd
+ .values_of("tags")
+ .map(|ts| ts.into_iter().map(String::from).map(TimeTrackingTag::from).collect());
+
+ let start_time_filter = has_start_time_where(move |dt: &NaiveDateTime| {
+ start <= *dt
+ });
+
+ let end_time_filter = has_end_time_where(move |dt: &NaiveDateTime| {
+ end >= *dt
+ });
+
+ let tags_filter = move |fle: &FileLockEntry| {
+ match tags {
+ Some(ref tags) => has_one_of_tags(&tags).filter(fle),
+ None => true,
+ }
+ };
+
+ tags_filter.and(start_time_filter).and(end_time_filter)
+ };
+
+ rt.store()
+ .get_timetrackings()
+ .and_then(|iter| {
+ iter.trace_unwrap()
+ .filter(|e| filter.filter(e))
+ .fold(Ok(()), |acc, e| {
+ acc.and_then(|_| {
+ debug!("Processing {:?}", e.get_location());
+
+ let tag = try!(e.get_timetrack_tag());
+ debug!(" -> tag = {:?}", tag);
+
+ let start = try!(e.get_start_datetime());
+ debug!(" -> start = {:?}", start);
+
+ let end = try!(e.get_end_datetime());
+ debug!(" -> end = {:?}", end);
+
+ match (start, end) {
+ (None, _) => println!("{} has no start time.", tag),
+ (Some(s), None) => println!("{} | {} - ...", tag, s),
+ (Some(s), Some(e)) => println!("{} | {} - {}", tag, s, e),
+ }
+
+ Ok(())
+ })
+ })
+ })
+ .map(|_| 0)
+ .map_err_trace()
+ .unwrap_or(1)
+}
+
diff --git a/imag-timetrack/src/year.rs b/imag-timetrack/src/year.rs
new file mode 100644
index 0000000..8534de1
--- /dev/null
+++ b/imag-timetrack/src/year.rs
@@ -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 std::cmp::Ord;
+use std::cmp::Ordering;
+use std::str::FromStr;
+
+use filters::ops::not::Not;
+use filters::filter::Filter;
+use itertools::Itertools;
+use itertools::MinMaxResult;
+use chrono::NaiveDateTime;
+
+use libimagerror::trace::trace_error;
+use libimagerror::trace::MapErrTrace;
+use libimagerror::iter::TraceIterator;
+use libimagstore::store::FileLockEntry;
+use libimagentrytimetrack::timetrackingstore::TimeTrackStore;
+use libimagentrytimetrack::timetracking::TimeTracking;
+use libimagentrytimetrack::tag::TimeTrackingTag;
+use libimagentrytimetrack::iter::filter::*;
+
+use libimagrt::runtime::Runtime;
+
+pub fn year(rt: &Runtime) -> i32 {
+ let cmd = rt.cli().subcommand().1.unwrap(); // checked in main
+
+ let filter = {
+ use chrono::offset::Local;
+ use chrono::naive::NaiveDate;
+ use chrono::Weekday;
+ use chrono::Datelike;
+
+ let now = Local::now();
+
+ let start = match cmd.value_of("start").map(::chrono::naive::NaiveDateTime::from_str) {
+ None => NaiveDate::from_ymd(now.year(), 1, 1).and_hms(0, 0, 0),
+ Some(Ok(dt)) => dt,
+ Some(Err(e)) => {
+ trace_error(&e);
+ return 1
+ }
+ };
+
+ let end = match cmd.value_of("end").map(::chrono::naive::NaiveDateTime::from_str) {
+ None => {
+ NaiveDate::from_ymd(now.year() + 1, 1, 1).and_hms(0, 0, 0)
+ },
+ Some(Ok(dt)) => dt,
+ Some(Err(e)) => {
+ trace_error(&e);
+ return 1
+ }
+ };
+
+ let tags = cmd
+ .values_of("tags")
+ .map(|ts| ts.into_iter().map(String::from).map(TimeTrackingTag::from).collect());
+
+ let start_time_filter = has_start_time_where(move |dt: &NaiveDateTime| {
+ start <= *dt
+ });
+
+ let end_time_filter = has_end_time_where(move |dt: &NaiveDateTime| {
+ end >= *dt
+ });
+
+ let tags_filter = move |fle: &FileLockEntry| {
+ match tags {
+ Some(ref tags) => has_one_of_tags(&tags).filter(fle),
+ None => true,
+ }
+ };
+
+ tags_filter.and(start_time_filter).and(end_time_filter)
+ };
+
+ rt.store()
+ .get_timetrackings()
+ .and_then(|iter| {
+ iter.trace_unwrap()
+ .filter(|e| filter.filter(e))
+ .fold(Ok(()), |acc, e| {
+ acc.and_then(|_| {
+ debug!("Processing {:?}", e.get_location());
+
+ let tag = try!(e.get_timetrack_tag());
+ debug!(" -> tag = {:?}", tag);
+
+ let start = try!(e.get_start_datetime());
+ debug!(" -> start = {:?}", start);
+
+ let end = try!(e.get_end_datetime());
+ debug!(" -> end = {:?}", end);
+
+ match (start, end) {
+ (None, _) => println!("{} has no start time.", tag),
+ (Some(s), None) => println!("{} | {} - ...", tag, s),
+ (Some(s), Some(e)) => println!("{} | {} - {}", tag, s, e),
+ }
+
+ Ok(())
+ })
+ })
+ })
+ .map(|_| 0)
+ .map_err_trace()
+ .unwrap_or(1)
+}
+
diff --git a/libimagentrytimetrack/Cargo.toml b/libimagentrytimetrack/Cargo.toml
index 316af99..02deca7 100644
--- a/libimagentrytimetrack/Cargo.toml
+++ b/libimagentrytimetrack/Cargo.toml
@@ -19,6 +19,7 @@ chrono = "0.4"
toml = "0.4"
toml-query = "0.3"
lazy_static = "0.2"
+is-match = "0.1"
[dependencies.libimagerror]
path = "../libimagerror"
diff --git a/libimagentrytimetrack/src/iter/filter.rs b/libimagentrytimetrack/src/iter/filter.rs
index b8299b4..0a3ae53 100644
--- a/libimagentrytimetrack/src/iter/filter.rs
+++ b/libimagentrytimetrack/src/iter/filter.rs
@@ -19,123 +19,106 @@
use result::Result;
+use chrono::NaiveDateTime;
+use filters::filter::Filter;
+
use libimagstore::store::FileLockEntry;
use tag::TimeTrackingTag as TTT;
use timetracking::TimeTracking;
-pub struct WithOneOf<'a, I>
- where I: Iterator<Item = Result<FileLockEntry<'a>>>
-{
- iter: I,
- allowed_tags: &'a Vec<TTT>,
+
+pub fn has_start_time(entry: &FileLockEntry) -> bool {
+ is_match!(entry.get_start_datetime(), Ok(Some(_)))
}
-impl<'a, I> WithOneOf<'a, I>
- where I: Iterator<Item = Result<FileLockEntry<'a>>>
-{
+pub fn has_end_time(entry: &FileLockEntry) -> bool {
+ is_match!(entry.get_end_datetime(), Ok(Some(_)))
+}
- pub fn new(iter: I, allowed_tags: &'a Vec<TTT>) -> WithOneOf<'a, I> {
- WithOneOf {
- iter: iter,
- allowed_tags: allowed_tags
- }
- }
+pub fn has_tag(entry: &FileLockEntry) -> bool {
+ is_match!(entry.get_timetrack_tag(), Ok(_))
}
-impl<'a, I> Iterator for WithOneOf<'a, I>
- where I: Iterator<Item = Result<FileLockEntry<'a>>>
+pub fn has_start_time_where<F>(f: F) -> HasStartTimeWhere<F>
+ where F: Fn(&NaiveDateTime) -> bool
{
- type Item = Result<FileLockEntry<'a>>;
-
- fn next(&mut self) -> Option<Self::Item> {
- loop {
- match self.iter.next() {
- Some(Ok(fle)) => {
- match fle.get_timetrack_tag() {
- Err(e) => return Some(Err(e)),
- Ok(t) => if self.allowed_tags.contains(&t) {
- return Some(Ok(fle))
- } else {
- // loop
- },
- }
- },
- Some(Err(e)) => return Some(Err(e)),
- None => return None,
- }
- }
- }
+ HasStartTimeWhere::new(f)
}
-pub trait WithOneOfTags<'a> : Sized + Iterator<Item = Result<FileLockEntry<'a>>> {
- fn with_timetracking_tags(self, tags: &'a Vec<TTT>) -> WithOneOf<'a, Self>;
+pub fn has_end_time_where<F>(f: F) -> HasEndTimeWhere<F>
+ where F: Fn(&NaiveDateTime) -> bool
+{
+ HasEndTimeWhere::new(f)
}
-impl<'a, I> WithOneOfTags<'a> for I
- where I: Iterator<Item = Result<FileLockEntry<'a>>>,
- Self: Sized
-{
- fn with_timetracking_tags(self, tags: &'a Vec<TTT>) -> WithOneOf<'a, Self> {
- WithOneOf::new(self, tags)
- }
+pub fn has_one_of_tags<'a>(tags: &'a Vec<TTT>) -> HasOneOfTags<'a> {
+ HasOneOfTags::new(tags)
}
+mod types {
+ use chrono::NaiveDateTime;
+ use filters::filter::Filter;
-pub struct WithNoneOf<'a, I>
- where I: Iterator<Item = Result<FileLockEntry<'a>>>
-{
- iter: I,
- disallowed_tags: &'a Vec<TTT>,
-}
+ use tag::TimeTrackingTag as TTT;
+ use timetracking::TimeTracking;
-impl<'a, I> WithNoneOf<'a, I>
- where I: Iterator<Item = Result<FileLockEntry<'a>>>
-{
+ use libimagstore::store::FileLockEntry;
- pub fn new(iter: I, disallowed_tags: &'a Vec<TTT>) -> WithNoneOf<'a, I> {
- WithNoneOf {
- iter: iter,
- disallowed_tags: disallowed_tags
+ pub struct HasStartTimeWhere<F>(F)
+ where F: Fn(&NaiveDateTime) -> bool;
+
+ impl<F: Fn(&NaiveDateTime) -> bool> HasStartTimeWhere<F> {
+ pub fn new(f: F) -> HasStartTimeWhere<F> {
+ HasStartTimeWhere(f)
}
}
-}
-impl<'a, I> Iterator for WithNoneOf<'a, I>
- where I: Iterator<Item = Result<FileLockEntry<'a>>>
-{
- type Item = Result<FileLockEntry<'a>>;
-
- fn next(&mut self) -> Option<Self::Item> {
- loop {
- match self.iter.next() {
- Some(Ok(fle)) => {
- match fle.get_timetrack_tag() {
- Err(e) => return Some(Err(e)),
- Ok(t) => if !self.disallowed_tags.contains(&t) {
- return Some(Ok(fle))
- } else {
- // loop
- },
- }
- },
- Some(Err(e)) => return Some(Err(e)),
- None => return None,
- }
+ impl<'a, F> Filter<FileLockEntry<'a>> for HasStartTimeWhere<F>
+ where F: Fn(&NaiveDateTime) -> bool
+ {
+ fn filter(&self, entry: &FileLockEntry) -> bool {
+ entry.get_start_datetime()
+ .map(|o| o.map(|dt| (self.0)(&dt)).unwrap_or(false))
+ .unwrap_or(false)
}
}
-}
-pub trait WithNoneOfTags<'a> : Sized + Iterator<Item = Result<FileLockEntry<'a>>> {
- fn without_timetracking_tags(self, tags: &'a Vec<TTT>) -> WithNoneOf<'a, Self>;
-}
+ pub struct HasEndTimeWhere<F>(F)
+ where F: Fn(&NaiveDateTime) -> bool;
-impl<'a, I> WithNoneOfTags<'a> for I
- where I: Iterator<Item = Result<FileLockEntry<'a>>>,
- Self: Sized
-{
- fn without_timetracking_tags(self, tags: &'a Vec<TTT>) -> WithNoneOf<'a, Self> {
- WithNoneOf::new(self, tags)
+ impl<F: Fn(&NaiveDateTime) -> bool> HasEndTimeWhere<F> {
+ pub fn new(f: F) -> HasEndTimeWhere<F> {
+ HasEndTimeWhere(f)
+ }
+ }
+
+ impl<'a, F> Filter<FileLockEntry<'a>> for HasEndTimeWhere<F>
+ where F: Fn(&NaiveDateTime) -> bool
+ {
+ fn filter(&self, entry: &FileLockEntry) -> bool {
+ entry.get_end_datetime()
+ .map(|o| o.map(|dt| (self.0)(&dt)).unwrap_or(false))
+ .unwrap_or(false)
+ }
}
+
+ pub struct HasOneOfTags<'a>(&'a Vec<TTT>);
+
+ impl<'a> HasOneOfTags<'a> {
+ pub fn new(tags: &'a Vec<TTT>) -> HasOneOfTags<'a> {
+ HasOneOfTags(tags)
+ }
+ }
+
+ impl<'a, 'b> Filter<FileLockEntry<'b>> for HasOneOfTags<'a> {
+ fn filter(&self, entry: &FileLockEntry) -> bool {
+ entry.get_timetrack_tag().map(|t| self.0.contains(&t)).unwrap_or(false)
+ }
+ }
+
}
+pub use self::types::HasStartTimeWhere;
+pub use self::types::HasEndTimeWhere;
+pub use self::types::HasOneOfTags;
diff --git a/libimagentrytimetrack/src/lib.rs b/libimagentrytimetrack/src/lib.rs
index 0334622..e3f9d2a 100644
--- a/libimagentrytimetrack/src/lib.rs
+++ b/libimagentrytimetrack/src/lib.rs
@@ -23,6 +23,8 @@ extern crate toml;
extern crate toml_query;
#[macro_use]
extern crate lazy_static;
+#[macro_use]
+extern crate is_match;
#[macro_use]
extern crate libimagerror;
diff --git a/libimagentrytimetrack/src/tag.rs b/libimagentrytimetrack/src/tag.rs
index ecf9c15..df2f979 100644
--- a/libimagentrytimetrack/src/tag.rs
+++ b/libimagentrytimetrack/src/tag.rs
@@ -17,6 +17,9 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
+use std::fmt::Display;
+use std::fmt::Error as FmtError;
+use std::fmt::Formatter;
use std::path::PathBuf;
use libimagstore::store::Result as StoreResult;
@@ -34,6 +37,12 @@ impl TimeTrackingTag {
}
}
+impl Display for TimeTrackingTag {
+ fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
+ self.0.fmt(f)
+ }
+}
+
impl Into<String> for TimeTrackingTag {
fn into(self) -> String {
self.0
diff --git a/libimagentrytimetrack/src/timetracking.rs b/libimagentrytimetrack/src/timetracking.rs
index 625238d..ba40ac6 100644
--- a/libimagentrytimetrack/src/timetracking.rs
+++ b/libimagentrytimetrack/src/timetracking.rs
@@ -66,9 +66,10 @@ impl TimeTracking for Entry {
self.get_header()
.read(DATE_TIME_TAG_HEADER_PATH)
.map_err_into(TTEK::HeaderReadError)
- .map(|value| match value {
- Some(&Value::String(ref s)) => s.clone().into(),
- _ => unimplemented!(),
+ .and_then(|value| match value {
+ Some(&Value::String(ref s)) => Ok(s.clone().into()),
+ Some(_) => Err(TTEK::HeaderFieldTypeError.into_error()),
+ _ => Err(TTEK::HeaderReadError.into_error())
})
}
diff --git a/libimagentrytimetrack/src/timetrackingstore.rs b/libimagentrytimetrack/src/timetrackingstore.rs
index 9312a1c..94e993a 100644
--- a/libimagentrytimetrack/src/timetrackingstore.rs
+++ b/libimagentrytimetrack/src/timetrackingstore.rs
@@ -44,7 +44,7 @@ pub trait TimeTrackStore<'a> {
fn create_timetracking_at(&'a self, start: &NDT, ts: &TTT) -> Result<FileLockEntry<'a>>;
fn create_timetracking(&'a self, start: &NDT, end: &NDT, ts: &TTT) -> Result<FileLockEntry<'a>>;
- fn get_timetrackings<I>(&'a self) -> Result<GetTimeTrackIter<'a>>;
+ fn get_timetrackings(&'a self) -> Result<GetTimeTrackIter<'a>>;
}
fn now() -> NDT {
@@ -104,7 +104,7 @@ impl<'a> TimeTrackStore<'a> for Store {
})
}
- fn get_timetrackings<I>(&'a self) -> Result<GetTimeTrackIter<'a>> {
+ fn get_timetrackings(&'a self) -> Result<GetTimeTrackIter<'a>> {
self.retrieve_for_module(CRATE_NAME)
.map_err_into(TTEK::StoreReadError)
.map(|iter| GetTimeTrackIter::new(iter, self))