Show logs in addition to entries
This commit is contained in:
parent
badc0d7a9f
commit
3e2fa54213
5 changed files with 90 additions and 41 deletions
26
src/cli.rs
26
src/cli.rs
|
|
@ -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)?;
|
||||||
|
|
|
||||||
|
|
@ -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.");
|
||||||
|
|
|
||||||
|
|
@ -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(())
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
34
src/files.rs
34
src/files.rs
|
|
@ -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!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue