diff options
author | Matthew Sotoudeh <matthewsot@outlook.com> | 2022-01-23 21:32:41 -0800 |
---|---|---|
committer | Matthew Sotoudeh <matthewsot@outlook.com> | 2022-01-23 21:32:41 -0800 |
commit | 99286549bb6bd205a342bd98fc03223ec263ca45 (patch) | |
tree | 1041fe7b1b11a79ee9cd9e6e1a027c03308c9394 | |
parent | d4839fe311a49ac2484b12487b8da6f0ae203dca (diff) |
'./wtd describe [week]' command works
-rw-r--r-- | src/dates.rs | 12 | ||||
-rw-r--r-- | src/html_calendar.rs | 19 | ||||
-rw-r--r-- | src/main.rs | 24 | ||||
-rw-r--r-- | src/md_calendar.rs | 46 | ||||
-rw-r--r-- | src/parse.rs | 5 | ||||
-rw-r--r-- | src/task.rs | 1 |
6 files changed, 91 insertions, 16 deletions
diff --git a/src/dates.rs b/src/dates.rs index c4c9c0d..09aac82 100644 --- a/src/dates.rs +++ b/src/dates.rs @@ -52,11 +52,21 @@ fn week_file(week_start: NaiveDate) -> Option<String> { return path_if_exists(&filename); } +pub fn parse_week_str(week_str: &str) -> Option<NaiveDate> { + let mut cased = week_str.to_string(); + let upper_start = cased.remove(0).to_uppercase().to_string(); + cased.insert_str(0, &upper_start); + return match NaiveDate::parse_from_str(&cased, "%b_%d_%Y") { + Ok(d) => Some(d), + Err(_) => None, + } +} + fn path_if_exists(name: &str) -> Option<String> { return if Path::new(name).exists() { Some(name.to_string()) } else { None }; } -fn week_start_of(date: &NaiveDate) -> NaiveDate { +pub fn week_start_of(date: &NaiveDate) -> NaiveDate { // number_from_monday() is 1-indexed, for some reason... let days_from_monday = date.weekday().number_from_monday() - 1; return *date - Duration::days(days_from_monday.into()); diff --git a/src/html_calendar.rs b/src/html_calendar.rs index 2902caa..c050f77 100644 --- a/src/html_calendar.rs +++ b/src/html_calendar.rs @@ -2,7 +2,7 @@ use std::io::prelude::*; use std::fs::File; use std::collections::HashMap; use std::path::Path; -use chrono::{NaiveTime, Duration, Local}; +use chrono::{NaiveTime, Duration, NaiveDate}; use crate::task::*; pub enum CalendarPrivacy { @@ -10,14 +10,14 @@ pub enum CalendarPrivacy { Private, } -pub fn tasks_to_html_file(tasks: &Vec<Task>, privacy: CalendarPrivacy, file_name: &str) { - let html = tasks_to_html(tasks, privacy); +pub fn tasks_to_html_file(tasks: &Vec<Task>, first_date: NaiveDate, last_date: NaiveDate, privacy: CalendarPrivacy, file_name: &str) { + let html = tasks_to_html(tasks, first_date, last_date, 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<Task>, privacy: CalendarPrivacy) -> String { +fn tasks_to_html(tasks: &Vec<Task>, first_date: NaiveDate, last_date: NaiveDate, 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."), @@ -28,12 +28,11 @@ fn tasks_to_html(tasks: &Vec<Task>, privacy: CalendarPrivacy) -> String { let mut html = "<html><head><meta charset=\"UTF-8\"><title>Calendar</title><link rel=\"stylesheet\" href=\"calendar_style.css\"></link></head><body>".to_string(); - let n_days = 14; - let start_period = Local::now().date().naive_local(); - let end_period = start_period + Duration::days(n_days); + let n_days = (last_date - first_date).num_days() + 1; + let mut week_task_ids: Vec<usize> = Vec::new(); for (i, task) in tasks.iter().enumerate() { - if task.date >= start_period && task.date < end_period { + if first_date <= task.date && task.date <= last_date { week_task_ids.push(i); } } @@ -52,7 +51,7 @@ fn tasks_to_html(tasks: &Vec<Task>, privacy: CalendarPrivacy) -> String { html.push_str("<tr><th>Time</th>"); for offset in 0..n_days { html.push_str("<th>"); - html.push_str(&(start_period + Duration::days(offset)).format("%a %-m/%-d/%y").to_string()); + html.push_str(&(first_date + Duration::days(offset)).format("%a %-m/%-d/%y").to_string()); html.push_str("</th>"); } html.push_str("</tr>"); @@ -64,7 +63,7 @@ fn tasks_to_html(tasks: &Vec<Task>, privacy: CalendarPrivacy) -> String { let timespan_end = NaiveTime::from_hms(0, 0, 0) + Duration::minutes((i + 1) * min_incr); for offset in 0..n_days { // (1) Find all task ids that intersect this timespan on this day. - let this_date = start_period + Duration::days(offset); + let this_date = first_date + Duration::days(offset); let on_this_date: Vec<usize> = week_task_ids.iter().map(|&idx| idx) .filter(|&idx| tasks[idx].date == this_date).collect(); diff --git a/src/main.rs b/src/main.rs index 49c8152..59a7a0d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use std::env; -use chrono::{Local, Duration}; +use chrono::{Local, Duration, Datelike}; mod task; use task::*; @@ -7,6 +7,8 @@ mod parse; use parse::*; mod html_calendar; use html_calendar::*; +mod md_calendar; +use md_calendar::*; mod dates; use dates::*; @@ -25,12 +27,26 @@ fn command_html() { 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"); + tasks_to_html_file(&tasks, first_date, last_date, CalendarPrivacy::Public, &"public.html"); + tasks_to_html_file(&tasks, first_date, last_date, CalendarPrivacy::Private, &"private.html"); } fn command_describe(week: &str) { - // TODO + 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<Task> = vec![]; + for path in paths { + tasks.append(&mut tasks_from_path(&path)); + } + + let md = tasks_to_md(&tasks, first_date, last_date); + println!("{}", md); } fn command_generate(week: &str) { diff --git a/src/md_calendar.rs b/src/md_calendar.rs new file mode 100644 index 0000000..d010628 --- /dev/null +++ b/src/md_calendar.rs @@ -0,0 +1,46 @@ +use chrono::{Duration, NaiveDate}; +use crate::task::*; +use crate::dates::*; + +pub fn tasks_to_md(tasks: &Vec<Task>, first_date: NaiveDate, last_date: NaiveDate) -> String { + let first_week = week_start_of(&first_date); + let last_week = week_start_of(&last_date); // inclusive + + let mut md_out = "".to_string(); + + let mut curr_week = first_week; + while curr_week <= last_week { + let curr_week_last_date = curr_week + Duration::days(6); + + // Collect all the relevant tasks. + let mut week_task_ids: Vec<usize> = Vec::new(); + for (i, task) in tasks.iter().enumerate() { + if curr_week <= task.date && task.date <= curr_week_last_date + && first_date <= task.date && task.date <= last_date { + week_task_ids.push(i); + } + } + week_task_ids.sort_by(|a, b| cmp_tasks(&tasks[*a], &tasks[*b])); + + // Print the week. + if week_task_ids.len() > 0 { + md_out.push_str(&format!("# {}\n", curr_week.format("%-m/%-d/%y").to_string())); + let mut last_written_date = curr_week - Duration::days(1); + for task_id in week_task_ids { + let task = &tasks[task_id]; + if last_written_date != task.date { + if last_written_date != curr_week - Duration::days(1) { // TODO: something smarter + md_out.push('\n'); + } + md_out.push_str(&format!("## {}\n", task.date.format("%A").to_string())); + last_written_date = task.date; + } + md_out.push_str(&task.raw); + } + } + + // Next iter. + curr_week = curr_week + Duration::days(7); + } + return md_out; +} diff --git a/src/parse.rs b/src/parse.rs index d751579..876b861 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -6,7 +6,6 @@ use chrono::{Datelike, NaiveDate, NaiveTime, Weekday, Duration, Timelike}; use crate::task::*; use crate::dates::*; -use crate::html_calendar::*; // 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<Task> { @@ -58,12 +57,16 @@ fn parse_file_str(file_str: &str, implicit_week: Option<NaiveDate>) -> Vec<Task> start_time: None, end_time: None, details: "".to_string(), + raw: l.to_string(), tags: Vec::new(), }); + tasks.last_mut().expect("").raw.push('\n'); let details = l.get(5..).expect("").trim(); handle_task_details(details, tasks.last_mut().expect("Unexpected error...")); } else if l.starts_with(" ") { // Extends the last task. + tasks.last_mut().expect("").raw.push_str(l); + tasks.last_mut().expect("").raw.push('\n'); handle_task_details(l, tasks.last_mut().expect("Unexpected error...")); } else if l.trim().len() > 0 { eprint!("Ignoring line: {}\n", l); diff --git a/src/task.rs b/src/task.rs index 871d8ff..952a35e 100644 --- a/src/task.rs +++ b/src/task.rs @@ -6,6 +6,7 @@ pub struct Task { pub start_time: Option<NaiveTime>, pub end_time: Option<NaiveTime>, pub details: String, + pub raw: String, pub tags: Vec<String>, } |