Refactor CLI argument parsing
This commit is contained in:
parent
4a46e70a73
commit
2f6911eeca
5 changed files with 112 additions and 45 deletions
10
src/cli.rs
10
src/cli.rs
|
|
@ -8,7 +8,7 @@ use directories::ProjectDirs;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use crate::eval::{DateRange, Entry, EntryMode};
|
use crate::eval::{DateRange, Entry, EntryMode};
|
||||||
use crate::files::arguments::Range;
|
use crate::files::arguments::CliRange;
|
||||||
use crate::files::{self, FileSource, Files};
|
use crate::files::{self, FileSource, Files};
|
||||||
|
|
||||||
use self::error::Error;
|
use self::error::Error;
|
||||||
|
|
@ -29,8 +29,8 @@ pub struct Opt {
|
||||||
/// Overwrite the current date
|
/// Overwrite the current date
|
||||||
#[structopt(short, long)]
|
#[structopt(short, long)]
|
||||||
date: Option<NaiveDate>,
|
date: Option<NaiveDate>,
|
||||||
/// The range days to focus on
|
/// Range of days to focus on
|
||||||
#[structopt(short, long, default_value = "today-2d--today+13d")]
|
#[structopt(short, long, default_value = "t-2d--t+13d")]
|
||||||
range: String,
|
range: String,
|
||||||
#[structopt(subcommand)]
|
#[structopt(subcommand)]
|
||||||
command: Option<Command>,
|
command: Option<Command>,
|
||||||
|
|
@ -57,7 +57,7 @@ pub enum Command {
|
||||||
#[structopt(required = true)]
|
#[structopt(required = true)]
|
||||||
entries: Vec<usize>,
|
entries: Vec<usize>,
|
||||||
},
|
},
|
||||||
/// Reformat all loaded files
|
/// Reformats all loaded files
|
||||||
Fmt,
|
Fmt,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,7 +145,7 @@ pub fn run() {
|
||||||
let now = find_now(&opt, &files);
|
let now = find_now(&opt, &files);
|
||||||
|
|
||||||
// Kinda ugly, but it can stay for now (until it grows at least).
|
// Kinda ugly, but it can stay for now (until it grows at least).
|
||||||
let range = match Range::from_str(&opt.range) {
|
let range = match CliRange::from_str(&opt.range) {
|
||||||
Ok(range) => match range.eval((), now.date()) {
|
Ok(range) => match range.eval((), now.date()) {
|
||||||
Ok(range) => range,
|
Ok(range) => range,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
|
||||||
28
src/eval.rs
28
src/eval.rs
|
|
@ -1,6 +1,6 @@
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
|
|
||||||
use crate::files::arguments::{Range, RangeDate};
|
use crate::files::arguments::{CliDate, CliDatum, CliRange};
|
||||||
use crate::files::{FileSource, Files};
|
use crate::files::{FileSource, Files};
|
||||||
|
|
||||||
use self::command::{CommandState, EvalCommand};
|
use self::command::{CommandState, EvalCommand};
|
||||||
|
|
@ -34,11 +34,27 @@ impl Files {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Range {
|
impl CliDate {
|
||||||
|
pub fn eval<S: Copy>(&self, index: S, today: NaiveDate) -> Result<NaiveDate, Error<S>> {
|
||||||
|
let mut date = match self.datum {
|
||||||
|
CliDatum::Date(d) => d,
|
||||||
|
CliDatum::Today => today,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(delta) = &self.delta {
|
||||||
|
let delta: Delta = delta.into();
|
||||||
|
date = delta.apply_date(index, date)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CliRange {
|
||||||
pub fn eval<S: Copy>(&self, index: S, today: NaiveDate) -> Result<DateRange, Error<S>> {
|
pub fn eval<S: Copy>(&self, index: S, today: NaiveDate) -> Result<DateRange, Error<S>> {
|
||||||
let mut start = match self.start {
|
let mut start = match self.start {
|
||||||
RangeDate::Date(d) => d,
|
CliDatum::Date(d) => d,
|
||||||
RangeDate::Today => today,
|
CliDatum::Today => today,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(delta) = &self.start_delta {
|
if let Some(delta) = &self.start_delta {
|
||||||
|
|
@ -49,8 +65,8 @@ impl Range {
|
||||||
let mut end = start;
|
let mut end = start;
|
||||||
|
|
||||||
match self.end {
|
match self.end {
|
||||||
Some(RangeDate::Date(d)) => end = d,
|
Some(CliDatum::Date(d)) => end = d,
|
||||||
Some(RangeDate::Today) => end = today,
|
Some(CliDatum::Today) => end = today,
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,35 +8,83 @@ use super::commands::Delta;
|
||||||
use super::parse::{self, Error, Result, Rule, TodayfileParser};
|
use super::parse::{self, Error, Result, Rule, TodayfileParser};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum RangeDate {
|
pub enum CliDatum {
|
||||||
Date(NaiveDate),
|
Date(NaiveDate),
|
||||||
Today,
|
Today,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
fn parse_cli_datum(p: Pair<'_, Rule>) -> Result<CliDatum> {
|
||||||
pub struct Range {
|
assert_eq!(p.as_rule(), Rule::cli_datum);
|
||||||
pub start: RangeDate,
|
let p = p.into_inner().next().unwrap();
|
||||||
pub start_delta: Option<Delta>,
|
|
||||||
pub end: Option<RangeDate>,
|
|
||||||
pub end_delta: Option<Delta>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parsing */
|
|
||||||
|
|
||||||
fn parse_range_date(p: Pair<'_, Rule>) -> Result<RangeDate> {
|
|
||||||
assert!(matches!(p.as_rule(), Rule::datum | Rule::today));
|
|
||||||
Ok(match p.as_rule() {
|
Ok(match p.as_rule() {
|
||||||
Rule::datum => RangeDate::Date(parse::parse_datum(p)?.value),
|
Rule::datum => CliDatum::Date(parse::parse_datum(p)?.value),
|
||||||
Rule::today => RangeDate::Today,
|
Rule::today => CliDatum::Today,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_range_start(p: Pair<'_, Rule>) -> Result<(RangeDate, Option<Delta>)> {
|
#[derive(Debug)]
|
||||||
assert_eq!(p.as_rule(), Rule::range_start);
|
pub struct CliDate {
|
||||||
|
pub datum: CliDatum,
|
||||||
|
pub delta: Option<Delta>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_cli_date(p: Pair<'_, Rule>) -> Result<CliDate> {
|
||||||
|
assert_eq!(p.as_rule(), Rule::cli_date);
|
||||||
let mut p = p.into_inner();
|
let mut p = p.into_inner();
|
||||||
|
|
||||||
let start = parse_range_date(p.next().unwrap())?;
|
let datum = parse_cli_datum(p.next().unwrap())?;
|
||||||
|
let delta = match p.next() {
|
||||||
|
Some(p) => Some(parse::parse_delta(p)?.value),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(p.next(), None);
|
||||||
|
|
||||||
|
Ok(CliDate { datum, delta })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum CliIdent {
|
||||||
|
Number(usize),
|
||||||
|
Date(CliDate),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_cli_ident(p: Pair<'_, Rule>) -> Result<CliIdent> {
|
||||||
|
assert_eq!(p.as_rule(), Rule::cli_ident);
|
||||||
|
let p = p.into_inner().next().unwrap();
|
||||||
|
Ok(match p.as_rule() {
|
||||||
|
Rule::number => CliIdent::Number(parse::parse_number(p) as usize),
|
||||||
|
Rule::cli_date => CliIdent::Date(parse_cli_date(p)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for CliIdent {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
|
let mut pairs = TodayfileParser::parse(Rule::cli_ident, s)?;
|
||||||
|
let p = pairs.next().unwrap();
|
||||||
|
assert_eq!(pairs.next(), None);
|
||||||
|
|
||||||
|
parse_cli_ident(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CliRange {
|
||||||
|
pub start: CliDatum,
|
||||||
|
pub start_delta: Option<Delta>,
|
||||||
|
pub end: Option<CliDatum>,
|
||||||
|
pub end_delta: Option<Delta>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_cli_range_start(p: Pair<'_, Rule>) -> Result<(CliDatum, Option<Delta>)> {
|
||||||
|
assert_eq!(p.as_rule(), Rule::cli_range_start);
|
||||||
|
let mut p = p.into_inner();
|
||||||
|
|
||||||
|
let start = parse_cli_datum(p.next().unwrap())?;
|
||||||
let start_delta = match p.next() {
|
let start_delta = match p.next() {
|
||||||
None => None,
|
None => None,
|
||||||
Some(p) => Some(parse::parse_delta(p)?.value),
|
Some(p) => Some(parse::parse_delta(p)?.value),
|
||||||
|
|
@ -47,15 +95,15 @@ fn parse_range_start(p: Pair<'_, Rule>) -> Result<(RangeDate, Option<Delta>)> {
|
||||||
Ok((start, start_delta))
|
Ok((start, start_delta))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_range_end(p: Pair<'_, Rule>) -> Result<(Option<RangeDate>, Option<Delta>)> {
|
fn parse_cli_range_end(p: Pair<'_, Rule>) -> Result<(Option<CliDatum>, Option<Delta>)> {
|
||||||
assert_eq!(p.as_rule(), Rule::range_end);
|
assert_eq!(p.as_rule(), Rule::cli_range_end);
|
||||||
|
|
||||||
let mut end = None;
|
let mut end = None;
|
||||||
let mut end_delta = None;
|
let mut end_delta = None;
|
||||||
|
|
||||||
for p in p.into_inner() {
|
for p in p.into_inner() {
|
||||||
match p.as_rule() {
|
match p.as_rule() {
|
||||||
Rule::datum | Rule::today => end = Some(parse_range_date(p)?),
|
Rule::cli_datum => end = Some(parse_cli_datum(p)?),
|
||||||
Rule::delta => end_delta = Some(parse::parse_delta(p)?.value),
|
Rule::delta => end_delta = Some(parse::parse_delta(p)?.value),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
@ -64,18 +112,18 @@ fn parse_range_end(p: Pair<'_, Rule>) -> Result<(Option<RangeDate>, Option<Delta
|
||||||
Ok((end, end_delta))
|
Ok((end, end_delta))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_range(p: Pair<'_, Rule>) -> Result<Range> {
|
fn parse_cli_range(p: Pair<'_, Rule>) -> Result<CliRange> {
|
||||||
assert_eq!(p.as_rule(), Rule::range);
|
assert_eq!(p.as_rule(), Rule::cli_range);
|
||||||
let mut p = p.into_inner();
|
let mut p = p.into_inner();
|
||||||
|
|
||||||
let (start, start_delta) = parse_range_start(p.next().unwrap())?;
|
let (start, start_delta) = parse_cli_range_start(p.next().unwrap())?;
|
||||||
let (end, end_delta) = match p.next() {
|
let (end, end_delta) = match p.next() {
|
||||||
// For some reason, the EOI gets captured but the SOI doesn't.
|
// For some reason, the EOI gets captured but the SOI doesn't.
|
||||||
Some(p) if p.as_rule() != Rule::EOI => parse_range_end(p)?,
|
Some(p) if p.as_rule() != Rule::EOI => parse_cli_range_end(p)?,
|
||||||
_ => (None, None),
|
_ => (None, None),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Range {
|
Ok(CliRange {
|
||||||
start,
|
start,
|
||||||
start_delta,
|
start_delta,
|
||||||
end,
|
end,
|
||||||
|
|
@ -83,14 +131,14 @@ fn parse_range(p: Pair<'_, Rule>) -> Result<Range> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Range {
|
impl FromStr for CliRange {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
let mut pairs = TodayfileParser::parse(Rule::range, s)?;
|
let mut pairs = TodayfileParser::parse(Rule::cli_range, s)?;
|
||||||
let p = pairs.next().unwrap();
|
let p = pairs.next().unwrap();
|
||||||
assert_eq!(pairs.next(), None);
|
assert_eq!(pairs.next(), None);
|
||||||
|
|
||||||
parse_range(p)
|
parse_cli_range(p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,10 @@ command = { include | timezone | task | note | log }
|
||||||
|
|
||||||
file = ${ SOI ~ (empty_line* ~ command)* ~ empty_line* ~ WHITESPACE* ~ EOI }
|
file = ${ SOI ~ (empty_line* ~ command)* ~ empty_line* ~ WHITESPACE* ~ EOI }
|
||||||
|
|
||||||
today = { "today" }
|
today = { "today" | "t" }
|
||||||
range_start = { (datum | today) ~ delta? }
|
cli_datum = { datum | today }
|
||||||
range_end = { (datum | today) ~ delta? | delta }
|
cli_date = { cli_datum ~ delta? }
|
||||||
range = { SOI ~ range_start ~ ("--" ~ range_end)? ~ EOI}
|
cli_ident = { SOI ~ (number | cli_date) ~ EOI }
|
||||||
|
cli_range_start = { cli_datum ~ delta? }
|
||||||
|
cli_range_end = { cli_datum ~ delta? | delta }
|
||||||
|
cli_range = { SOI ~ cli_range_start ~ ("--" ~ cli_range_end)? ~ EOI }
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ fn parse_timezone(p: Pair<'_, Rule>) -> Spanned<String> {
|
||||||
Spanned::new(span, name)
|
Spanned::new(span, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_number(p: Pair<'_, Rule>) -> i32 {
|
pub fn parse_number(p: Pair<'_, Rule>) -> i32 {
|
||||||
assert_eq!(p.as_rule(), Rule::number);
|
assert_eq!(p.as_rule(), Rule::number);
|
||||||
p.as_str().parse().unwrap()
|
p.as_str().parse().unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue