Implement done command

This commit is contained in:
Joscha 2021-12-14 22:37:27 +01:00
parent 0de89bd424
commit b476a95afb
6 changed files with 67 additions and 20 deletions

View file

@ -1,5 +1,4 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::process;
use chrono::{Duration, NaiveDate}; use chrono::{Duration, NaiveDate};
use directories::ProjectDirs; use directories::ProjectDirs;
@ -8,8 +7,9 @@ use structopt::StructOpt;
use crate::eval::{DateRange, EntryMode}; use crate::eval::{DateRange, EntryMode};
use crate::files::Files; use crate::files::Files;
use self::error::Result; use self::error::{Error, Result};
mod done;
mod error; mod error;
mod layout; mod layout;
mod show; mod show;
@ -75,15 +75,12 @@ pub fn run() -> Result<()> {
match opt.command { match opt.command {
None | Some(Command::Show) => match opt.entry { None | Some(Command::Show) => match opt.entry {
None => print!("{}", show::show_all(&layout)), None => show::show_all(&layout),
Some(n) => show::show_entry(&files, &entries, &layout, n)?, Some(n) => show::show_entry(&files, &entries[layout.look_up_number(n)?])?,
}, },
Some(Command::Done) => match opt.entry { Some(Command::Done) => match opt.entry {
None => { None => return Err(Error::NoNumber),
println!("Please specify an entry. See `today --help` for more details."); Some(n) => done::mark_done(&mut files, &entries[layout.look_up_number(n)?], now)?,
process::exit(1);
}
Some(i) => todo!(),
}, },
Some(Command::Fmt) => files.mark_all_dirty(), Some(Command::Fmt) => files.mark_all_dirty(),
} }

16
src/cli/done.rs Normal file
View file

@ -0,0 +1,16 @@
use chrono::NaiveDateTime;
use crate::eval::Entry;
use crate::files::commands::Done;
use crate::files::Files;
use super::error::Result;
pub fn mark_done(files: &mut Files, entry: &Entry, now: NaiveDateTime) -> Result<()> {
let done = Done {
date: entry.dates.map(|dates| dates.into()),
done_at: now.date(),
};
files.add_done(entry.source, done)?;
Ok(())
}

View file

@ -1,5 +1,6 @@
use std::result; use std::result;
use crate::files::Source;
use crate::{eval, files}; use crate::{eval, files};
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
@ -8,6 +9,8 @@ pub enum Error {
Files(#[from] files::Error), Files(#[from] files::Error),
#[error("{0}")] #[error("{0}")]
Eval(#[from] eval::Error), Eval(#[from] eval::Error),
#[error("No number specified")]
NoNumber,
#[error("No entry with number {0}")] #[error("No entry with number {0}")]
NoSuchEntry(usize), NoSuchEntry(usize),
} }

View file

@ -115,23 +115,16 @@ impl ShowLines {
} }
} }
pub fn show_all(layout: &LineLayout) -> String { pub fn show_all(layout: &LineLayout) {
let num_width = cmp::max(layout.num_width(), 3); // `now` is 3 chars wide let num_width = cmp::max(layout.num_width(), 3); // `now` is 3 chars wide
let mut show_lines = ShowLines::new(num_width, layout.span_width()); let mut show_lines = ShowLines::new(num_width, layout.span_width());
for line in layout.lines() { for line in layout.lines() {
show_lines.display_line(line); show_lines.display_line(line);
} }
show_lines.result() print!("{}", show_lines.result());
} }
pub fn show_entry( pub fn show_entry(files: &Files, entry: &Entry) -> Result<()> {
files: &Files,
entries: &[Entry],
layout: &LineLayout,
number: usize,
) -> Result<()> {
let index = layout.look_up_number(number)?;
let entry = &entries[index];
let command = files.command(entry.source); let command = files.command(entry.source);
match entry.kind { match entry.kind {

View file

@ -37,6 +37,30 @@ impl fmt::Display for Dates {
} }
} }
impl From<Dates> for DoneDate {
fn from(dates: Dates) -> Self {
match dates.times {
Some(times) if dates.root == dates.other && times.root == times.other => {
DoneDate::DateWithTime {
root: dates.root,
root_time: times.root,
}
}
Some(times) => DoneDate::DateToDateWithTime {
root: dates.root,
root_time: times.root,
other: dates.other,
other_time: times.other,
},
None if dates.root == dates.other => DoneDate::Date { root: dates.root },
None => DoneDate::DateToDate {
root: dates.root,
other: dates.other,
},
}
}
}
impl Dates { impl Dates {
pub fn new(root: NaiveDate, other: NaiveDate) -> Self { pub fn new(root: NaiveDate, other: NaiveDate) -> Self {
Self { Self {

View file

@ -5,7 +5,7 @@ use std::{fs, io, result};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use tzfile::Tz; use tzfile::Tz;
use self::commands::{Command, File}; use self::commands::{Command, Done, File};
pub mod commands; pub mod commands;
mod format; mod format;
@ -79,6 +79,9 @@ pub enum Error {
file2: PathBuf, file2: PathBuf,
tz2: String, tz2: String,
}, },
// TODO Add span or something similar for a nicer error message
#[error("Not a task")]
NotATask,
} }
pub type Result<T> = result::Result<T, Error>; pub type Result<T> = result::Result<T, Error>;
@ -161,6 +164,7 @@ impl Files {
pub fn save(&self) -> Result<()> { pub fn save(&self) -> Result<()> {
for file in &self.files { for file in &self.files {
if file.dirty { if file.dirty {
println!("Saving file {:?}", file.path);
Self::save_file(&file.path, &file.file)?; Self::save_file(&file.path, &file.file)?;
} }
} }
@ -185,6 +189,16 @@ impl Files {
&self.files[source.file].file.commands[source.command] &self.files[source.file].file.commands[source.command]
} }
pub fn add_done(&mut self, source: Source, done: Done) -> Result<()> {
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),
}
file.dirty = true;
Ok(())
}
pub fn commands(&self) -> Vec<SourcedCommand<'_>> { pub fn commands(&self) -> Vec<SourcedCommand<'_>> {
let mut result = vec![]; let mut result = vec![];
for (file_index, file) in self.files.iter().enumerate() { for (file_index, file) in self.files.iter().enumerate() {