Allow tasks to be canceled
This commit is contained in:
parent
e0cb1c8f23
commit
0e4ef7fef3
13 changed files with 107 additions and 14 deletions
15
src/cli.rs
15
src/cli.rs
|
|
@ -13,6 +13,7 @@ use crate::files::{self, Files};
|
|||
use self::error::Result;
|
||||
use self::layout::line::LineLayout;
|
||||
|
||||
mod cancel;
|
||||
mod done;
|
||||
mod error;
|
||||
mod layout;
|
||||
|
|
@ -49,6 +50,12 @@ pub enum Command {
|
|||
#[structopt(required = true)]
|
||||
entries: Vec<usize>,
|
||||
},
|
||||
/// Marks one or more entries as canceled
|
||||
Cancel {
|
||||
/// Entries to mark as done
|
||||
#[structopt(required = true)]
|
||||
entries: Vec<usize>,
|
||||
},
|
||||
/// Reformat all loaded files
|
||||
Fmt,
|
||||
}
|
||||
|
|
@ -107,6 +114,14 @@ fn run_command(opt: &Opt, files: &mut Files, range: DateRange, now: NaiveDateTim
|
|||
let layout = find_layout(files, &entries, range, now);
|
||||
print::print(&layout);
|
||||
}
|
||||
Some(Command::Cancel { entries: ns }) => {
|
||||
let entries = find_entries(files, range)?;
|
||||
let layout = find_layout(files, &entries, range, now);
|
||||
cancel::cancel(files, &entries, &layout, ns, now)?;
|
||||
let entries = find_entries(files, range)?;
|
||||
let layout = find_layout(files, &entries, range, now);
|
||||
print::print(&layout);
|
||||
}
|
||||
Some(Command::Fmt) => files.mark_all_dirty(),
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
37
src/cli/cancel.rs
Normal file
37
src/cli/cancel.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
use std::vec;
|
||||
|
||||
use chrono::NaiveDateTime;
|
||||
|
||||
use crate::eval::Entry;
|
||||
use crate::files::commands::{Done, DoneKind};
|
||||
use crate::files::Files;
|
||||
|
||||
use super::error::{Error, Result};
|
||||
use super::layout::line::LineLayout;
|
||||
|
||||
pub fn cancel(
|
||||
files: &mut Files,
|
||||
entries: &[Entry],
|
||||
layout: &LineLayout,
|
||||
numbers: &[usize],
|
||||
now: NaiveDateTime,
|
||||
) -> Result<()> {
|
||||
let mut not_tasks = vec![];
|
||||
for &number in numbers {
|
||||
let entry = &entries[layout.look_up_number(number)?];
|
||||
let done = Done {
|
||||
kind: DoneKind::Canceled,
|
||||
date: entry.dates.map(|dates| dates.into()),
|
||||
done_at: now.date(),
|
||||
};
|
||||
if !files.add_done(entry.source, done) {
|
||||
not_tasks.push(number);
|
||||
}
|
||||
}
|
||||
|
||||
if not_tasks.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::NotATask(not_tasks))
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ use std::vec;
|
|||
use chrono::NaiveDateTime;
|
||||
|
||||
use crate::eval::Entry;
|
||||
use crate::files::commands::Done;
|
||||
use crate::files::commands::{Done, DoneKind};
|
||||
use crate::files::Files;
|
||||
|
||||
use super::error::{Error, Result};
|
||||
|
|
@ -20,6 +20,7 @@ pub fn done(
|
|||
for &number in numbers {
|
||||
let entry = &entries[layout.look_up_number(number)?];
|
||||
let done = Done {
|
||||
kind: DoneKind::Done,
|
||||
date: entry.dates.map(|dates| dates.into()),
|
||||
done_at: now.date(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -73,7 +73,9 @@ impl DayLayout {
|
|||
fn layout_entry(&mut self, index: usize, entry: &Entry) {
|
||||
match entry.kind {
|
||||
EntryKind::Task => self.layout_task(index, entry),
|
||||
EntryKind::TaskDone(at) => self.layout_task_done(index, entry, at),
|
||||
EntryKind::TaskDone(at) | EntryKind::TaskCanceled(at) => {
|
||||
self.layout_task_done(index, entry, at)
|
||||
}
|
||||
EntryKind::Note | EntryKind::Birthday(_) => self.layout_note(index, entry),
|
||||
}
|
||||
}
|
||||
|
|
@ -208,7 +210,7 @@ impl DayLayout {
|
|||
// 3.
|
||||
entries.sort_by_key(|(_, e, _)| match e.kind {
|
||||
EntryKind::Task => 0,
|
||||
EntryKind::TaskDone(_) => 1,
|
||||
EntryKind::TaskDone(_) | EntryKind::TaskCanceled(_) => 1,
|
||||
EntryKind::Birthday(_) => 2,
|
||||
EntryKind::Note => 3,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ pub enum Times {
|
|||
pub enum LineKind {
|
||||
Task,
|
||||
Done,
|
||||
Canceled,
|
||||
Note,
|
||||
Birthday,
|
||||
}
|
||||
|
|
@ -245,6 +246,7 @@ impl LineLayout {
|
|||
match entry.kind {
|
||||
EntryKind::Task => LineKind::Task,
|
||||
EntryKind::TaskDone(_) => LineKind::Done,
|
||||
EntryKind::TaskCanceled(_) => LineKind::Canceled,
|
||||
EntryKind::Note => LineKind::Note,
|
||||
EntryKind::Birthday(_) => LineKind::Birthday,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ impl ShowLines {
|
|||
match kind {
|
||||
LineKind::Task => "T".magenta().bold(),
|
||||
LineKind::Done => "D".green().bold(),
|
||||
LineKind::Canceled => "C".red().bold(),
|
||||
LineKind::Note => "N".blue().bold(),
|
||||
LineKind::Birthday => "B".yellow().bold(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ fn show_entry(files: &Files, entry: &Entry) {
|
|||
println!("DONE {}", command.title());
|
||||
println!("DONE AT {}", when);
|
||||
}
|
||||
EntryKind::TaskCanceled(when) => {
|
||||
println!("CANCELED {}", command.title());
|
||||
println!("CANCELED AT {}", when);
|
||||
}
|
||||
EntryKind::Note => println!("NOTE {}", command.title()),
|
||||
EntryKind::Birthday(Some(age)) => {
|
||||
println!("BIRTHDAY {}", command.title());
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
|||
use chrono::{Duration, NaiveDate};
|
||||
|
||||
use crate::files::commands::{
|
||||
self, BirthdaySpec, Command, Done, DoneDate, Note, Spec, Statement, Task,
|
||||
self, BirthdaySpec, Command, Done, DoneDate, DoneKind, Note, Spec, Statement, Task,
|
||||
};
|
||||
use crate::files::primitives::{Span, Spanned, Time};
|
||||
use crate::files::SourcedCommand;
|
||||
|
|
@ -296,10 +296,12 @@ impl<'a> CommandState<'a> {
|
|||
}
|
||||
|
||||
fn eval_done(&mut self, done: &Done) -> Result<()> {
|
||||
self.add_forced(self.entry_with_remind(
|
||||
EntryKind::TaskDone(done.done_at),
|
||||
done.date.map(|date| date.into()),
|
||||
)?);
|
||||
let kind = match done.kind {
|
||||
DoneKind::Done => EntryKind::TaskDone(done.done_at),
|
||||
DoneKind::Canceled => EntryKind::TaskCanceled(done.done_at),
|
||||
};
|
||||
let dates = done.date.map(|date| date.into());
|
||||
self.add_forced(self.entry_with_remind(kind, dates)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use super::range::DateRange;
|
|||
pub enum EntryKind {
|
||||
Task,
|
||||
TaskDone(NaiveDate),
|
||||
TaskCanceled(NaiveDate),
|
||||
Note,
|
||||
Birthday(Option<i32>),
|
||||
}
|
||||
|
|
@ -120,7 +121,7 @@ impl Entries {
|
|||
}
|
||||
|
||||
// Tasks that were finished inside the range
|
||||
if let EntryKind::TaskDone(done) = entry.kind {
|
||||
if let EntryKind::TaskDone(done) | EntryKind::TaskCanceled(done) = entry.kind {
|
||||
if self.range.contains(done) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -309,8 +309,15 @@ impl DoneDate {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DoneKind {
|
||||
Done,
|
||||
Canceled,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Done {
|
||||
pub kind: DoneKind,
|
||||
pub date: Option<DoneDate>,
|
||||
pub done_at: NaiveDate,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ use std::fmt;
|
|||
|
||||
use chrono::Datelike;
|
||||
|
||||
use crate::files::commands::DoneKind;
|
||||
|
||||
use super::commands::{
|
||||
BirthdaySpec, Command, DateSpec, Delta, DeltaStep, Done, DoneDate, Expr, File, FormulaSpec,
|
||||
Note, Repeat, Spec, Statement, Task, Var, WeekdaySpec,
|
||||
|
|
@ -258,7 +260,11 @@ impl fmt::Display for DoneDate {
|
|||
|
||||
impl fmt::Display for Done {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "DONE [{}]", self.done_at)?;
|
||||
let kind = match self.kind {
|
||||
DoneKind::Done => "DONE",
|
||||
DoneKind::Canceled => "CANCELED",
|
||||
};
|
||||
write!(f, "{} [{}]", kind, self.done_at)?;
|
||||
if let Some(date) = &self.date {
|
||||
write!(f, " {}", date)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,7 +118,8 @@ donedate = {
|
|||
| datum ~ "--" ~ datum
|
||||
| datum
|
||||
}
|
||||
done = !{ "DONE" ~ "[" ~ datum ~ "]" ~ donedate? ~ eol }
|
||||
done_kind = { "DONE" | "CANCELED" }
|
||||
done = !{ done_kind ~ "[" ~ datum ~ "]" ~ donedate? ~ eol }
|
||||
dones = { done* }
|
||||
|
||||
desc_line = { "#" ~ (" " ~ rest_any)? ~ eol }
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ use pest::prec_climber::{Assoc, Operator, PrecClimber};
|
|||
use pest::{Parser, Span};
|
||||
|
||||
use super::commands::{
|
||||
BirthdaySpec, Command, DateSpec, Delta, DeltaStep, Done, DoneDate, Expr, File, FormulaSpec,
|
||||
Note, Repeat, Spec, Statement, Task, Var, WeekdaySpec,
|
||||
BirthdaySpec, Command, DateSpec, Delta, DeltaStep, Done, DoneDate, DoneKind, Expr, File,
|
||||
FormulaSpec, Note, Repeat, Spec, Statement, Task, Var, WeekdaySpec,
|
||||
};
|
||||
use super::primitives::{Spanned, Time, Weekday};
|
||||
|
||||
|
|
@ -704,10 +704,20 @@ fn parse_donedate(p: Pair<'_, Rule>) -> Result<DoneDate> {
|
|||
})
|
||||
}
|
||||
|
||||
fn parse_done_kind(p: Pair<'_, Rule>) -> DoneKind {
|
||||
assert_eq!(p.as_rule(), Rule::done_kind);
|
||||
match p.as_str() {
|
||||
"DONE" => DoneKind::Done,
|
||||
"CANCELED" => DoneKind::Canceled,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_done(p: Pair<'_, Rule>) -> Result<Done> {
|
||||
assert_eq!(p.as_rule(), Rule::done);
|
||||
let mut p = p.into_inner();
|
||||
|
||||
let kind = parse_done_kind(p.next().unwrap());
|
||||
let done_at = parse_datum(p.next().unwrap())?.value;
|
||||
let date = if let Some(p) = p.next() {
|
||||
Some(parse_donedate(p)?)
|
||||
|
|
@ -717,7 +727,11 @@ fn parse_done(p: Pair<'_, Rule>) -> Result<Done> {
|
|||
|
||||
assert_eq!(p.next(), None);
|
||||
|
||||
Ok(Done { date, done_at })
|
||||
Ok(Done {
|
||||
kind,
|
||||
date,
|
||||
done_at,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_dones(p: Pair<'_, Rule>) -> Result<Vec<Done>> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue