diff --git a/src/main.rs b/src/main.rs index 881bd56..aac4f6f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,17 @@ mod config; +mod todo; mod todo_file; use crate::config::Config; +use crate::todo::{Status as TaskStatus, TaskGroup}; use crate::todo_file::TodoFile; use chrono::naive::NaiveDate; use chrono::{Datelike, Local}; -use comrak::nodes::{AstNode, NodeHeading, NodeValue}; -use comrak::{format_commonmark, parse_document, Arena}; +use comrak::nodes::{AstNode, NodeValue}; +use comrak::{parse_document, Arena}; use comrak::{ComrakExtensionOptions, ComrakOptions, ComrakParseOptions}; use std::borrow::Borrow; -use std::collections::HashSet; +use std::collections::HashMap; use std::env; use std::fs::{read, read_dir, File}; use std::io::Write; @@ -20,7 +22,7 @@ use std::str; //TODO handle unwraps and errors more uniformly //TODO clean up verbose printing -//TODO create config for passing options to different files +//TODO create custom errors for better error handling fn main() { let expected_cfg_files = Config::expected_locations().unwrap(); @@ -41,14 +43,12 @@ fn main() { println!("{:#?}", cfg); let data_dir = get_data_dir("notes"); - println!("{}", data_dir.to_str().unwrap()); + println!("dir = {}", data_dir.to_str().unwrap()); let latest_file = get_latest_file(&data_dir).expect(format!("Could not find any notes files").as_str()); println!("Latest file: {:?}", latest_file); - let mut editor = Command::new(cfg.editor.expect("Could not resovle edidtor from config")); - let now = Local::now(); let today = NaiveDate::from_ymd_opt(now.year(), now.month(), now.day()); let current_file = match today { @@ -62,30 +62,52 @@ fn main() { ); let mut today_file_path = data_dir.clone(); today_file_path.push(today_file_name); - let sections: HashSet = - HashSet::from_iter(cfg.sections.clone().unwrap().into_iter()); let arena = Arena::new(); let root = parse_todo_file(&latest_file, &arena); - let found_sections: HashSet = HashSet::from_iter( - &mut cleanup_sections(&root, &cfg.sections.unwrap()).into_iter(), + //println!("{:#?}", root); + + //println!("======================================================="); + + let sections = &cfg.sections.unwrap(); + let groups = extract_secitons(root, sections); + println!("{:#?}", groups); + + let level = groups.values().map(|group| group.level).min().unwrap_or(2); + + sections + .iter() + .map(|section| match groups.get(section) { + Some(group) => group.clone(), + None => TaskGroup::empty(section.to_string(), level), + }) + .for_each(|task_group| println!("{}", task_group.to_string())); + + let mut content = format!( + "# Today's tasks {}-{:02}-{:02}\n", + today.year(), + today.month(), + today.day() ); - let missing_sections: Vec<&String> = sections.symmetric_difference(&found_sections).collect(); + sections + .iter() + .map(|section| match groups.get(section) { + Some(group) => group.clone(), + None => TaskGroup::empty(section.to_string(), level), + }) + .for_each(|task_group| { + content.push_str(format!("\n{}", task_group.to_string()).as_str()) + }); - - let mut new_doc = vec![]; - format_commonmark(root, &ComrakOptions::default(), &mut new_doc).unwrap(); - for section in missing_sections.iter().map(|s| format!("\n## {}\n", s)) { - new_doc.append(&mut section.as_bytes().to_vec()) - } - let mut new_file = File::create(today_file_path.clone()).unwrap(); - new_file.write_all(&new_doc).unwrap(); + let mut file = File::create(today_file_path.clone()) + .expect("Could not open today's file: {today_file_path}"); + write!(file, "{}", content).expect("Could not write to file: {today_file_path}"); Some(today_file_path) } Some(_) => { - println!("Todays file was created"); + println!("Today's file was created"); Some(latest_file.file.path()) } _ => { @@ -95,13 +117,12 @@ fn main() { }; if let Some(file) = current_file { - editor + Command::new(cfg.editor.expect("Could not resolve editor from config")) .args([file]) .status() .expect(format!("failed to launch editor {}", "vim").as_str()); }; } - fn parse_todo_file<'a>(file: &TodoFile, arena: &'a Arena>) -> &'a AstNode<'a> { let options = &ComrakOptions { extension: ComrakExtensionOptions { @@ -128,15 +149,17 @@ fn parse_todo_file<'a>(file: &TodoFile, arena: &'a Arena>) -> &'a As parse_document(arena, contents, options) } -fn cleanup_sections<'a>(root: &'a AstNode<'a>, sections: &Vec) -> Vec { - let mut found_sections: Vec = Vec::new(); +fn extract_secitons<'a>( + root: &'a AstNode<'a>, + sections: &Vec, +) -> HashMap { + let mut groups: HashMap = HashMap::new(); for node in root.reverse_children() { let node_ref = &node.data.borrow(); if let NodeValue::Heading(heading) = node_ref.value { - if heading.level < 3 { + if heading.level < 2 { continue; } - println!("at level {}", heading.level); let first_child_ref = &node.first_child(); let first_child = if let Some(child) = first_child_ref.borrow() { @@ -152,27 +175,20 @@ fn cleanup_sections<'a>(root: &'a AstNode<'a>, sections: &Vec) -> Vec break, - _ => node.detach(), - } + println!("Attempting to parse {}", title); + if sections.iter().any(|section| section.eq(title)) { + if let Ok(mut group) = TaskGroup::try_from(node) { + group.tasks = group + .tasks + .into_iter() + .filter(|task| !matches!(task.status, TaskStatus::Done(_))) + .collect(); + groups.insert(title.to_string(), group); } - node.detach(); // remove heading as well - } else { - found_sections.push(title.to_string()); } }; } - found_sections + groups } fn get_data_dir(dir_name: &str) -> PathBuf {