From 3e2fa542139778c32c555c5a37117e9582560936 Mon Sep 17 00:00:00 2001 From: Joscha Date: Fri, 7 Jan 2022 22:34:00 +0100 Subject: [PATCH] Show logs in addition to entries --- src/cli.rs | 26 +++++++++++++++---- src/cli/error.rs | 4 +++ src/cli/show.rs | 65 ++++++++++++++++++++++++++++++++---------------- src/eval.rs | 2 +- src/files.rs | 34 ++++++++++++++----------- 5 files changed, 90 insertions(+), 41 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index df1624f..5baf98c 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -8,7 +8,7 @@ use directories::ProjectDirs; use structopt::StructOpt; use crate::eval::{self, DateRange, Entry, EntryMode}; -use crate::files::arguments::CliRange; +use crate::files::arguments::{CliIdent, CliRange}; use crate::files::{self, FileSource, Files, ParseError}; use self::error::Error; @@ -41,9 +41,9 @@ pub enum Command { #[allow(rustdoc::broken_intra_doc_links)] /// Shows individual entries in detail Show { - /// Entries to show + /// Entries and days to show #[structopt(required = true)] - entries: Vec, + identifiers: Vec, }, /// Marks one or more entries as done Done { @@ -113,6 +113,21 @@ where None } +fn parse_show_idents(identifiers: &[String], today: NaiveDate) -> Vec { + let mut idents = vec![]; + for ident in identifiers { + let ident = match parse_eval_arg("identifier", ident, |ident: CliIdent| match ident { + CliIdent::Number(n) => Ok(show::Ident::Number(n)), + CliIdent::Date(d) => Ok(show::Ident::Date(d.eval((), today)?)), + }) { + Some(ident) => ident, + None => process::exit(1), + }; + idents.push(ident); + } + idents +} + fn run_command( opt: &Opt, files: &mut Files, @@ -125,10 +140,11 @@ fn run_command( let layout = find_layout(files, &entries, range, now); print::print(&layout); } - Some(Command::Show { entries: ns }) => { + Some(Command::Show { identifiers }) => { let entries = find_entries(files, range)?; let layout = find_layout(files, &entries, range, now); - show::show(files, &entries, &layout, ns)?; + let idents = parse_show_idents(identifiers, now.date()); + show::show(files, &entries, &layout, &idents); } Some(Command::Done { entries: ns }) => { let entries = find_entries(files, range)?; diff --git a/src/cli/error.rs b/src/cli/error.rs index a84a0df..7ad1b01 100644 --- a/src/cli/error.rs +++ b/src/cli/error.rs @@ -1,3 +1,4 @@ +use chrono::NaiveDate; use codespan_reporting::files::Files; use codespan_reporting::term::Config; @@ -10,6 +11,8 @@ pub enum Error { Eval(#[from] eval::Error), #[error("No entry with number {0}")] NoSuchEntry(usize), + #[error("No log for {0}")] + NoSuchLog(NaiveDate), #[error("Not a task")] NotATask(Vec), } @@ -19,6 +22,7 @@ impl<'a, F: Files<'a>> Eprint<'a, F> for Error { match self { Error::Eval(e) => e.eprint(files, config), Error::NoSuchEntry(n) => eprintln!("No entry with number {}", n), + Error::NoSuchLog(date) => eprintln!("No log for {}", date), Error::NotATask(ns) => { if ns.is_empty() { eprintln!("Not a task."); diff --git a/src/cli/show.rs b/src/cli/show.rs index 1b433eb..07c648c 100644 --- a/src/cli/show.rs +++ b/src/cli/show.rs @@ -1,9 +1,18 @@ +use chrono::NaiveDate; + use crate::eval::{Entry, EntryKind}; -use crate::files::Files; +use crate::files::commands::{Command, Log}; +use crate::files::{Files, Sourced}; use super::error::Error; use super::layout::line::LineLayout; +fn show_command(command: &Command) { + for line in format!("{}", command).lines() { + println!("| {}", line); + } +} + fn show_entry(files: &Files, entry: &Entry) { let command = files.command(entry.source); @@ -28,32 +37,46 @@ fn show_entry(files: &Files, entry: &Entry) { } println!("FROM COMMAND"); - for line in format!("{}", command.command).lines() { - println!("| {}", line); + show_command(command.value); +} + +fn show_log(files: &Files, log: Sourced<'_, Log>) { + let command = files.command(log.source); + + println!("LOG {}", log.value.date); + + println!("FROM COMMAND"); + show_command(command.value); +} + +fn show_ident(files: &Files, entries: &[Entry], layout: &LineLayout, ident: Ident) { + match ident { + Ident::Number(n) => match layout.look_up_number::<()>(n) { + Ok(index) => show_entry(files, &entries[index]), + Err(e) => println!("{}", e), + }, + Ident::Date(date) => match files.log(date) { + Some(log) => show_log(files, log), + None => println!("{}", Error::NoSuchLog::<()>(date)), + }, } } -pub fn show( - files: &Files, - entries: &[Entry], - layout: &LineLayout, - numbers: &[usize], -) -> Result<(), Error> { - if numbers.is_empty() { +#[derive(Debug, Clone, Copy)] +pub enum Ident { + Number(usize), + Date(NaiveDate), +} + +pub fn show(files: &Files, entries: &[Entry], layout: &LineLayout, idents: &[Ident]) { + if idents.is_empty() { // Nothing to do - return Ok(()); + return; } - let indices = numbers - .iter() - .map(|n| layout.look_up_number(*n)) - .collect::, _>>()?; - - show_entry(files, &entries[indices[0]]); - for &index in indices.iter().skip(1) { + show_ident(files, entries, layout, idents[0]); + for &ident in idents.iter().skip(1) { println!(); - show_entry(files, &entries[index]); + show_ident(files, entries, layout, ident); } - - Ok(()) } diff --git a/src/eval.rs b/src/eval.rs index def8a51..b0ca8cf 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -24,7 +24,7 @@ impl Files { let mut entries = Entries::new(mode, range); for command in self.commands() { let source = command.source; - if let Some(command) = EvalCommand::new(command.command) { + if let Some(command) = EvalCommand::new(command.value) { for entry in CommandState::new(command, source, range).eval()?.entries() { entries.add(entry); } diff --git a/src/files.rs b/src/files.rs index f1e170c..3f32812 100644 --- a/src/files.rs +++ b/src/files.rs @@ -62,9 +62,15 @@ impl Source { } #[derive(Debug)] -pub struct SourcedCommand<'a> { +pub struct Sourced<'a, T> { pub source: Source, - pub command: &'a Command, + pub value: &'a T, +} + +impl<'a, T> Sourced<'a, T> { + fn new(source: Source, value: &'a T) -> Self { + Self { source, value } + } } #[derive(Debug)] @@ -212,7 +218,7 @@ impl Files { let mut found: Option<(Source, Spanned)> = None; for command in self.commands() { - if let Command::Timezone(tz) = command.command { + if let Command::Timezone(tz) = command.value { if let Some((found_source, found_tz)) = &found { if tz.value != found_tz.value { return Err(Error::TzConflict { @@ -247,14 +253,14 @@ impl Files { fn collect_logs(&mut self) -> Result<()> { for command in Self::commands_of_files(&self.files) { - if let Command::Log(log) = command.command { + if let Command::Log(log) = command.value { match self.logs.entry(log.date.value) { Entry::Vacant(e) => { e.insert(command.source); } Entry::Occupied(e) => { let other_cmd = Self::command_of_files(&self.files, *e.get()); - let other_span = match &other_cmd.command { + let other_span = match &other_cmd.value { Command::Log(log) => log.date.span, _ => unreachable!(), }; @@ -301,34 +307,34 @@ impl Files { /* Querying */ - fn commands_of_files(files: &[LoadedFile]) -> Vec> { + fn commands_of_files(files: &[LoadedFile]) -> Vec> { let mut result = vec![]; for (file_index, file) in files.iter().enumerate() { for (command_index, command) in file.file.commands.iter().enumerate() { let source = Source::new(file_index, command_index); - result.push(SourcedCommand { source, command }); + result.push(Sourced::new(source, command)); } } result } - pub fn commands(&self) -> Vec> { + pub fn commands(&self) -> Vec> { Self::commands_of_files(&self.files) } - fn command_of_files(files: &[LoadedFile], source: Source) -> SourcedCommand<'_> { + fn command_of_files(files: &[LoadedFile], source: Source) -> Sourced<'_, Command> { let command = &files[source.file].file.commands[source.command]; - SourcedCommand { source, command } + Sourced::new(source, command) } - pub fn command(&self, source: Source) -> SourcedCommand<'_> { + pub fn command(&self, source: Source) -> Sourced<'_, Command> { Self::command_of_files(&self.files, source) } - pub fn log(&self, date: NaiveDate) -> Option<&Log> { + pub fn log(&self, date: NaiveDate) -> Option> { let source = *self.logs.get(&date)?; - match self.command(source).command { - Command::Log(log) => Some(log), + match self.command(source).value { + Command::Log(log) => Some(Sourced::new(source, log)), _ => unreachable!(), } }