Refactor CLI argument parsing

This commit is contained in:
Joscha 2022-01-07 20:04:31 +01:00
parent 4a46e70a73
commit 2f6911eeca
5 changed files with 112 additions and 45 deletions

View file

@ -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) => {

View file

@ -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 => {}
} }

View file

@ -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)
} }
} }

View file

@ -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 }

View file

@ -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()
} }