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