diff --git a/src/file/mod.rs b/src/file/mod.rs index 63f43b6..f348abc 100644 --- a/src/file/mod.rs +++ b/src/file/mod.rs @@ -1,21 +1,32 @@ -use chrono::Datelike; -use crate::TaskGroup; +use crate::todo::{File as TodoFile, Status as TaskStatus}; use crate::NaiveDate; -use crate::todo::{Status as TaskStatus,File as TodoFile}; +use crate::TaskGroup; +use chrono::Datelike; use comrak::nodes::{AstNode, NodeValue}; -use comrak::{Arena, ComrakExtensionOptions, ComrakOptions, ComrakParseOptions}; -use std::str; -use std::io::Write; -use std::collections::HashMap; use comrak::parse_document; -use std::path::{Path, PathBuf}; +use comrak::{Arena, ComrakExtensionOptions, ComrakOptions, ComrakParseOptions}; +use std::collections::HashMap; use std::fs::{read, read_dir, File}; +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), + ParseError(chrono::ParseError), +} pub fn get_filepath(data_dir: &PathBuf, date: &NaiveDate) -> PathBuf { let file_name = format!("{}-{:02}-{:02}.md", date.year(), date.month(), date.day()); let mut file_path = data_dir.clone(); file_path.push(file_name); - file_path } @@ -114,3 +125,86 @@ 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); + } +}