serialization order change to avoid data loss; time tracking command
This commit is contained in:
parent
6aa3eb4e83
commit
a1a1713c28
17
src/main.rs
17
src/main.rs
@ -18,7 +18,7 @@ struct Args {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::Subcommand, Debug, PartialEq, Eq)]
|
#[derive(clap::Subcommand, Debug, PartialEq, Eq)]
|
||||||
#[clap(version, about, author, global_setting=clap::AppSettings::DisableHelpSubcommand)]
|
#[clap(version, help_short='H', about, author, global_setting=clap::AppSettings::DisableHelpSubcommand)]
|
||||||
enum Command {
|
enum Command {
|
||||||
/// Create a new task.
|
/// Create a new task.
|
||||||
New {
|
New {
|
||||||
@ -74,6 +74,14 @@ enum Command {
|
|||||||
// - which columns to include
|
// - which columns to include
|
||||||
// - filters which exclude values
|
// - filters which exclude values
|
||||||
},
|
},
|
||||||
|
/// For tracking time against a note.
|
||||||
|
Track {
|
||||||
|
id_or_name : String,
|
||||||
|
#[clap(short, default_value_t=0)]
|
||||||
|
hours : u16,
|
||||||
|
#[clap(short, default_value_t=0)]
|
||||||
|
minutes : u16,
|
||||||
|
},
|
||||||
/// For making changes to global configuration.
|
/// For making changes to global configuration.
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
Config(ConfigCommand),
|
Config(ConfigCommand),
|
||||||
@ -227,6 +235,13 @@ fn program() -> Result<(), error::Error> {
|
|||||||
}
|
}
|
||||||
println!("Updated task {}", colour::id(&id.to_string()));
|
println!("Updated task {}", colour::id(&id.to_string()));
|
||||||
},
|
},
|
||||||
|
Track { id_or_name, hours, minutes } => {
|
||||||
|
let id = state.name_or_id_to_id(&id_or_name)?;
|
||||||
|
let mut task = tasks::Task::load(id, vault_folder, false)?;
|
||||||
|
let entry = tasks::TimeEntry::new(hours, minutes);
|
||||||
|
task.data.time_entries.push(entry);
|
||||||
|
task.save()?;
|
||||||
|
},
|
||||||
Discard { id_or_name } => {
|
Discard { id_or_name } => {
|
||||||
let id = state.name_or_id_to_id(&id_or_name)?;
|
let id = state.name_or_id_to_id(&id_or_name)?;
|
||||||
let mut task = tasks::Task::load(id, vault_folder, false)?;
|
let mut task = tasks::Task::load(id, vault_folder, false)?;
|
||||||
|
@ -80,9 +80,11 @@ impl State {
|
|||||||
.create(true)
|
.create(true)
|
||||||
.open(&path)?;
|
.open(&path)?;
|
||||||
|
|
||||||
|
let file_contents = toml::to_string(&data)?;
|
||||||
|
|
||||||
file.set_len(0)?;
|
file.set_len(0)?;
|
||||||
file.seek(io::SeekFrom::Start(0))?;
|
file.seek(io::SeekFrom::Start(0))?;
|
||||||
file.write_all(toml::to_string(&data)?.as_bytes())?;
|
file.write_all(file_contents.as_bytes())?;
|
||||||
|
|
||||||
let task = Self {
|
let task = Self {
|
||||||
file,
|
file,
|
||||||
@ -100,9 +102,11 @@ impl State {
|
|||||||
data,
|
data,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
|
let file_contents = toml::to_string(&data)?;
|
||||||
|
|
||||||
file.set_len(0)?;
|
file.set_len(0)?;
|
||||||
file.seek(io::SeekFrom::Start(0))?;
|
file.seek(io::SeekFrom::Start(0))?;
|
||||||
file.write_all(toml::to_string(&data)?.as_bytes())?;
|
file.write_all(file_contents.as_bytes())?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
69
src/tasks.rs
69
src/tasks.rs
@ -51,10 +51,26 @@ impl Priority {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct TimeEntry {
|
pub struct TimeEntry {
|
||||||
hours : u32,
|
logged_date : chrono::NaiveDate,
|
||||||
minutes : u8,
|
hours : u16,
|
||||||
|
minutes : u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimeEntry {
|
||||||
|
pub fn new(hours : u16, minutes : u16) -> Self {
|
||||||
|
|
||||||
|
let (hours, minutes) = {
|
||||||
|
(hours + minutes / 60, minutes % 60)
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
logged_date : chrono::Utc::now().naive_local().date(),
|
||||||
|
hours,
|
||||||
|
minutes,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
@ -66,10 +82,10 @@ pub struct InternalTask {
|
|||||||
pub dependencies : HashSet<Id>,
|
pub dependencies : HashSet<Id>,
|
||||||
pub priority : Priority,
|
pub priority : Priority,
|
||||||
//due : Option<chrono::NaiveDateTime>,
|
//due : Option<chrono::NaiveDateTime>,
|
||||||
pub time_entries : Vec<TimeEntry>,
|
|
||||||
pub created : chrono::NaiveDateTime,
|
pub created : chrono::NaiveDateTime,
|
||||||
pub complete : bool,
|
pub complete : bool,
|
||||||
pub discarded : bool,
|
pub discarded : bool,
|
||||||
|
pub time_entries : Vec<TimeEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Task {
|
impl Task {
|
||||||
@ -102,9 +118,11 @@ impl Task {
|
|||||||
discarded : false,
|
discarded : false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let file_contents = toml::to_string(&data)?;
|
||||||
|
|
||||||
file.set_len(0)?;
|
file.set_len(0)?;
|
||||||
file.seek(io::SeekFrom::Start(0))?;
|
file.seek(io::SeekFrom::Start(0))?;
|
||||||
file.write_all(toml::to_string(&data)?.as_bytes())?;
|
file.write_all(file_contents.as_bytes())?;
|
||||||
|
|
||||||
state.index_insert(data.name.clone(), id);
|
state.index_insert(data.name.clone(), id);
|
||||||
|
|
||||||
@ -186,9 +204,11 @@ impl Task {
|
|||||||
data,
|
data,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
|
let file_contents = toml::to_string(&data)?;
|
||||||
|
|
||||||
file.set_len(0)?;
|
file.set_len(0)?;
|
||||||
file.seek(io::SeekFrom::Start(0))?;
|
file.seek(io::SeekFrom::Start(0))?;
|
||||||
file.write_all(toml::to_string(&data)?.as_bytes())?;
|
file.write_all(file_contents.as_bytes())?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -215,15 +235,22 @@ impl Task {
|
|||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = &self.data.id.to_string();
|
let (heading, heading_length) = {
|
||||||
let discarded = if self.data.discarded { String::from(" (discarded)") } else { String::new() };
|
let id = &self.data.id.to_string();
|
||||||
let heading = format!("[{}] {} {}{}", if self.data.complete {"X"} else {" "}, colour::id(id), colour::task_name(&self.data.name), colour::greyed_out(&discarded));
|
let discarded = if self.data.discarded { String::from(" (discarded)") } else { String::new() };
|
||||||
println!("{}", heading);
|
|
||||||
|
|
||||||
line(5 + self.data.name.chars().count() + id.chars().count() + discarded.chars().count());
|
(
|
||||||
println!("Priority: {}", self.data.priority.coloured());
|
format!("[{}] {} {}{}", if self.data.complete {"X"} else {" "}, colour::id(id), colour::task_name(&self.data.name), colour::greyed_out(&discarded)),
|
||||||
println!("Tags: [{}]", format_hash_set(&self.data.tags)?);
|
5 + self.data.name.chars().count() + id.chars().count() + discarded.chars().count()
|
||||||
println!("Created: {}", self.data.created);
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("{}", heading);
|
||||||
|
line(heading_length);
|
||||||
|
|
||||||
|
println!("Priority: {}", self.data.priority.coloured());
|
||||||
|
println!("Tags: [{}]", format_hash_set(&self.data.tags)?);
|
||||||
|
println!("Created: {}", self.data.created);
|
||||||
|
|
||||||
if let Some(mut info) = self.data.info.clone() {
|
if let Some(mut info) = self.data.info.clone() {
|
||||||
let mut max_line_width = 0;
|
let mut max_line_width = 0;
|
||||||
@ -238,10 +265,18 @@ impl Task {
|
|||||||
max_line_width = usize::max(max_line_width, line.chars().count() + 4);
|
max_line_width = usize::max(max_line_width, line.chars().count() + 4);
|
||||||
println!(" {}", line);
|
println!(" {}", line);
|
||||||
}
|
}
|
||||||
line(usize::min(max_line_width, usize::try_from(termsize::get().map(|s| s.cols).unwrap_or(0)).unwrap()));
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// Need to work out appropriate line size.
|
if !self.data.time_entries.is_empty() {
|
||||||
|
|
||||||
|
let mut entries = self.data.time_entries.clone();
|
||||||
|
// Sort entries by date.
|
||||||
|
entries.sort_by(|e1, e2| e1.logged_date.cmp(&e2.logged_date));
|
||||||
|
|
||||||
|
println!("Time Entries:");
|
||||||
|
for entry in &entries {
|
||||||
|
println!(" {}:{:0>2} [{}]", entry.hours, entry.minutes, entry.logged_date);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// dependencies as a tree
|
// dependencies as a tree
|
||||||
|
Loading…
Reference in New Issue
Block a user