diff --git a/.gitignore b/.gitignore index 4dffa1b..7ac495c 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,4 @@ Cargo.lock # config file .rusty_task.json - +Notes/ diff --git a/src/file/mod.rs b/src/file/mod.rs index f348abc..247bf8e 100644 --- a/src/file/mod.rs +++ b/src/file/mod.rs @@ -11,12 +11,6 @@ use std::io::Write; use std::path::{Path, PathBuf}; use std::str; -#[derive(Debug, Clone, PartialEq)] -pub struct DatedPathBuf { - path: PathBuf, - date: NaiveDate, -} - #[derive(Debug)] pub enum FileNameParseError { TypeConversionError(&'static str), @@ -49,13 +43,13 @@ pub fn write_file(path: &PathBuf, content: &String) { } pub fn load_file(file: &TodoFile) -> String { - let contents_utf8 = read(file.file.path()) - .expect(format!("Could not read file {}", file.file.path().to_string_lossy()).as_str()); + let contents_utf8 = read(file.file.clone()) + .expect(format!("Could not read file {}", file.file.to_string_lossy()).as_str()); str::from_utf8(&contents_utf8) .expect( format!( "failed to convert contents of file to string: {}", - file.file.path().to_string_lossy() + file.file.to_string_lossy() ) .as_str(), ) @@ -125,86 +119,3 @@ pub fn get_latest_file(dir: &Path) -> Result { .reduce(|a, b| TodoFile::latest_file(a, b)) .ok_or("Could not reduce items".to_string()) } - -fn try_get_date(file: &PathBuf) -> Result { - let file_name = file - .file_name() - .ok_or(FileNameParseError::TypeConversionError( - "Could not get filename from path: {:?}", - ))? - .to_str() - .ok_or(FileNameParseError::TypeConversionError( - "Could not get filename from path: {:?}", - ))?; - - NaiveDate::parse_from_str(file_name, "%Y-%m-%d.md") - .or_else(|e| Err(FileNameParseError::ParseError(e))) -} - -impl TryFrom for DatedPathBuf { - type Error = FileNameParseError; - - fn try_from(path: PathBuf) -> Result { - Ok(Self { - date: try_get_date(&path)?, - path, - }) - } -} - -pub fn get_closest_files(files: &Vec, target: NaiveDate, n: usize) -> Vec { - let mut dated_files = files - .clone() - .into_iter() - .filter_map(|file| DatedPathBuf::try_from(file).ok()) - .collect::>(); - dated_files.sort_by_cached_key(|dated_file| (dated_file.date - target).num_days().abs()); - - dated_files[..n].to_vec() -} - -#[cfg(test)] -mod test { - use super::*; - use chrono::NaiveDate; - use std::path::PathBuf; - - #[test] - fn test_get_closest_date() { - let files = vec![ - PathBuf::from("./2024-01-01.md"), - PathBuf::from("./2024-01-02.md"), - PathBuf::from("./2024-01-03.md"), - PathBuf::from("./2024-02-01.md"), - PathBuf::from("./2024-03-01.md"), - PathBuf::from("./2024-04-01.md"), - PathBuf::from("./2024-04-02.md"), - PathBuf::from("./2024-04-03.md"), - PathBuf::from("./2024-04-04.md"), - ]; - - let res = get_closest_files(&files, NaiveDate::from_ymd_opt(2023, 12, 30).unwrap(), 3); - let expected_res = vec![ - DatedPathBuf::try_from(PathBuf::from("./2024-01-01.md")).unwrap(), - DatedPathBuf::try_from(PathBuf::from("./2024-01-02.md")).unwrap(), - DatedPathBuf::try_from(PathBuf::from("./2024-01-03.md")).unwrap(), - ]; - assert_eq!(res, expected_res); - - let res = get_closest_files(&files, NaiveDate::from_ymd_opt(2024, 2, 1).unwrap(), 3); - let expected_res = vec![ - DatedPathBuf::try_from(PathBuf::from("./2024-02-01.md")).unwrap(), - DatedPathBuf::try_from(PathBuf::from("./2024-01-03.md")).unwrap(), - DatedPathBuf::try_from(PathBuf::from("./2024-03-01.md")).unwrap(), - ]; - assert_eq!(res, expected_res); - - let res = get_closest_files(&files, NaiveDate::from_ymd_opt(2024, 5, 2).unwrap(), 3); - let expected_res = vec![ - DatedPathBuf::try_from(PathBuf::from("./2024-04-04.md")).unwrap(), - DatedPathBuf::try_from(PathBuf::from("./2024-04-03.md")).unwrap(), - DatedPathBuf::try_from(PathBuf::from("./2024-04-02.md")).unwrap(), - ]; - assert_eq!(res, expected_res); - } -} diff --git a/src/main.rs b/src/main.rs index 1ea31b9..4240199 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,9 +5,9 @@ mod todo; use crate::cli::Args; use crate::config::Config; -use crate::todo::TaskGroup; +use crate::todo::{File as TodoFile, TaskGroup}; use chrono::naive::NaiveDate; -use chrono::{Datelike, Local}; +use chrono::{Datelike, Local, TimeDelta}; use clap::Parser; use comrak::Arena; use resolve_path::PathResolveExt; @@ -66,7 +66,16 @@ fn main() { }; } - let latest_file = file::get_latest_file(&data_dir); + let files = fs::read_dir(&data_dir) + .expect(format!("Could not find notes folder: {:?}", &data_dir).as_str()) + .filter_map(|f| f.ok()) + .map(|file| file.path()); + let today = Local::now().date_naive(); + let target = today - TimeDelta::try_days(args.previous.into()).unwrap(); + let cloesest_files = TodoFile::get_closest_files(files.collect(), target, 5); + println!("{:?}", cloesest_files); + + let latest_file = cloesest_files.first().ok_or(""); let now = Local::now(); let today = NaiveDate::from_ymd_opt(now.year(), now.month(), now.day()).unwrap(); @@ -109,7 +118,7 @@ fn main() { file::write_file(&file_path, &content); file_path } - Ok(todo_file) => todo_file.file.path(), + Ok(todo_file) => todo_file.file.clone(), }; Command::new(&cfg.editor) diff --git a/src/todo/file.rs b/src/todo/file.rs index 60fcbfd..bb9c0a1 100644 --- a/src/todo/file.rs +++ b/src/todo/file.rs @@ -1,18 +1,22 @@ use chrono::naive::NaiveDate; use regex::Regex; +use std::cmp::min; use std::convert::TryFrom; use std::fs::DirEntry; +use std::path::PathBuf; use std::str::FromStr; -#[derive(Debug)] +use crate::file::FileNameParseError; + +#[derive(Debug, Clone, PartialEq)] pub struct File { - pub file: DirEntry, + pub file: PathBuf, pub date: NaiveDate, } -pub enum FileError{ +pub enum FileError { //IOError(&'static str), - ParseError(&'static str) + ParseError(&'static str), } impl File { @@ -46,13 +50,13 @@ impl TryFrom for File { fn try_from(direntry: DirEntry) -> Result { let re = File::get_file_regex(); -// println!("{:?}", re); + // println!("{:?}", re); let file_name = direntry.file_name(); let file_name_str = match file_name.to_str() { Some(name) => name, _ => "", }; -// println!("{:?}", file_name_str); + // println!("{:?}", file_name_str); if let Some(caps) = re.captures(file_name_str) { let year: i32 = Self::capture_as_number(&caps, "year").unwrap(); @@ -60,10 +64,116 @@ impl TryFrom for File { let day: u32 = Self::capture_as_number(&caps, "day").unwrap(); return Ok(Self { - file: direntry, + 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 { + let file_name = file + .file_name() + .ok_or(FileNameParseError::TypeConversionError( + "Could not get filename from path: {:?}", + ))? + .to_str() + .ok_or(FileNameParseError::TypeConversionError( + "Could not get filename from path: {:?}", + ))?; + + NaiveDate::parse_from_str(file_name, "%Y-%m-%d.md") + .or_else(|e| Err(FileNameParseError::ParseError(e))) +} + +impl TryFrom for File { + type Error = FileNameParseError; + + fn try_from(path: PathBuf) -> Result { + Ok(Self { + date: try_get_date(&path)?, + file: path.into(), + }) + } +} +impl File { + pub fn get_closest_files(files: Vec, target: NaiveDate, n: usize) -> Vec { + let mut dated_files = files + .into_iter() + .filter_map(|file| File::try_from(file).ok()) + .collect::>(); + dated_files.sort_by_cached_key(|dated_file| (dated_file.date - target).num_days().abs()); + + let count = min(n, dated_files.len()); + dated_files[..count].to_vec() + } +} + +#[cfg(test)] +mod test { + use super::*; + use chrono::NaiveDate; + use std::path::PathBuf; + + #[test] + fn test_get_closest_date() { + let files = vec![ + PathBuf::from("./2024-01-01.md"), + PathBuf::from("./2024-01-02.md"), + PathBuf::from("./2024-01-03.md"), + PathBuf::from("./2024-02-01.md"), + PathBuf::from("./2024-03-01.md"), + PathBuf::from("./2024-04-01.md"), + PathBuf::from("./2024-04-02.md"), + PathBuf::from("./2024-04-03.md"), + PathBuf::from("./2024-04-04.md"), + ]; + + let res = File::get_closest_files( + files.clone(), + NaiveDate::from_ymd_opt(2023, 12, 30).unwrap(), + 3, + ); + let expected_res = vec![ + File::try_from(PathBuf::from("./2024-01-01.md")).unwrap(), + File::try_from(PathBuf::from("./2024-01-02.md")).unwrap(), + File::try_from(PathBuf::from("./2024-01-03.md")).unwrap(), + ]; + assert_eq!(res, expected_res); + + let res = File::get_closest_files( + files.clone(), + NaiveDate::from_ymd_opt(2024, 2, 1).unwrap(), + 3, + ); + let expected_res = vec![ + File::try_from(PathBuf::from("./2024-02-01.md")).unwrap(), + File::try_from(PathBuf::from("./2024-01-03.md")).unwrap(), + File::try_from(PathBuf::from("./2024-03-01.md")).unwrap(), + ]; + assert_eq!(res, expected_res); + + let res = File::get_closest_files( + files.clone(), + NaiveDate::from_ymd_opt(2024, 5, 2).unwrap(), + 3, + ); + let expected_res = vec![ + File::try_from(PathBuf::from("./2024-04-04.md")).unwrap(), + File::try_from(PathBuf::from("./2024-04-03.md")).unwrap(), + File::try_from(PathBuf::from("./2024-04-02.md")).unwrap(), + ]; + assert_eq!(res, expected_res); + + let res = File::get_closest_files( + files[..1].to_vec(), + NaiveDate::from_ymd_opt(2023, 12, 30).unwrap(), + 3, + ); + let expected_res = vec![ + File::try_from(PathBuf::from("./2024-01-01.md")).unwrap(), + ]; + assert_eq!(res, expected_res); + } +} diff --git a/src/todo/mod.rs b/src/todo/mod.rs index 7534deb..61ed8f8 100644 --- a/src/todo/mod.rs +++ b/src/todo/mod.rs @@ -3,3 +3,4 @@ mod tasks; pub use file::File; pub use tasks::{Status, TaskGroup}; +