1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
extern crate chrono; extern crate clap; extern crate two_timer; use crate::configure::Configuration; use crate::log::{Event, Filter, LogController}; use crate::util::fatal; use crate::vacation::VacationController; use chrono::{Duration, Local, NaiveDate, NaiveDateTime}; use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; use two_timer::parse; fn after_help() -> &'static str { "If you are expected to log a certain number of hours a day this command allows you \ to discover how many addional hours you will have to work to meet this expectation. Without any additional arguments the assumed period is the current day. Perhaps more useful \ is the pay period, but to use 'pay period' (abbreviated 'pp') as your time expression, \ you must have configured a pay period for the job log. See the configure subcommand. > job when when: today you were done at 4:16:52 PM All prefixes of 'when' are aliases of the subcommand. " } pub fn cli(mast: App<'static, 'static>, display_order: usize) -> App<'static, 'static> { mast.subcommand( SubCommand::with_name("when") .aliases(&["w", "wh", "whe"]) .about("Says when you will have worked all the hours expected within the given period") .after_help(after_help()) .setting(AppSettings::TrailingVarArg) .arg( Arg::with_name("period") .help("time expression") .long_help( "All the <period> arguments are concatenated to produce a time expression.", ) .value_name("period") .default_value("today") .multiple(true) ) .display_order(display_order) ) } pub fn run(matches: &ArgMatches) { let configuration = Configuration::read(None); let phrase = matches .values_of("period") .unwrap() .collect::<Vec<&str>>() .join(" "); println!("when: {}", phrase); match parse(&phrase, configuration.two_timer_config()) { Ok((start, end, _)) => { let now = Local::now().naive_local(); if now <= start { fatal( format!( "the current moment, {}, must be after the first moment sought: {}.", now, start ), &configuration, ) } else if start >= end { fatal( format!( "the current moment, {}, must be before the last moment sought: {}.", now, end ), &configuration, ) } else { let mut reader = LogController::new(None).expect("could not read log"); let events = reader.events_in_range(&start, &now); let events = Event::gather_by_day(events, &end); let filter = Filter::dummy(); let events = VacationController::read(None).add_vacation_times( &start, &end, events, &configuration, None, &filter, ); let mut hours_required = 0.0; let mut seconds_worked = 0.0; let mut last_workday: Option<NaiveDate> = None; for e in events { let date = e.start.date(); if configuration.is_workday(&date) { if last_workday.is_none() || last_workday.unwrap() != date { hours_required += configuration.day_length; last_workday = Some(date); } } seconds_worked += e.duration(&now); } let seconds_required = hours_required * (60.0 * 60.0); let delta = seconds_required - seconds_worked; let delta_hours = delta / (60.0 * 60.0); let completion_time = now + Duration::seconds(delta as i64); if completion_time > now { println!( "you will be finished at {}, {:.2} hours from now", tell_time(&now, &completion_time), delta_hours ); } else { println!("you were done at {}", tell_time(&now, &completion_time)); } } } Err(e) => fatal(e.msg(), &configuration), } } fn tell_time(now: &NaiveDateTime, then: &NaiveDateTime) -> String { if now.date() == then.date() { format!("{}", then.format("%l:%M:%S %p")) } else { format!("{}", then.format("%l:%M:%S %p on %A, %e %B %Y")) } }