2020-01-23 23:05:40 +01:00
use super ::Mode ;
2020-01-24 00:02:32 +01:00
use serde ::{ Deserialize , Serialize } ;
use serde_json ::from_reader ;
use std ::fs ::File ;
use std ::io ::{ self , BufReader , Write } ;
2020-01-23 23:05:40 +01:00
use tui ::backend ::Backend ;
use tui ::Terminal ;
pub struct Tracc {
// We use owned strings here because they’re easier to manipulate when editing.
pub todos : Vec < Todo > ,
2020-01-24 11:40:16 +01:00
pub selected : usize ,
2020-01-23 23:05:40 +01:00
pub mode : Mode ,
}
2020-01-24 11:07:37 +01:00
#[ derive(Serialize, Deserialize, Default, Clone) ]
2020-01-23 23:05:40 +01:00
pub struct Todo {
text : String ,
done : bool ,
}
impl Todo {
pub fn new ( text : & str ) -> Self {
Todo {
text : text . to_owned ( ) ,
done : false ,
}
}
}
2020-01-24 00:02:32 +01:00
const JSON_PATH : & str = " tracc.json " ;
fn read_todos ( ) -> Option < Vec < Todo > > {
File ::open ( JSON_PATH )
. ok ( )
. map ( | f | BufReader ::new ( f ) )
. and_then ( | r | from_reader ( r ) . ok ( ) )
}
2020-01-23 23:05:40 +01:00
impl Tracc {
2020-01-24 00:02:32 +01:00
pub fn open_or_create ( ) -> Self {
2020-01-23 23:05:40 +01:00
Self {
2020-01-24 00:02:32 +01:00
todos : read_todos ( ) . unwrap_or ( vec! [ Todo ::new ( " This is a list entry " ) ] ) ,
2020-01-24 11:40:16 +01:00
selected : 0 ,
2020-01-23 23:05:40 +01:00
mode : Mode ::Normal ,
}
}
pub fn printable_todos ( & self ) -> Vec < String > {
self . todos
. iter ( )
. map ( | todo | format! ( " [ {} ] {} " , if todo . done { 'x' } else { ' ' } , todo . text ) )
. collect ( )
}
pub fn selection_down ( & mut self ) {
2020-01-24 11:40:16 +01:00
self . selected = ( self . selected + 1 ) . min ( self . todos . len ( ) . saturating_sub ( 1 ) ) ;
2020-01-23 23:05:40 +01:00
}
pub fn selection_up ( & mut self ) {
2020-01-24 11:40:16 +01:00
self . selected = self . selected . saturating_sub ( 1 ) ;
2020-01-23 23:05:40 +01:00
}
2020-01-24 11:07:37 +01:00
pub fn insert ( & mut self , todo : Todo ) {
2020-01-24 11:40:16 +01:00
if self . selected = = self . todos . len ( ) . saturating_sub ( 1 ) {
self . todos . push ( todo ) ;
self . selected = self . todos . len ( ) - 1 ;
} else {
self . todos . insert ( self . selected + 1 , todo ) ;
self . selected + = 1 ;
2020-01-24 11:07:37 +01:00
}
2020-01-23 23:05:40 +01:00
self . mode = Mode ::Normal ;
}
2020-01-24 11:07:37 +01:00
pub fn remove_current ( & mut self ) -> Option < Todo > {
if self . todos . is_empty ( ) {
return None ;
}
2020-01-24 11:40:16 +01:00
let index = self . selected ;
self . selected = index . min ( self . todos . len ( ) - 1 ) ;
return Some ( self . todos . remove ( index ) ) ;
2020-01-23 23:05:40 +01:00
}
pub fn toggle_current ( & mut self ) {
2020-01-24 11:40:16 +01:00
self . todos [ self . selected ] . done = ! self . todos [ self . selected ] . done ;
2020-01-23 23:05:40 +01:00
}
2020-01-24 11:40:16 +01:00
fn current ( & self ) -> & Todo {
& self . todos [ self . selected ]
2020-01-23 23:05:40 +01:00
}
pub fn set_mode (
& mut self ,
mode : Mode ,
term : & mut Terminal < impl Backend > ,
) -> Result < ( ) , io ::Error > {
match mode {
Mode ::Insert = > term . show_cursor ( ) ? ,
Mode ::Normal = > {
if self . current ( ) . text . is_empty ( ) {
2020-01-24 11:07:37 +01:00
self . remove_current ( ) ;
2020-01-24 11:40:16 +01:00
self . selected = self . selected . saturating_sub ( 1 ) ;
2020-01-23 23:05:40 +01:00
}
2020-01-24 00:02:32 +01:00
term . hide_cursor ( ) ?
}
2020-01-23 23:05:40 +01:00
} ;
self . mode = mode ;
Ok ( ( ) )
}
pub fn append_to_current ( & mut self , chr : char ) {
2020-01-24 11:40:16 +01:00
self . todos [ self . selected ] . text . push ( chr ) ;
2020-01-23 23:05:40 +01:00
}
pub fn current_pop ( & mut self ) {
2020-01-24 11:40:16 +01:00
self . todos [ self . selected ] . text . pop ( ) ;
2020-01-23 23:05:40 +01:00
}
2020-01-24 00:02:32 +01:00
pub fn persist ( self ) {
let string = serde_json ::to_string ( & self . todos ) . unwrap ( ) ;
2020-01-24 08:11:07 +01:00
std ::fs ::OpenOptions ::new ( )
. create ( true )
. write ( true )
. truncate ( true )
. open ( JSON_PATH )
2020-01-24 00:02:32 +01:00
. ok ( )
. or_else ( | | panic! ( " Can’t save todos to JSON. Dumping raw data: \n {} " , string ) )
. map ( | mut f | f . write ( string . as_bytes ( ) ) ) ;
}
2020-01-23 23:05:40 +01:00
}