Show logs in addition to entries

This commit is contained in:
Joscha 2022-01-07 22:34:00 +01:00
parent badc0d7a9f
commit 3e2fa54213
5 changed files with 90 additions and 41 deletions

View file

@ -8,7 +8,7 @@ use directories::ProjectDirs;
use structopt::StructOpt; use structopt::StructOpt;
use crate::eval::{self, DateRange, Entry, EntryMode}; 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 crate::files::{self, FileSource, Files, ParseError};
use self::error::Error; use self::error::Error;
@ -41,9 +41,9 @@ pub enum Command {
#[allow(rustdoc::broken_intra_doc_links)] #[allow(rustdoc::broken_intra_doc_links)]
/// Shows individual entries in detail /// Shows individual entries in detail
Show { Show {
/// Entries to show /// Entries and days to show
#[structopt(required = true)] #[structopt(required = true)]
entries: Vec<usize>, identifiers: Vec<String>,
}, },
/// Marks one or more entries as done /// Marks one or more entries as done
Done { Done {
@ -113,6 +113,21 @@ where
None None
} }
fn parse_show_idents(identifiers: &[String], today: NaiveDate) -> Vec<show::Ident> {
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( fn run_command(
opt: &Opt, opt: &Opt,
files: &mut Files, files: &mut Files,
@ -125,10 +140,11 @@ fn run_command(
let layout = find_layout(files, &entries, range, now); let layout = find_layout(files, &entries, range, now);
print::print(&layout); print::print(&layout);
} }
Some(Command::Show { entries: ns }) => { Some(Command::Show { identifiers }) => {
let entries = find_entries(files, range)?; let entries = find_entries(files, range)?;
let layout = find_layout(files, &entries, range, now); 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 }) => { Some(Command::Done { entries: ns }) => {
let entries = find_entries(files, range)?; let entries = find_entries(files, range)?;

View file

@ -1,3 +1,4 @@
use chrono::NaiveDate;
use codespan_reporting::files::Files; use codespan_reporting::files::Files;
use codespan_reporting::term::Config; use codespan_reporting::term::Config;
@ -10,6 +11,8 @@ pub enum Error<S> {
Eval(#[from] eval::Error<S>), Eval(#[from] eval::Error<S>),
#[error("No entry with number {0}")] #[error("No entry with number {0}")]
NoSuchEntry(usize), NoSuchEntry(usize),
#[error("No log for {0}")]
NoSuchLog(NaiveDate),
#[error("Not a task")] #[error("Not a task")]
NotATask(Vec<usize>), NotATask(Vec<usize>),
} }
@ -19,6 +22,7 @@ impl<'a, F: Files<'a>> Eprint<'a, F> for Error<F::FileId> {
match self { match self {
Error::Eval(e) => e.eprint(files, config), Error::Eval(e) => e.eprint(files, config),
Error::NoSuchEntry(n) => eprintln!("No entry with number {}", n), Error::NoSuchEntry(n) => eprintln!("No entry with number {}", n),
Error::NoSuchLog(date) => eprintln!("No log for {}", date),
Error::NotATask(ns) => { Error::NotATask(ns) => {
if ns.is_empty() { if ns.is_empty() {
eprintln!("Not a task."); eprintln!("Not a task.");

View file

@ -1,9 +1,18 @@
use chrono::NaiveDate;
use crate::eval::{Entry, EntryKind}; 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::error::Error;
use super::layout::line::LineLayout; 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) { fn show_entry(files: &Files, entry: &Entry) {
let command = files.command(entry.source); let command = files.command(entry.source);
@ -28,32 +37,46 @@ fn show_entry(files: &Files, entry: &Entry) {
} }
println!("FROM COMMAND"); println!("FROM COMMAND");
for line in format!("{}", command.command).lines() { show_command(command.value);
println!("| {}", line); }
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<S>( #[derive(Debug, Clone, Copy)]
files: &Files, pub enum Ident {
entries: &[Entry], Number(usize),
layout: &LineLayout, Date(NaiveDate),
numbers: &[usize], }
) -> Result<(), Error<S>> {
if numbers.is_empty() { pub fn show(files: &Files, entries: &[Entry], layout: &LineLayout, idents: &[Ident]) {
if idents.is_empty() {
// Nothing to do // Nothing to do
return Ok(()); return;
} }
let indices = numbers show_ident(files, entries, layout, idents[0]);
.iter() for &ident in idents.iter().skip(1) {
.map(|n| layout.look_up_number(*n))
.collect::<Result<Vec<usize>, _>>()?;
show_entry(files, &entries[indices[0]]);
for &index in indices.iter().skip(1) {
println!(); println!();
show_entry(files, &entries[index]); show_ident(files, entries, layout, ident);
} }
Ok(())
} }

View file

@ -24,7 +24,7 @@ impl Files {
let mut entries = Entries::new(mode, range); let mut entries = Entries::new(mode, range);
for command in self.commands() { for command in self.commands() {
let source = command.source; 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() { for entry in CommandState::new(command, source, range).eval()?.entries() {
entries.add(entry); entries.add(entry);
} }

View file

@ -62,9 +62,15 @@ impl Source {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct SourcedCommand<'a> { pub struct Sourced<'a, T> {
pub source: Source, 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)] #[derive(Debug)]
@ -212,7 +218,7 @@ impl Files {
let mut found: Option<(Source, Spanned<String>)> = None; let mut found: Option<(Source, Spanned<String>)> = None;
for command in self.commands() { 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 let Some((found_source, found_tz)) = &found {
if tz.value != found_tz.value { if tz.value != found_tz.value {
return Err(Error::TzConflict { return Err(Error::TzConflict {
@ -247,14 +253,14 @@ impl Files {
fn collect_logs(&mut self) -> Result<()> { fn collect_logs(&mut self) -> Result<()> {
for command in Self::commands_of_files(&self.files) { 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) { match self.logs.entry(log.date.value) {
Entry::Vacant(e) => { Entry::Vacant(e) => {
e.insert(command.source); e.insert(command.source);
} }
Entry::Occupied(e) => { Entry::Occupied(e) => {
let other_cmd = Self::command_of_files(&self.files, *e.get()); 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, Command::Log(log) => log.date.span,
_ => unreachable!(), _ => unreachable!(),
}; };
@ -301,34 +307,34 @@ impl Files {
/* Querying */ /* Querying */
fn commands_of_files(files: &[LoadedFile]) -> Vec<SourcedCommand<'_>> { fn commands_of_files(files: &[LoadedFile]) -> Vec<Sourced<'_, Command>> {
let mut result = vec![]; let mut result = vec![];
for (file_index, file) in files.iter().enumerate() { for (file_index, file) in files.iter().enumerate() {
for (command_index, command) in file.file.commands.iter().enumerate() { for (command_index, command) in file.file.commands.iter().enumerate() {
let source = Source::new(file_index, command_index); let source = Source::new(file_index, command_index);
result.push(SourcedCommand { source, command }); result.push(Sourced::new(source, command));
} }
} }
result result
} }
pub fn commands(&self) -> Vec<SourcedCommand<'_>> { pub fn commands(&self) -> Vec<Sourced<'_, Command>> {
Self::commands_of_files(&self.files) 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]; 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) Self::command_of_files(&self.files, source)
} }
pub fn log(&self, date: NaiveDate) -> Option<&Log> { pub fn log(&self, date: NaiveDate) -> Option<Sourced<'_, Log>> {
let source = *self.logs.get(&date)?; let source = *self.logs.get(&date)?;
match self.command(source).command { match self.command(source).value {
Command::Log(log) => Some(log), Command::Log(log) => Some(Sourced::new(source, log)),
_ => unreachable!(), _ => unreachable!(),
} }
} }