todo module refactor
This commit is contained in:
parent
c78071d132
commit
86750a7428
|
|
@ -3,7 +3,7 @@ mod todo;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::todo::{Status as TaskStatus, TaskGroup};
|
use crate::todo::{Status as TaskStatus, TaskGroup};
|
||||||
use crate::todo::file::TodoFile;
|
use crate::todo::File as TodoFile;
|
||||||
use chrono::naive::NaiveDate;
|
use chrono::naive::NaiveDate;
|
||||||
use chrono::{Datelike, Local};
|
use chrono::{Datelike, Local};
|
||||||
use comrak::nodes::{AstNode, NodeValue};
|
use comrak::nodes::{AstNode, NodeValue};
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@ use std::fs::DirEntry;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TodoFile {
|
pub struct File {
|
||||||
pub file: DirEntry,
|
pub file: DirEntry,
|
||||||
pub date: NaiveDate,
|
pub date: NaiveDate,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TodoFile {
|
impl File {
|
||||||
fn capture_as_number<T: FromStr>(capture: ®ex::Captures, name: &str) -> Result<T, String> {
|
fn capture_as_number<T: FromStr>(capture: ®ex::Captures, name: &str) -> Result<T, String> {
|
||||||
Ok(capture
|
Ok(capture
|
||||||
.name(name)
|
.name(name)
|
||||||
|
|
@ -21,7 +21,7 @@ impl TodoFile {
|
||||||
.ok_or("Something went wrong".to_owned())?)
|
.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 {
|
if a.date > b.date {
|
||||||
a
|
a
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -36,11 +36,11 @@ impl TodoFile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<DirEntry> for TodoFile {
|
impl TryFrom<DirEntry> for File {
|
||||||
type Error = String;
|
type Error = String;
|
||||||
|
|
||||||
fn try_from(direntry: DirEntry) -> Result<Self, Self::Error> {
|
fn try_from(direntry: DirEntry) -> Result<Self, Self::Error> {
|
||||||
let re = TodoFile::get_file_regex();
|
let re = File::get_file_regex();
|
||||||
// println!("{:?}", re);
|
// println!("{:?}", re);
|
||||||
let file_name = direntry.file_name();
|
let file_name = direntry.file_name();
|
||||||
let file_name_str = match file_name.to_str() {
|
let file_name_str = match file_name.to_str() {
|
||||||
|
|
|
||||||
133
src/todo/mod.rs
133
src/todo/mod.rs
|
|
@ -1,130 +1,5 @@
|
||||||
pub mod file;
|
mod file;
|
||||||
|
mod tasks;
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
pub use file::File;
|
||||||
|
pub use tasks::{Status, Task, TaskGroup};
|
||||||
use comrak::nodes::AstNode;
|
|
||||||
use comrak::nodes::NodeValue;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct TaskGroup {
|
|
||||||
pub name: String,
|
|
||||||
pub tasks: Vec<Task>,
|
|
||||||
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<Self, Self::Error> {
|
|
||||||
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<Self, Self::Error> {
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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<Task>,
|
||||||
|
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<Self, Self::Error> {
|
||||||
|
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<Self, Self::Error> {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue