diff --git a/src/cli.rs b/src/cli.rs index 1bf0aa8..910cb94 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,14 +1,15 @@ use std::path::PathBuf; use std::{process, result}; -use chrono::{Duration, NaiveDate}; +use chrono::{Duration, NaiveDate, NaiveDateTime}; use directories::ProjectDirs; use structopt::StructOpt; -use crate::eval::{DateRange, EntryMode}; +use crate::eval::{DateRange, Entry, EntryMode}; use crate::files::{self, Files}; use self::error::Result; +use self::layout::line::LineLayout; mod done; mod error; @@ -67,7 +68,63 @@ fn load_files(opt: &Opt) -> result::Result { Files::load(&file) } -pub fn run() -> Result<()> { +fn find_now(opt: &Opt, files: &Files) -> NaiveDateTime { + let now = files.now().naive_local(); + if let Some(date) = opt.date { + date.and_time(now.time()) + } else { + now + } +} + +fn find_range(opt: &Opt, now: NaiveDateTime) -> DateRange { + let range_date = opt.date.unwrap_or_else(|| now.date()); + DateRange::new( + range_date - Duration::days(opt.before.into()), + range_date + Duration::days(opt.after.into()), + ) + .expect("determine range") +} + +fn find_entries(files: &Files, range: DateRange) -> Result> { + Ok(files.eval(EntryMode::Relevant, range)?) +} + +fn find_layout( + files: &Files, + entries: &[Entry], + range: DateRange, + now: NaiveDateTime, +) -> LineLayout { + layout::layout(files, entries, range, now) +} + +fn run_command(opt: &Opt, files: &mut Files, range: DateRange, now: NaiveDateTime) -> Result<()> { + match &opt.command { + None => { + let entries = find_entries(files, range)?; + let layout = find_layout(files, &entries, range, now); + print::print(&layout); + } + Some(Command::Show { entries: ns }) => { + let entries = find_entries(files, range)?; + let layout = find_layout(files, &entries, range, now); + show::show(files, &entries, &layout, ns)?; + } + Some(Command::Done { entries: ns }) => { + let entries = find_entries(files, range)?; + let layout = find_layout(files, &entries, range, now); + done::done(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(()) +} + +pub fn run() { let opt = Opt::from_args(); let mut files = match load_files(&opt) { @@ -78,33 +135,16 @@ pub fn run() -> Result<()> { } }; - let now = files.now().naive_local(); + let now = find_now(&opt, &files); + let range = find_range(&opt, now); - let range_date = opt.date.unwrap_or_else(|| now.date()); - let range = DateRange::new( - range_date - Duration::days(opt.before.into()), - range_date + Duration::days(opt.after.into()), - ) - .expect("determine range"); - - let entries = files.eval(EntryMode::Relevant, range)?; - let layout = layout::layout(&files, &entries, range, now); - - match opt.command { - None => print::print(&layout), - Some(Command::Show { entries: numbers }) => { - show::show(&files, &entries, &layout, &numbers)? - } - Some(Command::Done { entries: numbers }) => { - done::done(&mut files, &entries, &layout, &numbers, now)? - } - Some(Command::Fmt) => files.mark_all_dirty(), + if let Err(e) = run_command(&opt, &mut files, range, now) { + e.print(&files); + process::exit(1); } if let Err(e) = files.save() { e.print(); process::exit(1); } - - Ok(()) } diff --git a/src/cli/error.rs b/src/cli/error.rs index e0d19e4..8f15b7a 100644 --- a/src/cli/error.rs +++ b/src/cli/error.rs @@ -1,19 +1,35 @@ use std::result; -use crate::{eval, files}; +use crate::eval; +use crate::files::Files; #[derive(Debug, thiserror::Error)] pub enum Error { - #[error("{0}")] - Files(#[from] files::Error), #[error("{0}")] Eval(#[from] eval::Error), - #[error("No number specified")] - NoNumber, #[error("No entry with number {0}")] NoSuchEntry(usize), #[error("Not a task")] NotATask(Vec), } +impl Error { + pub fn print(&self, files: &Files) { + match self { + Error::Eval(e) => e.print(files), + Error::NoSuchEntry(n) => eprintln!("No entry with number {}", n), + Error::NotATask(ns) => { + if ns.is_empty() { + eprintln!("Not a task."); + } else if ns.len() == 1 { + eprintln!("{} is not a task.", ns[0]); + } else { + let ns = ns.iter().map(|n| n.to_string()).collect::>(); + eprintln!("{} are not tasks.", ns.join(", ")); + } + } + } + } +} + pub type Result = result::Result; diff --git a/src/eval/error.rs b/src/eval/error.rs index 54abe79..7953740 100644 --- a/src/eval/error.rs +++ b/src/eval/error.rs @@ -3,6 +3,7 @@ use std::result; use chrono::NaiveDate; use crate::files::primitives::{Span, Time}; +use crate::files::Files; #[derive(Debug, thiserror::Error)] pub enum Error { @@ -62,4 +63,105 @@ pub enum Error { }, } +impl Error { + fn print_at(files: &Files, file: &usize, span: &Span, message: String) { + use pest::error as pe; + let (name, content) = files.file(*file).expect("file index is valid"); + let span = pest::Span::new(content, span.start, span.end).expect("span is valid"); + let variant = pe::ErrorVariant::<()>::CustomError { message }; + let error = pe::Error::new_from_span(variant, span).with_path(&name.to_string_lossy()); + eprintln!("{}", error); + } + + fn fmt_date_time(date: NaiveDate, time: Option