current time management across commands

This commit is contained in:
Abraham Toriz 2021-07-16 12:45:27 -05:00
parent da124869ca
commit f62ebef762
No known key found for this signature in database
GPG Key ID: D5B4A746DB5DD42A
15 changed files with 201 additions and 121 deletions

Binary file not shown.

View File

@ -2,6 +2,7 @@ use std::convert::TryFrom;
use std::io::Write;
use clap::ArgMatches;
use chrono::{DateTime, Utc};
use crate::error::Result;
use crate::database::Database;
@ -19,5 +20,5 @@ pub mod list;
pub trait Command<'a> {
type Args: TryFrom<&'a ArgMatches<'a>>;
fn handle<D: Database, O: Write, E: Write>(args: Self::Args, db: &mut D, out: &mut O, err: &mut E, config: &Config) -> Result<()>;
fn handle<D: Database, O: Write, E: Write>(args: Self::Args, db: &mut D, out: &mut O, err: &mut E, config: &Config, now: DateTime<Utc>) -> Result<()>;
}

View File

@ -3,17 +3,16 @@ use std::io::Write;
use std::str::FromStr;
use clap::ArgMatches;
use chrono::{DateTime, Utc, Local, LocalResult, TimeZone};
use ansi_term::Color::Yellow;
use chrono::{DateTime, Utc};
use regex::Regex;
use crate::error::{Result, Error};
use crate::database::{Database, DBVersion};
use crate::database::Database;
use crate::formatters::Formatter;
use crate::config::Config;
use crate::timeparse::parse_time;
use crate::models::Entry;
use crate::regex::parse_regex;
use crate::old::{entries_or_warning, warn_if_needed};
use super::Command;
@ -21,33 +20,6 @@ use super::Command;
// Things that are general to all commands that display in some way
// ----------------------------------------------------------------
fn local_to_utc(t: DateTime<Utc>) -> Result<DateTime<Utc>> {
let local_time = match Local.from_local_datetime(&t.naive_utc()) {
LocalResult::None => return Err(Error::NoneLocalTime(t.naive_utc().to_string())),
LocalResult::Single(t) => t,
LocalResult::Ambiguous(t1, t2) => return Err(Error::AmbiguousLocalTime {
orig: t.naive_utc().to_string(),
t1: t1.naive_local(),
t2: t2.naive_local(),
}),
};
Ok(Utc.from_utc_datetime(&local_time.naive_utc()))
}
fn local_to_utc_vec(entries: Vec<Entry>) -> Result<Vec<Entry>> {
entries
.into_iter()
.map(|e| {
Ok(Entry {
start: local_to_utc(e.start)?,
end: e.end.map(|t| local_to_utc(t)).transpose()?,
..e
})
})
.collect()
}
pub enum Sheet {
All,
Full,
@ -70,6 +42,7 @@ pub fn entries_for_display<D, O, E>(
start: Option<DateTime<Utc>>, end: Option<DateTime<Utc>>,
sheet: Option<Sheet>, db: &mut D, out: &mut O, err: &mut E,
format: Formatter, ids: bool, grep: Option<Regex>,
now: DateTime<Utc>,
) -> Result<()>
where
D: Database,
@ -91,30 +64,16 @@ where
entries.retain(|e| re.is_match(e.note.as_ref().unwrap_or(&String::new())));
}
let (entries, needs_warning) = if let DBVersion::Timetrap = db.version()? {
// this indicates that times in the database are specified in the
// local time and need to be converted to utc before displaying
(local_to_utc_vec(entries)?, true)
} else {
(entries, false)
};
let (entries, needs_warning) = entries_or_warning(entries, db)?;
format.print_formatted(
entries,
out,
Utc::now(),
now,
ids,
)?;
if needs_warning {
writeln!(
err,
"{} You are using the old timetrap format, it is advised that \
you update your database using t migrate",
Yellow.bold().paint("[WARNING]"),
)?;
}
warn_if_needed(err, needs_warning)?;
Ok(())
}
@ -153,13 +112,13 @@ pub struct DisplayCommand { }
impl<'a> Command<'a> for DisplayCommand {
type Args = Args;
fn handle<D, O, E>(args: Self::Args, db: &mut D, out: &mut O, err: &mut E, _config: &Config) -> Result<()>
fn handle<D, O, E>(args: Self::Args, db: &mut D, out: &mut O, err: &mut E, _config: &Config, now: DateTime<Utc>) -> Result<()>
where
D: Database,
O: Write,
E: Write,
{
entries_for_display(args.start, args.end, args.sheet, db, out, err, args.format, args.ids, args.grep)
entries_for_display(args.start, args.end, args.sheet, db, out, err, args.format, args.ids, args.grep, now)
}
}
@ -183,7 +142,7 @@ mod tests {
let config = Default::default();
std::env::set_var("TZ", "UTC");
DisplayCommand::handle(args, &mut db, &mut out, &mut err, &config).unwrap();
DisplayCommand::handle(args, &mut db, &mut out, &mut err, &config, Utc::now()).unwrap();
assert_eq!(PrettyString(&String::from_utf8_lossy(&out)), PrettyString("Timesheet: default
Day Start End Duration Notes
@ -222,7 +181,7 @@ mod tests {
db.entry_insert(Utc.ymd(2021, 6, 30).and_hms(10, 0, 0), None, Some("hola".into()), "default".into()).unwrap();
db.entry_insert(Utc.ymd(2021, 6, 30).and_hms(10, 10, 0), None, Some("hola".into()), "default".into()).unwrap();
DisplayCommand::handle(args, &mut db, &mut out, &mut err, &config).unwrap();
DisplayCommand::handle(args, &mut db, &mut out, &mut err, &config, Utc::now()).unwrap();
assert_eq!(PrettyString(&String::from_utf8_lossy(&out)), PrettyString("start,end,note,sheet
2021-06-30T10:10:00Z,,hola,default
@ -245,7 +204,7 @@ mod tests {
db.entry_insert(Utc.ymd(2021, 6, 30).and_hms(10, 0, 0), None, Some("hola".into()), "default".into()).unwrap();
db.entry_insert(Utc.ymd(2021, 6, 30).and_hms(10, 10, 0), None, Some("adios".into()), "default".into()).unwrap();
entries_for_display(None, None, None, &mut db, &mut out, &mut err, Formatter::Csv, true, Some("io".parse().unwrap())).unwrap();
entries_for_display(None, None, None, &mut db, &mut out, &mut err, Formatter::Csv, true, Some("io".parse().unwrap()), Utc::now()).unwrap();
assert_eq!(PrettyString(&String::from_utf8_lossy(&out)), PrettyString("id,start,end,note,sheet
2,2021-06-30T10:10:00Z,,adios,default

View File

@ -32,13 +32,13 @@ pub struct InCommand {}
impl<'a> Command<'a> for InCommand {
type Args = Args;
fn handle<D, O, E>(args: Args, db: &mut D, _out: &mut O, _err: &mut E, config: &Config) -> error::Result<()>
fn handle<D, O, E>(args: Args, db: &mut D, _out: &mut O, _err: &mut E, config: &Config, now: DateTime<Utc>) -> error::Result<()>
where
D: Database,
O: Write,
E: Write,
{
let start = args.at.unwrap_or(Utc::now());
let start = args.at.unwrap_or(now);
let sheet = db.current_sheet()?.unwrap_or("default".into());
let note = if let Some(note) = args.note {
note
@ -71,7 +71,7 @@ mod tests {
assert!(false, "there are no entries");
InCommand::handle(args, &mut d, &mut out, &mut err, &Default::default()).unwrap();
InCommand::handle(args, &mut d, &mut out, &mut err, &Default::default(), Utc::now()).unwrap();
assert!(false, "there is one entry");
}
@ -89,7 +89,7 @@ mod tests {
assert!(false, "there are no entries");
InCommand::handle(args, &mut d, &mut out, &mut err, &Default::default()).unwrap();
InCommand::handle(args, &mut d, &mut out, &mut err, &Default::default(), Utc::now()).unwrap();
assert!(false, "there are still no entries");
assert!(false, "a message is issued to the user warning a running entry in this sheet");

View File

@ -2,7 +2,7 @@ use std::convert::TryFrom;
use std::io::Write;
use clap::ArgMatches;
use chrono::{Utc, Duration, Local};
use chrono::{DateTime, Utc, Duration, Local};
use itertools::Itertools;
use crate::error::{Error, Result};
@ -10,6 +10,8 @@ use crate::database::Database;
use crate::config::Config;
use crate::tabulate::{Tabulate, Col, Align::*};
use crate::formatters::text::format_duration;
use crate::models::Entry;
use crate::old::{entries_or_warning, warn_if_needed};
use super::Command;
@ -33,27 +35,40 @@ pub struct ListCommand {}
impl<'a> Command<'a> for ListCommand {
type Args = Args;
fn handle<D, O, E>(args: Args, db: &mut D, out: &mut O, _err: &mut E, _config: &Config) -> Result<()>
fn handle<D, O, E>(args: Args, db: &mut D, out: &mut O, err: &mut E, _config: &Config, now: DateTime<Utc>) -> Result<()>
where
D: Database,
O: Write,
E: Write,
{
// no sheet, list sheets
let now = Utc::now();
let today = Local::now().date().and_hms(0, 0, 0).with_timezone(&Utc);
let mut sheets = if args.all {
let today = now.with_timezone(&Local).date().and_hms(0, 0, 0).with_timezone(&Utc);
let entries = if args.all {
db.entries_full(None, None)?
} else {
db.entries_all_visible(None, None)?
};
let current = db.current_sheet()?.unwrap_or("".into());
let last = db.last_sheet()?.unwrap_or("".into());
let (mut entries, needs_warning) = entries_or_warning(entries, db)?;
sheets.sort_unstable_by_key(|e| e.sheet.clone());
let current = db.current_sheet()?;
let last = db.last_sheet()?;
let sheets: Vec<_> = sheets
// introducte two fake entries to make both current and last show up
if let Some(ref current) = current {
entries.push(Entry {
id: 1, sheet: current.clone(), start: now, end: Some(now), note: None,
});
}
if let Some(ref last) = last {
entries.push(Entry {
id: 2, sheet: last.clone(), start: now, end: Some(now), note: None,
});
}
entries.sort_unstable_by_key(|e| e.sheet.clone());
let sheets: Vec<_> = entries
.into_iter()
.group_by(|e| e.sheet.clone())
.into_iter()
@ -61,15 +76,15 @@ impl<'a> Command<'a> for ListCommand {
let entries: Vec<_> = group.into_iter().collect();
(
if current == key {
if current.as_ref() == Some(&key) {
"*".into()
} else {
if last == key { "-".into() } else { "".into() }
if last.as_ref() == Some(&key) { "-".into() } else { "".into() }
},
key,
format_duration(
now - entries.iter().filter(|e| e.end.is_none()).next().map(|e| e.end).flatten().unwrap_or(now)
now - entries.iter().filter(|e| e.end.is_none()).next().map(|e| e.start).unwrap_or(now)
),
format_duration(
@ -112,6 +127,8 @@ impl<'a> Command<'a> for ListCommand {
out.write_all(tabs.print().as_bytes())?;
warn_if_needed(err, needs_warning)?;
Ok(())
}
}
@ -120,6 +137,7 @@ impl<'a> Command<'a> for ListCommand {
mod tests {
use chrono::{Utc, TimeZone};
use pretty_assertions::assert_eq;
use ansi_term::Color::Yellow;
use crate::database::{SqliteDatabase, Database};
use crate::test_utils::PrettyString;
@ -128,6 +146,8 @@ mod tests {
#[test]
fn list_sheets() {
std::env::set_var("TZ", "UTC");
let args = Default::default();
let mut db = SqliteDatabase::from_memory().unwrap();
let mut out = Vec::new();
@ -135,38 +155,65 @@ mod tests {
let config = Default::default();
db.init().unwrap();
db.set_current_sheet("sheet2").unwrap();
db.set_last_sheet("sheet4").unwrap();
db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(0, 0, 0), None, None, "_archived".into()).unwrap();
db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(0, 0, 0), None, None, "sheet1".into()).unwrap();
db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(0, 0, 0), None, None, "sheet2".into()).unwrap();
db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(0, 0, 0), None, None, "sheet3".into()).unwrap();
db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(0, 0, 0), Some(Utc.ymd(2021, 1, 1).and_hms(1, 0, 0)), None, "_archived".into()).unwrap();
db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(0, 0, 0), Some(Utc.ymd(2021, 1, 1).and_hms(10,13, 55)), None, "sheet1".into()).unwrap();
db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(0, 0, 0), Some(Utc.ymd(2021, 1, 1).and_hms(7, 39, 18)), None, "sheet3".into()).unwrap();
db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(12, 0, 0), Some(Utc.ymd(2021, 1, 1).and_hms(13, 52, 45)), None, "sheet3".into()).unwrap();
db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(12, 0, 0), None, None, "sheet4".into()).unwrap();
ListCommand::handle(args, &mut db, &mut out, &mut err, &config).unwrap();
let now = Utc.ymd(2021, 1, 1).and_hms(13, 52, 45);
ListCommand::handle(args, &mut db, &mut out, &mut err, &config, now).unwrap();
assert_eq!(PrettyString(&String::from_utf8_lossy(&out)), PrettyString(" Timesheet Running Today Total Time
sheet1 0:00:00 0:00:00 10:13:55
* sheet2 0:00:00 0:00:00 0:00:00
sheet3 0:00:00 1:52:45 9:32:03
- sheet4 1:52:45 1:52:45 1:52:45
"));
// now show all the sheets
let mut out = Vec::new();
let mut err = Vec::new();
let args = Args {
all: true,
};
ListCommand::handle(args, &mut db, &mut out, &mut err, &config, now).unwrap();
assert_eq!(PrettyString(&String::from_utf8_lossy(&out)), PrettyString(" Timesheet Running Today Total Time
_archived 0:00:00 0:00:00 1:00:00
sheet1 0:00:00 0:00:00 10:13:55
* sheet2 0:00:00 0:00:00 0:00:00
sheet3 0:00:00 1:52:45 9:32:03
- sheet4 1:52:45 1:52:45 1:52:45
"));
}
#[test]
fn show_all_sheets() {
assert!(false);
}
fn old_database() {
let args = Default::default();
let mut db = SqliteDatabase::from_path("assets/test_list_old_database.db").unwrap();
let mut out = Vec::new();
let mut err = Vec::new();
let config = Default::default();
#[test]
fn show_warning_if_old_database() {
assert!(false);
}
let now = Local.ymd(2021, 7, 16).and_hms(11, 30, 45);
#[test]
fn list_sheets_shows_last() {
assert!(false);
}
ListCommand::handle(args, &mut db, &mut out, &mut err, &config, now.with_timezone(&Utc)).unwrap();
#[test]
fn list_sheets_shows_running() {
assert!(false);
assert_eq!(PrettyString(&String::from_utf8_lossy(&out)), PrettyString(" Timesheet Running Today Total Time
* default 0:10:24 0:10:26 0:10:26
"));
assert_eq!(
String::from_utf8_lossy(&err),
format!("{} You are using the old timetrap format, it is advised that you update your database using t migrate\n", Yellow.bold().paint("[WARNING]")),
);
}
}

View File

@ -93,18 +93,16 @@ pub struct MonthCommand { }
impl<'a> Command<'a> for MonthCommand {
type Args = Args;
fn handle<D, O, E>(args: Self::Args, db: &mut D, out: &mut O, err: &mut E, _config: &Config) -> Result<()>
fn handle<D, O, E>(args: Self::Args, db: &mut D, out: &mut O, err: &mut E, _config: &Config, now: DateTime<Utc>) -> Result<()>
where
D: Database,
O: Write,
E: Write,
{
let now = Local::now();
let (start, end) = match args.month {
MonthSpec::This => (beginning_of_month(now), Utc::now()),
MonthSpec::This => (beginning_of_month(now.with_timezone(&Local)), now),
MonthSpec::Last => {
(beginning_of_previous_month(now), beginning_of_month(now))
(beginning_of_previous_month(now.with_timezone(&Local)), beginning_of_month(now.with_timezone(&Local)))
},
MonthSpec::Month(month) => {
if month < now.month() {
@ -131,6 +129,6 @@ impl<'a> Command<'a> for MonthCommand {
},
};
entries_for_display(Some(start), Some(end), args.sheet, db, out, err, args.format, args.ids, args.grep)
entries_for_display(Some(start), Some(end), args.sheet, db, out, err, args.format, args.ids, args.grep, now)
}
}

View File

@ -2,6 +2,7 @@ use std::convert::TryFrom;
use std::io::Write;
use clap::ArgMatches;
use chrono::{DateTime, Utc};
use crate::database::Database;
use crate::error::{Error, Result};
@ -29,7 +30,7 @@ pub struct SheetCommand {}
impl<'a> Command<'a> for SheetCommand {
type Args = Args;
fn handle<D, O, E>(args: Args, db: &mut D, out: &mut O, err: &mut E, config: &Config) -> Result<()>
fn handle<D, O, E>(args: Args, db: &mut D, out: &mut O, err: &mut E, config: &Config, now: DateTime<Utc>) -> Result<()>
where
D: Database,
O: Write,
@ -40,7 +41,7 @@ impl<'a> Command<'a> for SheetCommand {
unimplemented!()
} else {
// call list
ListCommand::handle(Default::default(), db, out, err, config)
ListCommand::handle(Default::default(), db, out, err, config, now)
}
}
}

View File

@ -42,14 +42,14 @@ pub struct TodayCommand { }
impl<'a> Command<'a> for TodayCommand {
type Args = Args;
fn handle<D, O, E>(args: Self::Args, db: &mut D, out: &mut O, err: &mut E, _config: &Config) -> Result<()>
fn handle<D, O, E>(args: Self::Args, db: &mut D, out: &mut O, err: &mut E, _config: &Config, now: DateTime<Utc>) -> Result<()>
where
D: Database,
O: Write,
E: Write,
{
let start = Some(Local::now().date().and_hms(0, 0, 0).with_timezone(&Utc));
let start = Some(now.with_timezone(&Local).date().and_hms(0, 0, 0).with_timezone(&Utc));
entries_for_display(start, args.end, args.sheet, db, out, err, args.format, args.ids, args.grep)
entries_for_display(start, args.end, args.sheet, db, out, err, args.format, args.ids, args.grep, now)
}
}

View File

@ -86,15 +86,15 @@ pub struct WeekCommand { }
impl<'a> Command<'a> for WeekCommand {
type Args = Args;
fn handle<D, O, E>(args: Self::Args, db: &mut D, out: &mut O, err: &mut E, config: &Config) -> Result<()>
fn handle<D, O, E>(args: Self::Args, db: &mut D, out: &mut O, err: &mut E, config: &Config, now: DateTime<Utc>) -> Result<()>
where
D: Database,
O: Write,
E: Write,
{
let start = prev_day(Local::now(), config.week_start);
let start = prev_day(now.with_timezone(&Local), config.week_start);
entries_for_display(Some(start), args.end, args.sheet, db, out, err, args.format, args.ids, args.grep)
entries_for_display(Some(start), args.end, args.sheet, db, out, err, args.format, args.ids, args.grep, now)
}
}

View File

@ -2,7 +2,7 @@ use std::convert::TryFrom;
use std::io::Write;
use clap::ArgMatches;
use chrono::{Utc, Local, Duration};
use chrono::{DateTime, Utc, Local, Duration};
use regex::Regex;
use crate::error::{Result, Error};
@ -39,16 +39,17 @@ pub struct YesterdayCommand { }
impl<'a> Command<'a> for YesterdayCommand {
type Args = Args;
fn handle<D, O, E>(args: Self::Args, db: &mut D, out: &mut O, err: &mut E, _config: &Config) -> Result<()>
fn handle<D, O, E>(args: Self::Args, db: &mut D, out: &mut O, err: &mut E, _config: &Config, now: DateTime<Utc>) -> Result<()>
where
D: Database,
O: Write,
E: Write,
{
let start = Some((Local::today() - Duration::days(1)).and_hms(0, 0, 0).with_timezone(&Utc));
let end = Some(Local::today().and_hms(0, 0, 0).with_timezone(&Utc));
let today = now.with_timezone(&Local).date();
let start = Some((today - Duration::days(1)).and_hms(0, 0, 0).with_timezone(&Utc));
let end = Some(today.and_hms(0, 0, 0).with_timezone(&Utc));
entries_for_display(start, end, args.sheet, db, out, err, args.format, args.ids, args.grep)
entries_for_display(start, end, args.sheet, db, out, err, args.format, args.ids, args.grep, now)
}
}
@ -83,7 +84,7 @@ mod tests {
db.entry_insert(yesterday.and_hms(1, 2, 3).with_timezone(&Utc), None, Some("This!".into()), "default".into()).unwrap();
db.entry_insert(today.and_hms(1, 2, 3).with_timezone(&Utc), None, None, "default".into()).unwrap();
YesterdayCommand::handle(args, &mut db, &mut out, &mut err, &config).unwrap();
YesterdayCommand::handle(args, &mut db, &mut out, &mut err, &config, Utc::now()).unwrap();
assert_eq!(PrettyString(&String::from_utf8_lossy(&out)), PrettyString(&format!("start,end,note,sheet
{},,This!,default

View File

@ -159,6 +159,12 @@ pub trait Database {
Ok(())
}
fn set_last_sheet(&mut self, sheet: &str) -> Result<()> {
self.execute("INSERT INTO meta (key, value) VALUES ('last_sheet', ?1)", &[&sheet])?;
Ok(())
}
fn version(&self) -> Result<DBVersion> {
let results = self.meta_query("select * from meta where key='database_version'", &[])?;

View File

@ -2,7 +2,7 @@ use std::io::Write;
use itertools::Itertools;
use chrono::{
DateTime, Utc, TimeZone, Duration, Local, NaiveTime, Timelike,
DateTime, Utc, Duration, Local, NaiveTime, Timelike,
NaiveDateTime,
};
@ -58,7 +58,7 @@ pub fn print_formatted<W: Write>(entries: Vec<Entry>, out: &mut W, now: DateTime
]);
}
let entries_by_date = group.group_by(|e| Local.from_utc_datetime(&e.start.naive_utc()).date());
let entries_by_date = group.group_by(|e| e.start.with_timezone(&Local).date());
let mut total = Duration::seconds(0);
for (date, entries) in entries_by_date.into_iter() {
@ -66,11 +66,11 @@ pub fn print_formatted<W: Write>(entries: Vec<Entry>, out: &mut W, now: DateTime
for (i, entry) in entries.into_iter().enumerate() {
let startend = format!("{} - {}",
format_start(Local.from_utc_datetime(&entry.start.naive_utc()).time()),
format_start(entry.start.with_timezone(&Local).time()),
entry.end.map(|t| {
format_end(
entry.start.with_timezone(&Local).naive_local(),
Local.from_utc_datetime(&t.naive_utc()).naive_local()
t.with_timezone(&Local).naive_local()
)
}).unwrap_or("".into())
);
@ -107,6 +107,7 @@ pub fn print_formatted<W: Write>(entries: Vec<Entry>, out: &mut W, now: DateTime
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use chrono::TimeZone;
use crate::test_utils::PrettyString;
@ -165,6 +166,7 @@ mod tests {
#[test]
fn test_text_output_long_duration() {
std::env::set_var("TZ", "UTC");
let mut output = Vec::new();
let entries = vec![
Entry::new_sample(1, Utc.ymd(2008, 10, 1).and_hms(12, 0, 0), Some(Utc.ymd(2008, 10, 3).and_hms(14, 0, 0))),
@ -188,6 +190,7 @@ mod tests {
#[test]
fn test_text_output_with_ids() {
std::env::set_var("TZ", "UTC");
let mut output = Vec::new();
let entries = vec![
Entry::new_sample(1, Utc.ymd(2008, 10, 3).and_hms(12, 0, 0), Some(Utc.ymd(2008, 10, 3).and_hms(14, 0, 0))),
@ -215,6 +218,7 @@ mod tests {
#[test]
fn test_text_output_long_note_with_ids() {
std::env::set_var("TZ", "UTC");
let mut output = Vec::new();
let entries = vec![
Entry {
@ -248,6 +252,7 @@ mod tests {
#[test]
fn test_text_output_note_with_line_breaks() {
std::env::set_var("TZ", "UTC");
let mut output = Vec::new();
let entries = vec![
Entry {

View File

@ -11,6 +11,7 @@ pub mod models;
pub mod timeparse;
pub mod regex;
pub mod tabulate;
pub mod old;
#[cfg(test)]
pub mod test_utils;

View File

@ -3,6 +3,7 @@ use std::process::exit;
use std::io;
use clap::{App, Arg, SubCommand, AppSettings, crate_version, ArgMatches};
use chrono::Utc;
use tiempo::error;
use tiempo::database::SqliteDatabase;
@ -19,18 +20,19 @@ fn error_trap(matches: ArgMatches) -> error::Result<()> {
let mut conn = SqliteDatabase::from_path_or_create(&config.database_file)?;
let mut out = io::stdout();
let mut err = io::stderr();
let now = Utc::now();
match matches.subcommand() {
("in", Some(matches)) => InCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config),
("in", Some(matches)) => InCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config, now),
("display", Some(matches)) => DisplayCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config),
("today", Some(matches)) => TodayCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config),
("yesterday", Some(matches)) => YesterdayCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config),
("week", Some(matches)) => WeekCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config),
("month", Some(matches)) => MonthCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config),
("display", Some(matches)) => DisplayCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config, now),
("today", Some(matches)) => TodayCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config, now),
("yesterday", Some(matches)) => YesterdayCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config, now),
("week", Some(matches)) => WeekCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config, now),
("month", Some(matches)) => MonthCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config, now),
("sheet", Some(matches)) => SheetCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config),
("list", Some(matches)) => ListCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config),
("sheet", Some(matches)) => SheetCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config, now),
("list", Some(matches)) => ListCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config, now),
(cmd, _) => Err(error::Error::UnimplementedCommand(cmd.into())),
}

59
src/old.rs Normal file
View File

@ -0,0 +1,59 @@
use std::io::Write;
use chrono::{DateTime, Utc, Local, LocalResult, TimeZone};
use ansi_term::Color::Yellow;
use crate::error::{Error, Result};
use crate::models::Entry;
use crate::database::{Database, DBVersion};
fn local_to_utc(t: DateTime<Utc>) -> Result<DateTime<Utc>> {
let local_time = match Local.from_local_datetime(&t.naive_utc()) {
LocalResult::None => return Err(Error::NoneLocalTime(t.naive_utc().to_string())),
LocalResult::Single(t) => t,
LocalResult::Ambiguous(t1, t2) => return Err(Error::AmbiguousLocalTime {
orig: t.naive_utc().to_string(),
t1: t1.naive_local(),
t2: t2.naive_local(),
}),
};
Ok(Utc.from_utc_datetime(&local_time.naive_utc()))
}
fn local_to_utc_vec(entries: Vec<Entry>) -> Result<Vec<Entry>> {
entries
.into_iter()
.map(|e| {
Ok(Entry {
start: local_to_utc(e.start)?,
end: e.end.map(|t| local_to_utc(t)).transpose()?,
..e
})
})
.collect()
}
pub fn entries_or_warning<D: Database>(entries: Vec<Entry>, db: &D) -> Result<(Vec<Entry>, bool)> {
if let DBVersion::Timetrap = db.version()? {
// this indicates that times in the database are specified in the
// local time and need to be converted to utc before displaying
Ok((local_to_utc_vec(entries)?, true))
} else {
Ok((entries, false))
}
}
pub fn warn_if_needed<E: Write>(err: &mut E, needs_warning: bool) -> Result<()> {
if needs_warning {
writeln!(
err,
"{} You are using the old timetrap format, it is advised that \
you update your database using t migrate",
Yellow.bold().paint("[WARNING]"),
)?;
}
Ok(())
}