Implement done command
This commit is contained in:
parent
0de89bd424
commit
b476a95afb
6 changed files with 67 additions and 20 deletions
15
src/cli.rs
15
src/cli.rs
|
|
@ -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
16
src/cli/done.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
|
@ -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),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
16
src/files.rs
16
src/files.rs
|
|
@ -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() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue