Compare commits
3 Commits
v0.1.2
...
fc7e45cd3f
| Author | SHA1 | Date | |
|---|---|---|---|
| fc7e45cd3f | |||
| 9fe6ae5eb8 | |||
| ddd620a021 |
@@ -17,6 +17,9 @@ pub struct Args {
|
|||||||
/// list closest files to date
|
/// list closest files to date
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub list: bool,
|
pub list: bool,
|
||||||
|
/// number of files to list
|
||||||
|
#[arg(short, long, default_value_t = 5)]
|
||||||
|
pub number: usize,
|
||||||
/// list closest files to date
|
/// list closest files to date
|
||||||
#[arg(short = 'L', long)]
|
#[arg(short = 'L', long)]
|
||||||
pub list_all: bool,
|
pub list_all: bool,
|
||||||
|
|||||||
@@ -111,3 +111,101 @@ pub fn extract_secitons<'a>(
|
|||||||
}
|
}
|
||||||
groups
|
groups
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::todo::Task;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extract_sections() {
|
||||||
|
let test_md = "\
|
||||||
|
# Test
|
||||||
|
## Content
|
||||||
|
- [ ] something
|
||||||
|
- [x] done
|
||||||
|
- [!] other
|
||||||
|
## Unused
|
||||||
|
### Sub section
|
||||||
|
- [ ] task
|
||||||
|
## Unrealated Stuff
|
||||||
|
- [ ] something else
|
||||||
|
+ [ ] subtask";
|
||||||
|
|
||||||
|
let arena = Arena::new();
|
||||||
|
let root = parse_todo_file(&test_md.to_string(), &arena);
|
||||||
|
|
||||||
|
let result = extract_secitons(root, &vec![]);
|
||||||
|
assert_eq!(result.keys().count(), 0);
|
||||||
|
|
||||||
|
let result = extract_secitons(root, &vec!["Not There".to_string()]);
|
||||||
|
assert_eq!(result.keys().count(), 0);
|
||||||
|
|
||||||
|
let sections = vec!["Unused".to_string()];
|
||||||
|
let result = extract_secitons(root, §ions);
|
||||||
|
assert_eq!(result.keys().count(), 0);
|
||||||
|
|
||||||
|
let sections = vec!["Sub section".to_string()];
|
||||||
|
let result = extract_secitons(root, §ions);
|
||||||
|
println!("{:#?}", root);
|
||||||
|
assert_eq!(result.keys().count(), 1);
|
||||||
|
assert!(result.get(sections.first().unwrap()).is_some());
|
||||||
|
assert_eq!(result.get(sections.first().unwrap()).unwrap().level, 3);
|
||||||
|
|
||||||
|
let sections = vec!["Content".to_string()];
|
||||||
|
let result = extract_secitons(root, §ions);
|
||||||
|
println!("{:#?}", root);
|
||||||
|
assert_eq!(result.keys().count(), 1);
|
||||||
|
assert!(result.get(sections.first().unwrap()).is_some());
|
||||||
|
assert_eq!(
|
||||||
|
result
|
||||||
|
.get(sections.first().unwrap())
|
||||||
|
.expect("No Value for \"Content\""),
|
||||||
|
&TaskGroup {
|
||||||
|
name: sections.first().unwrap().clone(),
|
||||||
|
tasks: vec![
|
||||||
|
Task {
|
||||||
|
status: TaskStatus::Empty,
|
||||||
|
text: "something".to_string(),
|
||||||
|
subtasks: None
|
||||||
|
},
|
||||||
|
Task {
|
||||||
|
status: TaskStatus::Todo('!'),
|
||||||
|
text: "other".to_string(),
|
||||||
|
subtasks: None
|
||||||
|
},
|
||||||
|
],
|
||||||
|
level: 2
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let sections = vec!["Unrealated Stuff".to_string()];
|
||||||
|
let result = extract_secitons(root, §ions);
|
||||||
|
assert_eq!(result.keys().count(), 1);
|
||||||
|
assert!(result.get(sections.first().unwrap()).is_some());
|
||||||
|
assert_eq!(
|
||||||
|
result
|
||||||
|
.get(sections.first().unwrap())
|
||||||
|
.expect("No Value for \"Content\""),
|
||||||
|
&TaskGroup {
|
||||||
|
name: sections.first().unwrap().clone(),
|
||||||
|
tasks: vec![Task {
|
||||||
|
status: TaskStatus::Empty,
|
||||||
|
text: "something else".to_string(),
|
||||||
|
subtasks: Some(vec![Task {
|
||||||
|
status: TaskStatus::Empty,
|
||||||
|
text: "subtask".to_string(),
|
||||||
|
subtasks: None
|
||||||
|
}]),
|
||||||
|
}],
|
||||||
|
level: 2
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = extract_secitons(
|
||||||
|
root,
|
||||||
|
&vec!["Content".to_string(), "Sub section".to_string()],
|
||||||
|
);
|
||||||
|
assert_eq!(result.keys().count(), 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -79,7 +79,11 @@ fn main() {
|
|||||||
|
|
||||||
let today = Local::now().date_naive();
|
let today = Local::now().date_naive();
|
||||||
let target = today - TimeDelta::try_days(args.previous.into()).unwrap();
|
let target = today - TimeDelta::try_days(args.previous.into()).unwrap();
|
||||||
let closest_files = TodoFile::get_closest_files(files.collect(), target, 5);
|
if args.list_all {
|
||||||
|
files.for_each(|f| println!("{}", f.canonicalize().unwrap().to_string_lossy()));
|
||||||
|
return ();
|
||||||
|
}
|
||||||
|
let closest_files = TodoFile::get_closest_files(files.collect(), target, args.number);
|
||||||
if args.list {
|
if args.list {
|
||||||
closest_files
|
closest_files
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
use chrono::naive::NaiveDate;
|
use chrono::naive::NaiveDate;
|
||||||
use regex::Regex;
|
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fs::DirEntry;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use crate::file::FileNameParseError;
|
use crate::file::FileNameParseError;
|
||||||
|
|
||||||
@@ -14,56 +11,6 @@ pub struct File {
|
|||||||
pub date: NaiveDate,
|
pub date: NaiveDate,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum FileError {
|
|
||||||
//IOError(&'static str),
|
|
||||||
ParseError(&'static str),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl File {
|
|
||||||
fn capture_as_number<T: FromStr>(capture: ®ex::Captures, name: &str) -> Result<T, String> {
|
|
||||||
Ok(capture
|
|
||||||
.name(name)
|
|
||||||
.unwrap()
|
|
||||||
.as_str()
|
|
||||||
.parse::<T>()
|
|
||||||
.ok()
|
|
||||||
.ok_or("Something went wrong".to_owned())?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_file_regex() -> Regex {
|
|
||||||
//TODO This would ideally be configurable
|
|
||||||
Regex::new(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2}).md")
|
|
||||||
.expect("could not create regex")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<DirEntry> for File {
|
|
||||||
type Error = FileError;
|
|
||||||
|
|
||||||
fn try_from(direntry: DirEntry) -> Result<Self, Self::Error> {
|
|
||||||
let re = File::get_file_regex();
|
|
||||||
// println!("{:?}", re);
|
|
||||||
let file_name = direntry.file_name();
|
|
||||||
let file_name_str = match file_name.to_str() {
|
|
||||||
Some(name) => name,
|
|
||||||
_ => "",
|
|
||||||
};
|
|
||||||
// println!("{:?}", file_name_str);
|
|
||||||
|
|
||||||
if let Some(caps) = re.captures(file_name_str) {
|
|
||||||
let year: i32 = Self::capture_as_number(&caps, "year").unwrap();
|
|
||||||
let month: u32 = Self::capture_as_number(&caps, "month").unwrap();
|
|
||||||
let day: u32 = Self::capture_as_number(&caps, "day").unwrap();
|
|
||||||
|
|
||||||
return Ok(Self {
|
|
||||||
file: direntry.path(),
|
|
||||||
date: NaiveDate::from_ymd_opt(year, month, day).unwrap(),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
Err(FileError::ParseError("Could not parse file name"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_get_date(file: &PathBuf) -> Result<NaiveDate, FileNameParseError> {
|
fn try_get_date(file: &PathBuf) -> Result<NaiveDate, FileNameParseError> {
|
||||||
let file_name = file
|
let file_name = file
|
||||||
.file_name()
|
.file_name()
|
||||||
|
|||||||
@@ -2,5 +2,4 @@ mod file;
|
|||||||
mod tasks;
|
mod tasks;
|
||||||
|
|
||||||
pub use file::File;
|
pub use file::File;
|
||||||
pub use tasks::{Status, TaskGroup};
|
pub use tasks::{Status, Task, TaskGroup};
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::borrow::Borrow;
|
|||||||
use comrak::nodes::AstNode;
|
use comrak::nodes::AstNode;
|
||||||
use comrak::nodes::NodeValue;
|
use comrak::nodes::NodeValue;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct TaskGroup {
|
pub struct TaskGroup {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub tasks: Vec<Task>,
|
pub tasks: Vec<Task>,
|
||||||
@@ -11,7 +11,7 @@ pub struct TaskGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This does not support subtasks, need to figure out best path forward
|
// This does not support subtasks, need to figure out best path forward
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
pub status: Status,
|
pub status: Status,
|
||||||
pub text: String,
|
pub text: String,
|
||||||
@@ -92,6 +92,7 @@ impl ToString for Task {
|
|||||||
|
|
||||||
impl<'a> TryFrom<&'a AstNode<'a>> for Task {
|
impl<'a> TryFrom<&'a AstNode<'a>> for Task {
|
||||||
type Error = TaskError;
|
type Error = TaskError;
|
||||||
|
|
||||||
fn try_from(node: &'a AstNode<'a>) -> Result<Self, Self::Error> {
|
fn try_from(node: &'a AstNode<'a>) -> Result<Self, Self::Error> {
|
||||||
let data_ref = &node.data.borrow();
|
let data_ref = &node.data.borrow();
|
||||||
if let NodeValue::TaskItem(ch) = data_ref.value {
|
if let NodeValue::TaskItem(ch) = data_ref.value {
|
||||||
@@ -158,6 +159,7 @@ impl ToString for TaskGroup {
|
|||||||
|
|
||||||
impl<'a> TryFrom<&'a AstNode<'a>> for TaskGroup {
|
impl<'a> TryFrom<&'a AstNode<'a>> for TaskGroup {
|
||||||
type Error = TaskError;
|
type Error = TaskError;
|
||||||
|
|
||||||
fn try_from(node: &'a AstNode<'a>) -> Result<Self, Self::Error> {
|
fn try_from(node: &'a AstNode<'a>) -> Result<Self, Self::Error> {
|
||||||
let node_ref = &node.data.borrow();
|
let node_ref = &node.data.borrow();
|
||||||
if let NodeValue::Heading(heading) = node_ref.value {
|
if let NodeValue::Heading(heading) = node_ref.value {
|
||||||
|
|||||||
Reference in New Issue
Block a user