Allow tasks to be canceled

This commit is contained in:
Joscha 2021-12-31 23:57:40 +01:00
parent e0cb1c8f23
commit 0e4ef7fef3
13 changed files with 107 additions and 14 deletions

View file

@ -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
View 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))
}
}

View file

@ -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(),
};

View file

@ -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,
});

View file

@ -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,
}

View file

@ -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(),
}

View file

@ -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());

View file

@ -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(())
}
}

View file

@ -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;
}

View file

@ -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,
}

View file

@ -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)?;
}

View file

@ -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 }

View file

@ -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>> {