diff --git a/src/cli.rs b/src/cli.rs index 9f8abfc..1c91c0c 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -92,7 +92,7 @@ pub fn run() -> Result<()> { }, Some(Command::Done) => match opt.entry { None => return Err(Error::NoNumber), - Some(n) => done::mark_done(&mut files, &entries, &layout, n, now)?, + Some(n) => done::mark_done(&mut files, &entries, &layout, &[n], now)?, }, Some(Command::Fmt) => files.mark_all_dirty(), } diff --git a/src/cli/done.rs b/src/cli/done.rs index e24505b..7c32fff 100644 --- a/src/cli/done.rs +++ b/src/cli/done.rs @@ -1,24 +1,36 @@ +use std::vec; + use chrono::NaiveDateTime; use crate::eval::Entry; use crate::files::commands::Done; use crate::files::Files; -use super::error::Result; +use super::error::{Error, Result}; use super::layout::line::LineLayout; pub fn mark_done( files: &mut Files, entries: &[Entry], layout: &LineLayout, - number: usize, + numbers: &[usize], now: NaiveDateTime, ) -> Result<()> { - let entry = &entries[layout.look_up_number(number)?]; - let done = Done { - date: entry.dates.map(|dates| dates.into()), - done_at: now.date(), - }; - files.add_done(number, entry.source, done)?; - Ok(()) + let mut not_tasks = vec![]; + for &number in numbers { + let entry = &entries[layout.look_up_number(number)?]; + let done = Done { + 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)) + } } diff --git a/src/cli/error.rs b/src/cli/error.rs index 95bb5ec..e0d19e4 100644 --- a/src/cli/error.rs +++ b/src/cli/error.rs @@ -12,6 +12,8 @@ pub enum Error { NoNumber, #[error("No entry with number {0}")] NoSuchEntry(usize), + #[error("Not a task")] + NotATask(Vec), } pub type Result = result::Result; diff --git a/src/files.rs b/src/files.rs index 35fe842..93ec56e 100644 --- a/src/files.rs +++ b/src/files.rs @@ -163,14 +163,19 @@ impl Files { &self.files[source.file].file.commands[source.command] } - pub fn add_done(&mut self, number: usize, source: Source, done: Done) -> Result<()> { + /// Add a [`Done`] statement to the task identified by `source`. + /// + /// Returns whether the addition was successful. It can fail if the entry + /// identified by `source` is a note, not a task. + #[must_use] + pub fn add_done(&mut self, source: Source, done: Done) -> bool { let file = &mut self.files[source.file]; match &mut file.file.commands[source.command] { Command::Task(t) => t.done.push(done), - Command::Note(_) => return Err(Error::NotATask(vec![number])), + Command::Note(_) => return false, } file.dirty = true; - Ok(()) + true } pub fn commands(&self) -> Vec> { diff --git a/src/files/error.rs b/src/files/error.rs index 1076eca..67acee4 100644 --- a/src/files/error.rs +++ b/src/files/error.rs @@ -24,8 +24,6 @@ pub enum Error { file2: PathBuf, tz2: String, }, - #[error("Not a task")] - NotATask(Vec), } impl Error { @@ -62,16 +60,6 @@ impl Error { eprintln!(" {:?} has time zone {}", file1, tz1); eprintln!(" {:?} has time zone {}", file2, tz2); } - Error::NotATask(numbers) => { - if numbers.is_empty() { - eprintln!("Not a task."); - } else if numbers.len() == 1 { - eprintln!("{} is not a task.", numbers[0]); - } else { - let numbers = numbers.iter().map(|n| n.to_string()).collect::>(); - eprintln!("{} are not tasks.", numbers.join(", ")); - } - } } } }