summaryrefslogtreecommitdiff
path: root/src/parse.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/parse.rs')
-rw-r--r--src/parse.rs144
1 files changed, 144 insertions, 0 deletions
diff --git a/src/parse.rs b/src/parse.rs
new file mode 100644
index 0000000..079e83a
--- /dev/null
+++ b/src/parse.rs
@@ -0,0 +1,144 @@
+use std::str::FromStr;
+use chrono::{Datelike, NaiveDate, NaiveTime, Weekday, Duration, Timelike};
+
+// mod task;
+use crate::task::*;
+
+pub fn parse_file_str(file_str: &str) -> Vec<Task> {
+ let mut tasks = Vec::new();
+ let mut start_date = None;
+ let mut the_date = None;
+ for l in file_str.split('\n') {
+ if l.starts_with("# ") {
+ // '# 12/27/21', starts a new week block
+ start_date = parse_date_line(l);
+ } else if l.starts_with("## ") {
+ // '## Monday/Tuesday/...', starts a new day block
+ // Need to compute the actual date, basically looking for the first one after
+ // start_date.
+ let dayofweek = parse_day_line(l);
+ let mut current = start_date.expect("Invalid or missing '# ' date");
+ the_date = loop {
+ if current.weekday() == dayofweek {
+ break Some(current);
+ }
+ current = current.succ();
+ };
+ } else if l.starts_with("- [ ]") || l.starts_with("- [X]") {
+ // '- [ ] ...', starts a new task block
+ let date = the_date.expect("No current date parsed yet...");
+ tasks.push(Task {
+ date: date,
+ start_time: None,
+ end_time: None,
+ details: "".to_string(),
+ tags: Vec::new(),
+ });
+ 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.
+ handle_task_details(l, tasks.last_mut().expect("Unexpected error..."));
+ } else if l.trim().len() > 0 {
+ eprint!("Ignoring line: {}\n", l);
+ }
+ }
+ return tasks;
+}
+
+fn parse_date_line(l: &str) -> Option<NaiveDate> {
+ for maybe_date_str in l.split(' ') {
+ if let Ok(date) = NaiveDate::parse_from_str(maybe_date_str, "%m/%d/%y") {
+ return Some(date);
+ }
+ };
+ return None;
+}
+
+fn parse_day_line(l: &str) -> Weekday {
+ let daystr = l.get(3..).expect("Day-of-week line not long enough...");
+ return Weekday::from_str(daystr).expect("Misparse day-of-week str...");
+}
+
+fn parse_time(s_: &str) -> NaiveTime {
+ let formats = vec!["%l:%M%p", "%H:%M"];
+ let mut s = s_.to_string();
+ if !s.contains(":") {
+ if s.ends_with("M") {
+ s.insert_str(s.len() - 2, ":00");
+ } else {
+ s.push_str(":00");
+ }
+ }
+ for format in formats {
+ if let Ok(parsed) = NaiveTime::parse_from_str(&s, format) {
+ if !format.contains("%p") && parsed.hour() < 6 {
+ return parsed + Duration::hours(12);
+ }
+ return parsed;
+ }
+ }
+ panic!("Couldn't parse time {}", s);
+}
+
+fn parse_duration(s: &str) -> chrono::Duration {
+ // We try to find Mm, HhMm, Hh
+ if s.contains("h") && s.contains("m") {
+ // TODO: Decompose this case into the two below.
+ let hstr = s.split("h").collect::<Vec<&str>>().get(0).expect("").to_string();
+ let mstr = s.split("h").collect::<Vec<&str>>().get(1).expect("").split("m").collect::<Vec<&str>>().get(0).expect("Expected XhYm").to_string();
+ let secs = ((hstr.parse::<u64>().unwrap() * 60)
+ + (mstr.parse::<u64>().unwrap())) * 60;
+ return chrono::Duration::from_std(std::time::Duration::new(secs, 0)).unwrap();
+ } else if s.contains("h") {
+ let hstr = s.split("h").collect::<Vec<&str>>().get(0).expect("").to_string();
+ let secs = hstr.parse::<u64>().unwrap() * 60 * 60;
+ return chrono::Duration::from_std(std::time::Duration::new(secs, 0)).unwrap();
+ } else if s.contains("m") {
+ let hstr = s.split("m").collect::<Vec<&str>>().get(0).expect("").to_string();
+ let secs = hstr.parse::<u64>().unwrap() * 60;
+ return chrono::Duration::from_std(std::time::Duration::new(secs, 0)).unwrap();
+ }
+ panic!("Couldn't parse duration {}", s);
+}
+
+fn handle_task_details(l: &str, t: &mut Task) {
+ for tok in l.split(' ') {
+ if tok.starts_with("+") {
+ let tag = tok.get(1..).expect("Unexpected");
+ t.tags.push(tag.to_string());
+ } else if tok.starts_with("@") {
+ let timestr = tok.get(1..).expect("Unexpected");
+ if timestr.contains("+") { // @Start+Duration
+ let parts: Vec<&str> = timestr.split("+").collect();
+ match parts[..] {
+ [startstr, durstr] => {
+ t.start_time = Some(parse_time(startstr));
+ t.end_time = Some(t.start_time.unwrap() + parse_duration(durstr));
+ },
+ _ => panic!("Not 2 parts to {}\n", timestr)
+ }
+ } else if timestr.contains("--") { // @Start--End
+ let parts: Vec<&str> = timestr.split("--").collect();
+ match parts[..] {
+ [startstr, endstr] => {
+ t.start_time = Some(parse_time(startstr));
+ t.end_time = Some(parse_time(endstr));
+ if t.start_time > t.end_time {
+ panic!("Start time {} interpreted as after end time {}",
+ startstr, endstr);
+ }
+ },
+ _ => panic!("Not 2 parts to {}\n", timestr)
+ }
+ } else {
+ panic!("'{}' is not of the form Start+Duration or Start--End\n", timestr);
+ }
+ } else {
+ if t.details.len() > 0 {
+ t.details.push(' ');
+ }
+ t.details.push_str(tok.trim());
+ }
+ }
+}
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback