2020-04-13 23:56:25 +02:00
use super ::tracc ::ListView ;
use itertools ::Itertools ;
2020-01-25 22:15:41 +01:00
use serde ::{ Deserialize , Serialize } ;
use serde_json ::from_reader ;
2020-04-19 19:22:06 +02:00
use std ::collections ;
2020-04-18 18:15:23 +02:00
use std ::default ;
2020-01-25 22:15:41 +01:00
use std ::fmt ;
2020-04-19 19:22:06 +02:00
use std ::fs ;
use std ::io ;
use time ::{ Duration , OffsetDateTime } ;
2020-01-25 22:15:41 +01:00
pub struct TimeSheet {
pub times : Vec < TimePoint > ,
pub selected : usize ,
pub register : Option < TimePoint > ,
2020-04-13 23:56:25 +02:00
pub editing_time : bool ,
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
#[ derive(Serialize, Deserialize, Clone, Debug) ]
2020-01-25 22:15:41 +01:00
pub struct TimePoint {
text : String ,
2020-04-13 23:56:25 +02:00
time : OffsetDateTime ,
2020-01-25 22:15:41 +01:00
}
impl TimePoint {
pub fn new ( text : & str ) -> Self {
Self {
text : String ::from ( text ) ,
2020-04-13 23:56:25 +02:00
time : OffsetDateTime ::now_local ( ) ,
2020-01-25 22:15:41 +01:00
}
}
}
impl fmt ::Display for TimePoint {
fn fmt ( & self , f : & mut fmt ::Formatter ) -> fmt ::Result {
2020-04-13 23:56:25 +02:00
write! (
f ,
" [{}] {} " ,
self . time
. to_offset ( time ::UtcOffset ::current_local_offset ( ) )
. format ( " %H:%M " ) ,
self . text
)
2020-01-25 22:15:41 +01:00
}
}
2020-04-18 18:15:23 +02:00
impl default ::Default for TimePoint {
fn default ( ) -> Self {
TimePoint ::new ( " " )
}
}
2020-04-13 23:56:25 +02:00
fn read_times ( path : & str ) -> Option < Vec < TimePoint > > {
2020-04-19 19:22:06 +02:00
fs ::File ::open ( path )
2020-04-13 23:56:25 +02:00
. ok ( )
2020-04-19 19:22:06 +02:00
. map ( io ::BufReader ::new )
2020-04-13 23:56:25 +02:00
. and_then ( | r | from_reader ( r ) . ok ( ) )
}
2020-01-25 22:15:41 +01:00
impl TimeSheet {
2020-04-13 23:56:25 +02:00
pub fn open_or_create ( path : & str ) -> Self {
2020-01-25 22:15:41 +01:00
Self {
2020-04-19 19:22:06 +02:00
times : read_times ( path ) . unwrap_or_else ( | | vec! [ TimePoint ::new ( " Did something " ) ] ) ,
2020-01-25 22:15:41 +01:00
selected : 0 ,
register : None ,
2020-04-13 23:56:25 +02:00
editing_time : false ,
2020-01-25 22:15:41 +01:00
}
}
pub fn printable ( & self ) -> Vec < String > {
self . times . iter ( ) . map ( TimePoint ::to_string ) . collect ( )
}
2020-04-13 23:56:25 +02:00
fn current ( & self ) -> & TimePoint {
& self . times [ self . selected ]
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
pub fn time_by_tasks ( & self ) -> String {
2020-04-19 19:04:27 +02:00
self . times
. iter ( )
. tuple_windows ( )
. map ( | ( prev , next ) | ( prev . text . clone ( ) , next . time - prev . time ) )
2020-04-19 19:22:06 +02:00
. fold ( collections ::BTreeMap ::new ( ) , | mut map , ( text , duration ) | {
* map . entry ( text ) . or_insert_with ( Duration ::zero ) + = duration ;
map
} )
2020-04-13 23:56:25 +02:00
. into_iter ( )
. map ( | ( text , duration ) | format! ( " {} : {} " , text , format_duration ( & duration ) ) )
2020-04-19 19:04:27 +02:00
. join ( " | " )
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
pub fn sum_as_str ( & self ) -> String {
let total = self
. times
2020-04-19 19:07:54 +02:00
. iter ( )
. map ( | tp | tp . time )
. tuple_windows ( )
2020-04-19 19:22:06 +02:00
. fold ( Duration ::zero ( ) , | total , ( last , next ) | {
2020-04-13 23:56:25 +02:00
total + ( next - last )
} ) ;
format_duration ( & total )
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
}
2020-04-19 19:22:06 +02:00
fn format_duration ( d : & Duration ) -> String {
2020-04-13 23:56:25 +02:00
format! ( " {} : {:02} " , d . whole_hours ( ) , d . whole_minutes ( ) . max ( 1 ) % 60 )
}
2020-01-25 22:15:41 +01:00
2020-04-13 23:56:25 +02:00
impl ListView < TimePoint > for TimeSheet {
fn selection_pointer ( & mut self ) -> & mut usize {
& mut self . selected
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
fn list ( & mut self ) -> & mut Vec < TimePoint > {
& mut self . times
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
fn register ( & mut self ) -> & mut Option < TimePoint > {
& mut self . register
}
fn normal_mode ( & mut self ) {
2020-01-25 22:15:41 +01:00
if self . current ( ) . text . is_empty ( ) {
self . remove_current ( ) ;
self . selected = self . selected . saturating_sub ( 1 ) ;
}
2020-04-13 23:56:25 +02:00
self . times . sort_by_key ( | t | t . time ) ;
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
fn toggle_current ( & mut self ) {
self . editing_time = ! self . editing_time ;
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
fn append_to_current ( & mut self , chr : char ) {
self . times [ self . selected ] . text . push ( chr ) ;
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
fn backspace ( & mut self ) {
self . times [ self . selected ] . text . pop ( ) ;
}
2020-01-25 22:15:41 +01:00
}