diff --git a/src/main.rs b/src/main.rs index 981234a..f224694 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ mod todo; use crate::config::Config; use crate::todo::{Status as TaskStatus, TaskGroup}; -use crate::todo::file::TodoFile; +use crate::todo::File as TodoFile; use chrono::naive::NaiveDate; use chrono::{Datelike, Local}; use comrak::nodes::{AstNode, NodeValue}; diff --git a/src/todo/file.rs b/src/todo/file.rs index 57f7c17..b0326a0 100644 --- a/src/todo/file.rs +++ b/src/todo/file.rs @@ -5,12 +5,12 @@ use std::fs::DirEntry; use std::str::FromStr; #[derive(Debug)] -pub struct TodoFile { +pub struct File { pub file: DirEntry, pub date: NaiveDate, } -impl TodoFile { +impl File { fn capture_as_number(capture: ®ex::Captures, name: &str) -> Result { Ok(capture .name(name) @@ -21,7 +21,7 @@ impl TodoFile { .ok_or("Something went wrong".to_owned())?) } - pub fn latest_file(a: TodoFile, b: TodoFile) -> TodoFile { + pub fn latest_file(a: File, b: File) -> File { if a.date > b.date { a } else { @@ -36,11 +36,11 @@ impl TodoFile { } } -impl TryFrom for TodoFile { +impl TryFrom for File { type Error = String; fn try_from(direntry: DirEntry) -> Result { - let re = TodoFile::get_file_regex(); + let re = File::get_file_regex(); // println!("{:?}", re); let file_name = direntry.file_name(); let file_name_str = match file_name.to_str() { diff --git a/src/todo/mod.rs b/src/todo/mod.rs index 96e9542..fcd1155 100644 --- a/src/todo/mod.rs +++ b/src/todo/mod.rs @@ -1,130 +1,5 @@ -pub mod file; +mod file; +mod tasks; -use std::borrow::Borrow; - -use comrak::nodes::AstNode; -use comrak::nodes::NodeValue; - -#[derive(Debug, Clone)] -pub struct TaskGroup { - pub name: String, - pub tasks: Vec, - pub level: u8, -} - -// This does not support subtasks, need to figure out best path forward -#[derive(Debug, Clone)] -pub struct Task { - pub status: Status, - pub text: String, -} - -#[derive(Debug, PartialEq, Clone)] -pub enum Status { - Done(char), - Todo(char), - Empty, -} - -impl Task { - fn find_text<'a>(node: &'a AstNode<'a>) -> String { - let mut text = String::new(); - for child in node.descendants() { - let data_ref = child.data.borrow(); - if let NodeValue::Text(contents) = &data_ref.value { - text.push_str(format!("{}\n ", &contents.clone()).as_str()); - }; - } - text - } -} - -impl ToString for Task { - fn to_string(&self) -> String { - let ch = match self.status { - Status::Done(ch) => ch, - Status::Todo(ch) => ch, - Status::Empty => ' ', - }; - format!(" - [{}] {}\n", ch, self.text.trim()) - } -} - -impl<'a> TryFrom<&'a AstNode<'a>> for Task { - type Error = String; - fn try_from(node: &'a AstNode<'a>) -> Result { - let data_ref = &node.data.borrow(); - if let NodeValue::TaskItem(ch) = data_ref.value { - let text = Self::find_text(node); - let status = match ch { - Some(c) if c == 'x' || c == 'X' => Status::Done(c), - Some(c) => Status::Todo(c), - _ => Status::Empty, - }; - - Ok(Self { status, text }) - } else { - Err("Node being parsed is not a TaskItem".into()) - } - } -} - -impl TaskGroup { - pub fn empty(name: String, level: u8) -> TaskGroup { - TaskGroup { - name, - tasks: Vec::new(), - level, - } - } -} -impl ToString for TaskGroup { - fn to_string(&self) -> String { - let mut output = String::new(); - output.push_str(format!("{} {}\n", "#".repeat(self.level.into()), self.name).as_str()); - self.tasks - .iter() - .for_each(|task| output.push_str(task.to_string().as_str())); - - output - } -} - -impl<'a> TryFrom<&'a AstNode<'a>> for TaskGroup { - type Error = String; - fn try_from(node: &'a AstNode<'a>) -> Result { - let node_ref = &node.data.borrow(); - if let NodeValue::Heading(heading) = node_ref.value { - let level = heading.level; - let first_child_ref = &node.first_child(); - let first_child = if let Some(child) = first_child_ref.borrow() { - child - } else { - return Err("".into()); - }; - - let data_ref = &first_child.data.borrow(); - let name = if let NodeValue::Text(value) = &data_ref.value { - value.to_string() - } else { - return Err("".into()); - }; - - let next_sib = node.next_sibling().ok_or("Empty section at end of file")?; - - if let NodeValue::List(_list_meta) = next_sib.data.borrow().value { - let tasks = next_sib - .children() - .into_iter() - .filter_map(|item_node| Task::try_from(item_node).ok()) - .collect(); - - Ok(TaskGroup { name, tasks, level }) - } else { - Err("Next sibling of node is not a list".into()) - } - } else { - Err("Node is not a section heading".into()) - } - } -} +pub use file::File; +pub use tasks::{Status, Task, TaskGroup}; diff --git a/src/todo/tasks.rs b/src/todo/tasks.rs new file mode 100644 index 0000000..4d50ab7 --- /dev/null +++ b/src/todo/tasks.rs @@ -0,0 +1,128 @@ +use std::borrow::Borrow; + +use comrak::nodes::AstNode; +use comrak::nodes::NodeValue; + +#[derive(Debug, Clone)] +pub struct TaskGroup { + pub name: String, + pub tasks: Vec, + pub level: u8, +} + +// This does not support subtasks, need to figure out best path forward +#[derive(Debug, Clone)] +pub struct Task { + pub status: Status, + pub text: String, +} + +#[derive(Debug, PartialEq, Clone)] +pub enum Status { + Done(char), + Todo(char), + Empty, +} + +impl Task { + fn find_text<'a>(node: &'a AstNode<'a>) -> String { + let mut text = String::new(); + for child in node.descendants() { + let data_ref = child.data.borrow(); + if let NodeValue::Text(contents) = &data_ref.value { + text.push_str(format!("{}\n ", &contents.clone()).as_str()); + }; + } + text + } +} + +impl ToString for Task { + fn to_string(&self) -> String { + let ch = match self.status { + Status::Done(ch) => ch, + Status::Todo(ch) => ch, + Status::Empty => ' ', + }; + format!(" - [{}] {}\n", ch, self.text.trim()) + } +} + +impl<'a> TryFrom<&'a AstNode<'a>> for Task { + type Error = String; + fn try_from(node: &'a AstNode<'a>) -> Result { + let data_ref = &node.data.borrow(); + if let NodeValue::TaskItem(ch) = data_ref.value { + let text = Self::find_text(node); + let status = match ch { + Some(c) if c == 'x' || c == 'X' => Status::Done(c), + Some(c) => Status::Todo(c), + _ => Status::Empty, + }; + + Ok(Self { status, text }) + } else { + Err("Node being parsed is not a TaskItem".into()) + } + } +} + +impl TaskGroup { + pub fn empty(name: String, level: u8) -> TaskGroup { + TaskGroup { + name, + tasks: Vec::new(), + level, + } + } +} +impl ToString for TaskGroup { + fn to_string(&self) -> String { + let mut output = String::new(); + output.push_str(format!("{} {}\n", "#".repeat(self.level.into()), self.name).as_str()); + self.tasks + .iter() + .for_each(|task| output.push_str(task.to_string().as_str())); + + output + } +} + +impl<'a> TryFrom<&'a AstNode<'a>> for TaskGroup { + type Error = String; + fn try_from(node: &'a AstNode<'a>) -> Result { + let node_ref = &node.data.borrow(); + if let NodeValue::Heading(heading) = node_ref.value { + let level = heading.level; + let first_child_ref = &node.first_child(); + let first_child = if let Some(child) = first_child_ref.borrow() { + child + } else { + return Err("".into()); + }; + + let data_ref = &first_child.data.borrow(); + let name = if let NodeValue::Text(value) = &data_ref.value { + value.to_string() + } else { + return Err("".into()); + }; + + let next_sib = node.next_sibling().ok_or("Empty section at end of file")?; + + if let NodeValue::List(_list_meta) = next_sib.data.borrow().value { + let tasks = next_sib + .children() + .into_iter() + .filter_map(|item_node| Task::try_from(item_node).ok()) + .collect(); + + Ok(TaskGroup { name, tasks, level }) + } else { + Err("Next sibling of node is not a list".into()) + } + } else { + Err("Node is not a section heading".into()) + } + } +}