1use crate::config::Config;
5use crate::error::Error;
6use crate::file::LogFile;
7use crate::hook::{execute_hook, HookType};
8use crate::path::LogPath;
9use crate::task::{Task, TaskStatus};
10use std::fs::OpenOptions;
11use std::io::Write;
12use std::path::Path;
13
14pub fn rollover<W: Write>(
18 w: &mut W,
19 config: &Config,
20 p: &LogPath,
21) -> Result<(LogPath, usize), Error> {
22 let path = p.path();
23 let next = p.next()?;
24 let next_path = next.path();
25
26 execute_hook(w, config, &HookType::BeforeRollover, &[path.as_os_str()])?;
27 let tasks = load_carryover_tasks(path)?;
28 create_new_logfile(&next_path, &tasks)?;
29 execute_hook(
30 w,
31 config,
32 &HookType::AfterRollover,
33 &[path.as_os_str(), next_path.as_os_str()],
34 )?;
35
36 Ok((next, tasks.len()))
37}
38
39fn load_carryover_tasks(path: &Path) -> Result<Vec<Task>, Error> {
40 let prev = LogFile::load(path)?;
41 let mut tasks = Vec::new();
42 prev.tasks().iter().for_each(|t| {
43 if let TaskStatus::ToDo | TaskStatus::Started | TaskStatus::Blocked = t.status() {
44 tasks.push(t.clone());
45 }
46 });
47 Ok(tasks)
48}
49
50fn create_new_logfile(next_path: &Path, tasks: &[Task]) -> Result<(), Error> {
51 let mut f = OpenOptions::new()
52 .write(true)
53 .create_new(true)
54 .open(next_path)?;
55
56 for t in tasks {
57 write!(f, "{}\n", t)?;
58 }
59
60 Ok(())
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use crate::repository::LogRepository;
67 use tempfile::tempdir;
68
69 #[test]
70 fn test_rollover() {
71 let mut out = Vec::new();
72 let dir = tempdir().unwrap();
73 let repo = LogRepository::new(dir.path());
74 let config = Config::new(dir.path(), "");
75
76 repo.init().unwrap();
79 let first_logpath = repo.latest().unwrap().unwrap();
80
81 let (new_logpath, num_imported) = rollover(&mut out, &config, &first_logpath).unwrap();
84 assert_eq!(num_imported, 3);
85
86 let logfile = LogFile::load(new_logpath.path()).unwrap();
88 let task_statuses: Vec<TaskStatus> = logfile.tasks().iter().map(|t| t.status()).collect();
89 assert_eq!(
90 task_statuses,
91 vec![TaskStatus::ToDo, TaskStatus::Started, TaskStatus::Blocked]
92 );
93
94 let mut paths = repo.list().unwrap();
96 paths.sort();
97 assert_eq!(paths, vec![first_logpath, new_logpath]);
98 }
99}