Implement --range
This commit is contained in:
parent
ff627b98df
commit
e9ec2998f0
4 changed files with 91 additions and 33 deletions
46
src/cli.rs
46
src/cli.rs
|
|
@ -1,11 +1,13 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::str::FromStr;
|
||||||
use std::{process, result};
|
use std::{process, result};
|
||||||
|
|
||||||
use chrono::{Duration, NaiveDate, NaiveDateTime};
|
use chrono::{NaiveDate, NaiveDateTime};
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use crate::eval::{DateRange, Entry, EntryMode};
|
use crate::eval::{DateRange, Entry, EntryMode, SourceInfo};
|
||||||
|
use crate::files::arguments::Range;
|
||||||
use crate::files::{self, Files};
|
use crate::files::{self, Files};
|
||||||
|
|
||||||
use self::error::Result;
|
use self::error::Result;
|
||||||
|
|
@ -25,14 +27,9 @@ pub struct Opt {
|
||||||
/// Overwrite the current date
|
/// Overwrite the current date
|
||||||
#[structopt(short, long)]
|
#[structopt(short, long)]
|
||||||
date: Option<NaiveDate>,
|
date: Option<NaiveDate>,
|
||||||
// TODO Allow negative numbers for `before` and `after`
|
/// The range days to focus on
|
||||||
// TODO Or just allow any Delta
|
#[structopt(short, long, default_value = "today-2d--today+13d")]
|
||||||
/// How many days to include before the current date
|
range: String,
|
||||||
#[structopt(short, long, default_value = "3")]
|
|
||||||
before: u32,
|
|
||||||
/// How many days to include after the current date
|
|
||||||
#[structopt(short, long, default_value = "13")]
|
|
||||||
after: u32,
|
|
||||||
#[structopt(subcommand)]
|
#[structopt(subcommand)]
|
||||||
command: Option<Command>,
|
command: Option<Command>,
|
||||||
}
|
}
|
||||||
|
|
@ -77,15 +74,6 @@ fn find_now(opt: &Opt, files: &Files) -> NaiveDateTime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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<Vec<Entry>> {
|
fn find_entries(files: &Files, range: DateRange) -> Result<Vec<Entry>> {
|
||||||
Ok(files.eval(EntryMode::Relevant, range)?)
|
Ok(files.eval(EntryMode::Relevant, range)?)
|
||||||
}
|
}
|
||||||
|
|
@ -136,7 +124,25 @@ pub fn run() {
|
||||||
};
|
};
|
||||||
|
|
||||||
let now = find_now(&opt, &files);
|
let now = find_now(&opt, &files);
|
||||||
let range = find_range(&opt, now);
|
|
||||||
|
// Kinda ugly, but it can stay for now (until it grows at least).
|
||||||
|
let range = match Range::from_str(&opt.range) {
|
||||||
|
Ok(range) => match range.eval(0, now.date()) {
|
||||||
|
Ok(range) => range,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to evaluate --range:");
|
||||||
|
e.print(&[SourceInfo {
|
||||||
|
name: Some("--range".to_string()),
|
||||||
|
content: &opt.range,
|
||||||
|
}]);
|
||||||
|
process::exit(1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to parse --range:\n{}", e.with_path("--range"));
|
||||||
|
process::exit(1)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if let Err(e) = run_command(&opt, &mut files, range, now) {
|
if let Err(e) = run_command(&opt, &mut files, range, now) {
|
||||||
e.print(&files.sources());
|
e.print(&files.sources());
|
||||||
|
|
|
||||||
35
src/eval.rs
35
src/eval.rs
|
|
@ -1,10 +1,14 @@
|
||||||
|
use chrono::NaiveDate;
|
||||||
|
|
||||||
|
use crate::files::arguments::{Range, RangeDate};
|
||||||
use crate::files::Files;
|
use crate::files::Files;
|
||||||
|
|
||||||
use self::command::CommandState;
|
use self::command::CommandState;
|
||||||
pub use self::date::Dates;
|
pub use self::date::Dates;
|
||||||
|
use self::delta::Delta;
|
||||||
use self::entry::Entries;
|
use self::entry::Entries;
|
||||||
pub use self::entry::{Entry, EntryKind, EntryMode};
|
pub use self::entry::{Entry, EntryKind, EntryMode};
|
||||||
pub use self::error::{Error, Result};
|
pub use self::error::{Error, Result, SourceInfo};
|
||||||
pub use self::range::DateRange;
|
pub use self::range::DateRange;
|
||||||
|
|
||||||
mod command;
|
mod command;
|
||||||
|
|
@ -26,3 +30,32 @@ impl Files {
|
||||||
Ok(entries.entries())
|
Ok(entries.entries())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Range {
|
||||||
|
pub fn eval(&self, index: usize, today: NaiveDate) -> Result<DateRange> {
|
||||||
|
let mut start = match self.start {
|
||||||
|
RangeDate::Date(d) => d,
|
||||||
|
RangeDate::Today => today,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(delta) = &self.start_delta {
|
||||||
|
let delta: Delta = delta.into();
|
||||||
|
start = delta.apply_date(index, start)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut end = start;
|
||||||
|
|
||||||
|
match self.end {
|
||||||
|
Some(RangeDate::Date(d)) => end = d,
|
||||||
|
Some(RangeDate::Today) => end = today,
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(delta) = &self.end_delta {
|
||||||
|
let delta: Delta = delta.into();
|
||||||
|
end = delta.apply_date(index, end)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(DateRange::new(start, end))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,11 @@ impl<'a> CommandState<'a> {
|
||||||
.filter(|&until| until < range_until)
|
.filter(|&until| until < range_until)
|
||||||
.unwrap_or(range_until);
|
.unwrap_or(range_until);
|
||||||
|
|
||||||
DateRange::new(from, until)
|
if from <= until {
|
||||||
|
Some(DateRange::new(from, until))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an entry, respecting [`Self::from`] and [`Self::until`]. Does not
|
/// Add an entry, respecting [`Self::from`] and [`Self::until`]. Does not
|
||||||
|
|
|
||||||
|
|
@ -12,20 +12,37 @@ pub struct DateRange {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DateRange {
|
impl DateRange {
|
||||||
pub fn new(from: NaiveDate, until: NaiveDate) -> Option<Self> {
|
pub fn new(from: NaiveDate, until: NaiveDate) -> Self {
|
||||||
if from <= until {
|
if from <= until {
|
||||||
Some(Self { from, until })
|
Self { from, until }
|
||||||
|
} else {
|
||||||
|
Self {
|
||||||
|
from: until,
|
||||||
|
until: from,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a new range with its [`Self::from`] set to a new value.
|
||||||
|
///
|
||||||
|
/// Returns [`None`] if the new value is later than [`Self::until`].
|
||||||
|
pub fn with_from(&self, from: NaiveDate) -> Option<Self> {
|
||||||
|
if from <= self.until {
|
||||||
|
Some(Self::new(from, self.until))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_from(&self, from: NaiveDate) -> Option<Self> {
|
/// Return a new range with its [`Self::until`] set to a new value.
|
||||||
Self::new(from, self.until)
|
///
|
||||||
}
|
/// Returns [`None`] if the new value is earlier than [`Self::from`].
|
||||||
|
|
||||||
pub fn with_until(&self, until: NaiveDate) -> Option<Self> {
|
pub fn with_until(&self, until: NaiveDate) -> Option<Self> {
|
||||||
Self::new(self.from, until)
|
if self.from <= until {
|
||||||
|
Some(Self::new(self.from, until))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn containing(&self, date: NaiveDate) -> Self {
|
pub fn containing(&self, date: NaiveDate) -> Self {
|
||||||
|
|
@ -75,8 +92,7 @@ impl DateRange {
|
||||||
self.from + Duration::days(expand_lower.into()),
|
self.from + Duration::days(expand_lower.into()),
|
||||||
self.until + Duration::days(expand_upper.into()),
|
self.until + Duration::days(expand_upper.into()),
|
||||||
)
|
)
|
||||||
// The range is never shrunk, so the new range should always be valid.
|
// The range should never shrink.
|
||||||
.expect("expanded range shrunk")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a new range that contains at least all dates from which the
|
/// Return a new range that contains at least all dates from which the
|
||||||
|
|
@ -90,7 +106,6 @@ impl DateRange {
|
||||||
self.until + Duration::days(move_upper.into()),
|
self.until + Duration::days(move_upper.into()),
|
||||||
)
|
)
|
||||||
// The delta's upper bound is greater or equal than its lower bound, so
|
// The delta's upper bound is greater or equal than its lower bound, so
|
||||||
// the range should never become smaller. It can only move and expand.
|
// the range should never shrink. It can only move and expand.
|
||||||
.expect("moved range shrunk")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue