From f12dfa1375cc9bf88f4e8defc400b462e9d895aa Mon Sep 17 00:00:00 2001 From: Matthew Sotoudeh Date: Sun, 23 Jan 2022 20:00:30 -0800 Subject: Locate which files to be read for advanced mode support --- README.md | 2 +- src/dates.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++ src/html_calendar.rs | 12 ++++++++- src/main.rs | 71 +++++++++++++++++++++++++++++++++------------------- src/parse.rs | 28 +++++++++++++++++++-- 5 files changed, 143 insertions(+), 30 deletions(-) create mode 100644 src/dates.rs diff --git a/README.md b/README.md index 3f8ba1b..942577b 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ these `public_tags`, see `calendar_style.css`. To generate the HTML calendars, run: ``` -$ cargo run +$ cargo run html ``` You should now see `public.html` and `private.html` in the current directory. diff --git a/src/dates.rs b/src/dates.rs new file mode 100644 index 0000000..fc3863c --- /dev/null +++ b/src/dates.rs @@ -0,0 +1,60 @@ +use std::path::Path; +// use std::str::FromStr; +// use chrono::{Datelike, NaiveDate, NaiveTime, Weekday, Duration, Timelike}; +use chrono::{Datelike, NaiveDate, Duration}; + +pub struct WTDPath<'a> { + pub path: &'a Path, + pub week_start: Option, + pub is_weekly_file: bool, +} + +// both start, end are inclusive. +pub fn relevant_files<'a>(start: &'a NaiveDate, end: &'a NaiveDate) -> Vec> { + let mut paths: Vec = vec![]; + if let Some(path) = maybe_path("wtd.md".to_string()) { + paths.push(WTDPath { + path: &path, + week_start: None, + is_weekly_file: false, + }); + } + + let weekly = maybe_path("weekly.md".to_string()); + + let start_week = week_start_of(start); + let end_week = week_start_of(end); // inclusive + + let mut curr_week = start_week; + while curr_week <= end_week { + let maybe_week_path = week_file(curr_week); + let path = match [maybe_week_path, weekly] { + [Some(path), _] => path, + [_, Some(path)] => path, + [None, None] => continue, + }; + paths.push(WTDPath { + path: path, + week_start: Some(curr_week), + is_weekly_file: maybe_week_path.is_none(), + }); + curr_week = curr_week + Duration::weeks(1); + } + + return paths; +} + +fn week_file(week_start: NaiveDate) -> Option<&'static Path> { + let filename = week_start.format("%b_%d_%Y.md").to_string().to_lowercase(); + return maybe_path(filename); +} + +fn maybe_path(name: String) -> Option<&'static Path> { + let path = Path::new("wtd.md"); + return if path.exists() { Some(&path) } else { None }; +} + +fn week_start_of(date: &NaiveDate) -> NaiveDate { + let days_from_monday = date.weekday().number_from_monday(); + return *date - Duration::days(days_from_monday.into()); +} diff --git a/src/html_calendar.rs b/src/html_calendar.rs index b16b987..2902caa 100644 --- a/src/html_calendar.rs +++ b/src/html_calendar.rs @@ -1,4 +1,7 @@ +use std::io::prelude::*; +use std::fs::File; use std::collections::HashMap; +use std::path::Path; use chrono::{NaiveTime, Duration, Local}; use crate::task::*; @@ -7,7 +10,14 @@ pub enum CalendarPrivacy { Private, } -pub fn tasks_to_html(tasks: &Vec, privacy: CalendarPrivacy) -> String { +pub fn tasks_to_html_file(tasks: &Vec, privacy: CalendarPrivacy, file_name: &str) { + let html = tasks_to_html(tasks, privacy); + // https://riptutorial.com/rust/example/4276/write-in-a-file + let mut file = File::create(Path::new(file_name)).unwrap(); + writeln!(&mut file, "{}", html).unwrap(); +} + +fn tasks_to_html(tasks: &Vec, privacy: CalendarPrivacy) -> String { let public_tags = HashMap::from([ ("busy", "I will be genuinely busy, e.g., a meeting with others."), ("rough", "The nature of the event (e.g., a hike) makes it difficult to preduct the exact start/end times."), diff --git a/src/main.rs b/src/main.rs index 6dfb441..49c8152 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,37 +1,56 @@ -use std::fs::File; -use std::io::prelude::*; -use std::path::Path; +use std::env; +use chrono::{Local, Duration}; mod task; +use task::*; mod parse; use parse::*; mod html_calendar; use html_calendar::*; +mod dates; +use dates::*; -// https://doc.rust-lang.org/std/fs/struct.File.html -fn main() { - let path = Path::new("wtd.md"); - let display = path.display(); +fn command_html() { + /* Pull tasks from: + * - wtd.md (always, assuming exists) + * - [week].md (assuming exists) + * - weekly.md (only included for weeks without a week.md) + */ + let n_days = 14; + let first_date = Local::now().date().naive_local(); + let last_date = first_date + Duration::days(n_days - 1); // 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)); + } + + tasks_to_html_file(&tasks, CalendarPrivacy::Public, &"public.html"); + tasks_to_html_file(&tasks, CalendarPrivacy::Private, &"private.html"); +} + +fn command_describe(week: &str) { + // TODO +} + +fn command_generate(week: &str) { + // TODO +} - // 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, - }; +fn command_validate() { + // TODO +} - // 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(_) => { - let tasks = parse_file_str(&s); - let public_html = tasks_to_html(&tasks, CalendarPrivacy::Public); - let private_html = tasks_to_html(&tasks, CalendarPrivacy::Private); - // https://riptutorial.com/rust/example/4276/write-in-a-file - let mut public = File::create(Path::new("public.html")).unwrap(); - writeln!(&mut public, "{}", public_html).unwrap(); - let mut private = File::create(Path::new("private.html")).unwrap(); - writeln!(&mut private, "{}", private_html).unwrap(); - } +// 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.") } } diff --git a/src/parse.rs b/src/parse.rs index 079e83a..a896cc2 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,10 +1,34 @@ +use std::fs::File; +use std::io::prelude::*; use std::str::FromStr; use chrono::{Datelike, NaiveDate, NaiveTime, Weekday, Duration, Timelike}; -// mod task; use crate::task::*; +use crate::dates::*; +use crate::html_calendar::*; -pub fn parse_file_str(file_str: &str) -> Vec { +// Translates a Path into a vector of tasks. Panics if the path cannot be opened or read from. +pub fn tasks_from_path(wtd_path: &WTDPath) -> Vec { + let path = wtd_path.path; + 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 parse_file_str(&s); + } + } +} + +fn parse_file_str(file_str: &str) -> Vec { let mut tasks = Vec::new(); let mut start_date = None; let mut the_date = None; -- cgit v1.2.3