use chrono::NaiveDateTime;
use prettytable::Table;
use prettytable::Row;
use prettytable::Cell;
use kairos::parser::Parsed;
use kairos::parser::parse as kairos_parse;
use clap::ArgMatches;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::Error;
use resiter::Filter;
use resiter::AndThen;
use resiter::Map;
use libimagstore::store::FileLockEntry;
use libimagtimetrack::timetracking::TimeTracking;
use libimagtimetrack::store::TimeTrackStore;
use libimagrt::runtime::Runtime;
pub fn list(rt: &Runtime) -> Result<()> {
let (_, cmd) = rt.cli().subcommand();
let cmd = cmd.unwrap();
let gettime = |cmd: &ArgMatches, name| {
match cmd.value_of(name).map(kairos_parse) {
Some(Ok(Parsed::TimeType(tt))) => {
let tt = tt
.calculate()
.context(format_err!("Failed to calculate date from '{}'", cmd.value_of(name).unwrap()))?;
Ok(tt.get_moment().cloned())
},
Some(Ok(Parsed::Iterator(_))) => {
Err(format_err!("Expected single point in time, got '{}', which yields a list of dates", cmd.value_of(name).unwrap()))
},
Some(Err(e)) => Err(e),
None => Ok(None),
}
};
let start = gettime(&cmd, "start-time")?;
let end = gettime(&cmd, "end-time")?;
let list_not_ended = cmd.is_present("list-not-ended");
let show_duration = cmd.is_present("show-duration");
list_impl(rt, start, end, list_not_ended, show_duration)
}
pub fn list_impl(rt: &Runtime,
start: Option<NaiveDateTime>,
end: Option<NaiveDateTime>,
list_not_ended: bool,
show_duration: bool)
-> Result<()>
{
use filters::failable::filter::FailableFilter;
let start_time_filter = |timetracking: &FileLockEntry| -> Result<bool> {
start.map(|s| match timetracking.get_start_datetime()? {
Some(dt) => Ok(dt >= s),
None => {
warn!("Funny things are happening: Timetracking has no start time");
Ok(false)
}
})
.unwrap_or(Ok(true))
};
let end_time_filter = |timetracking: &FileLockEntry| -> Result<bool> {
end.map(|s| match timetracking.get_end_datetime()? {
Some(dt) => Ok(dt <= s),
None => Ok(list_not_ended),
})
.unwrap_or(Ok(true))
};
let filter = start_time_filter.and(end_time_filter);
let mut table = Table::new();
let title_row = if !show_duration {
Row::new(["Tag", "Start", "End"].iter().map(|s| Cell::new(s)).collect())
} else {
Row::new(["Tag", "Start", "End", "Duration"].iter().map(|s| Cell::new(s)).collect())
};
table.set_titles(title_row);
let table_empty = rt.store()
.get_timetrackings()?
.and_then_ok(|e| filter.filter(&e).map(|b| (b, e)))
.filter_ok(|tpl| tpl.0)
.map_ok(|tpl| tpl.1)
.and_then_ok(|e| {
debug!("Processing {:?}", e.get_location());
let tag = e.get_timetrack_tag()?;
debug!(" -> tag = {:?}", tag);
let start = e.get_start_datetime()?;
debug!(" -> start = {:?}", start);
let end = e.get_end_datetime()?;
debug!(" -> end = {:?}", end);
let v = match (start, end) {
(None, _) => {
let mut v = vec![String::from(tag.as_str()), String::from(""), String::from("")];
if show_duration {
v.push(String::from(""));
}
v
},
(Some(s), None) => {
let mut v = vec![
String::from(tag.as_str()),
format!("{}", s),
String::from(""),
];
if show_duration {
v.push(String::from(""));
}
v
},
(Some(s), Some(e)) => {
let mut v = vec![
String::from(tag.as_str()),
format!("{}", s),
format!("{}", e),
];
if show_duration {
let duration = e - s;
let dur = format!("{days} Days, {hours} Hours, {minutes} Minutes, {seconds} Seconds",
days = duration.num_days(),
hours = duration.num_hours(),
minutes = duration.num_minutes(),
seconds = duration.num_seconds());
v.push(dur);
}
v
},
};
let cells : Vec<Cell> = v.iter().map(|s| Cell::new(s)).collect();
table.add_row(Row::new(cells));
rt.report_touched(e.get_location())?;
Ok(false)
})
.collect::<Result<Vec<bool>>>()?
.iter()
.any(|b| !b);
if !table_empty {
table.print(&mut rt.stdout())
.context("Failed to print table")
.map_err(Error::from)
.map(|_| ())
} else {
Ok(())
}
}