From d9fac382c8d3b60f6ea641d175326d4e6bc678ff Mon Sep 17 00:00:00 2001 From: Matthew Sotoudeh Date: Mon, 24 Jan 2022 08:19:57 -0800 Subject: 'wtd generate [week]' command builds --- src/main.rs | 78 ++++++++++++++++++++++++++++++++++++++++++------------------ src/parse.rs | 2 +- src/prune.rs | 49 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 24 deletions(-) create mode 100644 src/prune.rs diff --git a/src/main.rs b/src/main.rs index 06431e8..ea29cb7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use std::io::Write; +use std::fs::OpenOptions; use std::env; use chrono::{Local, Duration, Datelike}; @@ -11,6 +13,22 @@ mod md_calendar; use md_calendar::*; mod dates; use dates::*; +mod prune; +use prune::*; + +// https://doc.rust-lang.org/std/fs/struct.File.html +// https://doc.rust-lang.org/book/ch12-01-accepting-command-line-arguments.html +fn main() { + let args: Vec = env::args().collect(); + let str_args: Vec<&str> = args.iter().map(String::as_str).collect(); + match str_args[..] { + [_, "html"] => command_html(), + [_, "describe", week] => command_describe(&week), + [_, "generate", week] => command_generate(&week), + [_, "validate"] => command_validate(), + _ => panic!("Invalid command. Valid commands are: html, describe [week], generate [week], and validate.") + } +} fn command_html() { /* Pull tasks from: @@ -32,25 +50,36 @@ fn command_html() { } fn command_describe(week: &str) { + println!("{}", internal_describe(week)); +} + +fn command_generate(week: &str) { + /* This command basically does two things: + * 1) wtd describe [week] > [week].md + * 2) Prune all tasks from this week from wtd.md + */ + let new_week_file_contents = internal_describe(week); let first_date = parse_week_str(week).expect("Invalid week string provided. Should be, e.g., jan_24_2022"); if first_date.weekday().number_from_monday() != 1 { // 1-based, for some reason. panic!("The provided week should start on a Monday."); } - let last_date = first_date + Duration::days(6); // inclusive - - let paths = relevant_files(&first_date, &last_date); - let mut tasks: Vec = vec![]; - for path in paths { - tasks.append(&mut tasks_from_path(&path)); - } - - let md = tasks_to_md(&tasks, first_date, last_date); - println!("{}", md); + let new_wtd_contents = prune_week_from_file(first_date, "wtd.md"); + let week_file_name = first_date.format("%b_%d_%Y.md").to_string().to_lowercase(); + overwrite_file(&week_file_name, &new_week_file_contents); + overwrite_file("wtd.md", &new_wtd_contents); } -fn command_generate(week: &str) { - // TODO +fn overwrite_file(name: &str, contents: &str) { + let mut file = match OpenOptions::new().create(true).write(true).truncate(true).open(name) { + Err(why) => panic!("Error opening {}: {}", name, why), + Ok(file) => file, + }; + + match file.write_all(contents.as_bytes()) { + Err(why) => panic!("Couldn't write to {}: {}", name, why), + Ok(_) => {}, + }; } fn command_validate() { @@ -81,16 +110,19 @@ fn command_validate() { } } -// https://doc.rust-lang.org/std/fs/struct.File.html -// https://doc.rust-lang.org/book/ch12-01-accepting-command-line-arguments.html -fn main() { - let args: Vec = env::args().collect(); - let str_args: Vec<&str> = args.iter().map(String::as_str).collect(); - match str_args[..] { - [_, "html"] => command_html(), - [_, "describe", week] => command_describe(&week), - [_, "generate", week] => command_generate(&week), - [_, "validate"] => command_validate(), - _ => panic!("Invalid command. Valid commands are: html, describe [week], generate [week], and validate.") +fn internal_describe(week: &str) -> String { + let first_date + = parse_week_str(week).expect("Invalid week string provided. Should be, e.g., jan_24_2022"); + if first_date.weekday().number_from_monday() != 1 { // 1-based, for some reason. + panic!("The provided week should start on a Monday."); + } + let last_date = first_date + Duration::days(6); // inclusive + + let paths = relevant_files(&first_date, &last_date); + let mut tasks: Vec = vec![]; + for path in paths { + tasks.append(&mut tasks_from_path(&path)); } + + return tasks_to_md(&tasks, first_date, last_date); } diff --git a/src/parse.rs b/src/parse.rs index 876b861..6bed4d9 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -75,7 +75,7 @@ fn parse_file_str(file_str: &str, implicit_week: Option) -> Vec return tasks; } -fn parse_date_line(l: &str) -> Option { +pub fn parse_date_line(l: &str) -> Option { for maybe_date_str in l.split(' ') { if let Ok(date) = NaiveDate::parse_from_str(maybe_date_str, "%m/%d/%y") { return Some(date); diff --git a/src/prune.rs b/src/prune.rs new file mode 100644 index 0000000..dd7a5fb --- /dev/null +++ b/src/prune.rs @@ -0,0 +1,49 @@ +use std::path::Path; +use std::fs::File; +use std::io::prelude::*; +use chrono::{NaiveDate, Duration}; + +use crate::parse::*; + +pub fn prune_week_from_file(week: NaiveDate, file_str: &str) -> String { + let path = Path::new(file_str); + let display = path.display(); + + // Open the path in read-only mode, returns `io::Result` + let mut file = match File::open(&path) { + Err(why) => panic!("Error opening {}: {}", display, why), + Ok(file) => file, + }; + + // Read the file contents into a string, returns `io::Result` + let mut s = String::new(); + match file.read_to_string(&mut s) { + Err(why) => panic!("Couldn't read {}: {}", display, why), + Ok(_) => return prune_week_from_str(week, &s), + } +} + +fn prune_week_from_str(week: NaiveDate, file_str: &str) -> String { + let mut new_contents = "\n".to_string(); + let mut ghost = false; + let ghost_start = week - Duration::days(6); + let ghost_end = week + Duration::days(6); + for l in file_str.split('\n') { + if l.starts_with("# ") { + // '# 12/27/21', starts a new week block + let start_date = parse_date_line(l).expect("Couldn't parse '# ' header."); + ghost = ghost_start <= start_date && start_date <= ghost_end; + if ghost && start_date != week { + panic!("generate currently assumes `# ` headers always start on Mondays."); + } + } + if !ghost { + new_contents.push_str(l); + new_contents.push('\n'); + } + } + // Remove leading & trailing newlines. + new_contents.remove(0); + new_contents.pop(); + return new_contents; +} -- cgit v1.2.3