diff --git a/README.md b/README.md index 12e83cf..80bfa20 100644 --- a/README.md +++ b/README.md @@ -25,16 +25,15 @@ digital, automated version of that. ```bash git clone https://github.com/andrei-stoica/rusty-tasks.git -cd rusty-task -cargo install --path . +cd rusty-task cargo install --path . ``` Alternatively, there is a binary download for AMD64 Linux machines available on the [releases page](https://github.com/andrei-stoica/rusty-tasks/releases). -Just drop that anywhere on you PATH. I recommend adding `~/bin` to your PATH +Just drop that anywhere on your PATH. I recommend adding `~/bin` to your PATH and dropping the executable there. -If you are not on a AMD64 Linux machine, you will need to build from source. +If you are not on an AMD64 Linux machine, you will need to build from source. I have not tested this on other platforms, so I hesitate to provide binaries for them. @@ -46,6 +45,7 @@ Usage: rusty-tasks [OPTIONS] Options: -c, --config set config file to use -C, --current-config show current config file + -d, --date view a specific date's file (format: YYYY-MM-DD) -p, --previous view previous day's notes [default: 0] -l, --list list closest files to date -n, --number number of files to list [default: 5] @@ -61,6 +61,12 @@ Use `rust-task -p ` to access a previous day's file where `` is the number of days back you want to go. If a file does not exist for that day, it will default to the closest to that date. A value of 0 represents today's file. +Alternatively, use `--date` or `-d` to specify a date specifically. Preferably +in the format year-month-day, padding with zero is optional. However, if the +year, or, year and month are omitted they will be filled in with the current +date's year and month. For example, If the current date is `2024-2-30`, the +string `4` will resolve to `2024-2-4`, and `1-4` will resolve to `2024-1-4`. + Specify a custom config location with `-c`, otherwise, it will scan for a config in the locations specified in the [config section](#config). If no config exists it will create one. To see what config is being loaded you can use `-C`. diff --git a/src/cli/mod.rs b/src/cli/mod.rs index d3cba42..87fcde5 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,3 +1,4 @@ +use chrono::{Datelike, NaiveDate}; use clap::Parser; #[derive(Parser, Debug)] @@ -10,6 +11,9 @@ pub struct Args { #[arg(short = 'C', long)] pub current_config: bool, + /// view a specific date's file (YYYY-MM-DD) + #[arg(short, long)] + pub date: Option, /// view previous day's notes #[arg(short = 'p', long, default_value_t = 0)] pub previous: u16, @@ -27,3 +31,74 @@ pub struct Args { #[arg(short, long, action = clap::ArgAction::Count)] pub verbose: u8, } + +pub fn smart_parse_date(date_str: &str, cur_date: &NaiveDate) -> Option { + let full_date_fmt = "%Y-%m-%d"; + + if let Ok(date) = NaiveDate::parse_from_str(date_str, &full_date_fmt) { + return Some(date); + } + let parts: Vec<&str> = date_str.split('-').collect(); + + match parts.len() { + 1 => cur_date.with_day(parts[0].parse().unwrap_or(cur_date.day())), + 2 => cur_date + .with_day(parts[1].parse().unwrap_or(cur_date.day()))? + .with_month(parts[0].parse().unwrap_or(cur_date.month())), + 3 => NaiveDate::from_ymd_opt( + parts[0].parse().ok()?, + parts[1].parse().ok()?, + parts[2].parse().ok()?, + ), + _ => None, + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_smart_parse_date() { + let good_date = NaiveDate::from_ymd_opt(2024, 01, 03).expect("Invalid date specified"); + let good_date_str = good_date.format("%Y-%m-%d").to_string(); + + assert_eq!( + Some(good_date), + smart_parse_date(&good_date_str, &good_date) + ); + let no_padding_date_str = "2024-1-3"; + assert_eq!( + Some(good_date), + smart_parse_date(no_padding_date_str, &good_date) + ); + + let bad_day_str = "2024-01-99"; + assert_eq!(None, smart_parse_date(bad_day_str, &good_date)); + let no_day_str = "2024-01"; + assert_eq!(None, smart_parse_date(no_day_str, &good_date)); + + let bad_month_str = "2024-25-01"; + assert_eq!(None, smart_parse_date(bad_month_str, &good_date)); + let no_month_str = "2024-14"; + assert_eq!(None, smart_parse_date(no_month_str, &good_date)); + + let no_year_str = "01-03"; + assert_eq!(Some(good_date), smart_parse_date(no_year_str, &good_date)); + let bad_month_no_year_str = "25-01"; + assert_eq!(None, smart_parse_date(bad_month_no_year_str, &good_date)); + let bad_day_no_year_str = "01-35"; + assert_eq!(None, smart_parse_date(bad_day_no_year_str, &good_date)); + + let no_year_month_str = "03"; + assert_eq!( + Some(good_date), + smart_parse_date(no_year_month_str, &good_date) + ); + let bad_day_no_year_month_str = "35"; + assert_eq!( + None, + smart_parse_date(bad_day_no_year_month_str, &good_date) + ); + } +} diff --git a/src/main.rs b/src/main.rs index 2def14c..43f61e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -97,7 +97,11 @@ fn main() { // get clossest files to specified date let today = Local::now().date_naive(); - let target = today - TimeDelta::try_days(args.previous.into()).unwrap(); + let target = if let Some(date_str) = args.date { + cli::smart_parse_date(&date_str, &today).expect("Could not parse date") + } else { + today - TimeDelta::try_days(args.previous.into()).unwrap() + }; let closest_files = TodoFile::get_closest_files(files.collect(), target, args.number); // list files if args.list {