diary/ops/
init.rs

1//! # Init operations
2//!
3//! The init module contains functionality relating to the init command,
4//! independent of the CLI.
5
6use std::{
7    fs::create_dir_all,
8    path::{Path, PathBuf},
9};
10
11use git2::Repository;
12
13use crate::errors::DiaryError;
14
15/// The options available to the init command.
16pub struct InitOptions {
17    /// The path to initialise the diary folder.
18    pub path: PathBuf,
19    /// The prefix to use for the diary entries.
20    pub prefix: Option<String>,
21    /// Whether or not to init a git repo.
22    pub git_repo: bool,
23}
24
25enum InitStatus {
26    UseConfig(PathBuf),
27    UseOpt(PathBuf),
28}
29
30/// Establishes where the diary folder should be initialised.
31///
32/// # Arguments
33///
34/// * `opts` - The options passed by the user at runtime.
35/// * `potential_path` - The potential location of the diary folder, as defined in the config.
36///
37/// # Returns
38///
39/// Either the initialisation status, which provides the path to use, or a DiaryError
40/// if the diary is already initialised somewhere.
41fn establish_path(opts: &InitOptions, potential_path: &Path) -> Result<InitStatus, DiaryError> {
42    if potential_path == PathBuf::from("") {
43        let diary_path = opts.path.join("diary");
44        if diary_path.exists() {
45            return Err(DiaryError::ExistsHere);
46        }
47        Ok(InitStatus::UseOpt(diary_path))
48    } else if potential_path.exists() {
49        Err(DiaryError::ExistsElsewhere)
50    } else {
51        Ok(InitStatus::UseConfig(potential_path.to_path_buf()))
52    }
53}
54
55/// Initialises the diary folder, if possible.
56///
57/// # Arguments
58///
59/// * `opts` - The options passed by the user at runtime.
60/// * `config` - The contents of the config file.
61///
62/// # Returns
63///
64/// Either the Path of the new diary folder or a DiaryError if there was an
65/// issue with initialisation.
66pub fn init(opts: &InitOptions, potential_path: &Path) -> Result<PathBuf, DiaryError> {
67    let init_status = establish_path(opts, potential_path);
68    let path = match init_status? {
69        InitStatus::UseConfig(path) => {
70            print!("It appears the config file already has a diary path set. ");
71            println!("Creating a diary folder here: {:?}", path);
72            path
73        }
74        InitStatus::UseOpt(path) => {
75            println!("Creating a diary folder.");
76            path
77        }
78    };
79    let path = match create_dir_all(&path) {
80        Ok(_) => path,
81        Err(e) => return Err(DiaryError::from(e)), // uncovered.
82    };
83
84    if opts.git_repo {
85        match Repository::init(&path) {
86            Ok(_) => (),
87            Err(e) => return Err(DiaryError::from(e)), // uncovered.
88        };
89    };
90    Ok(path)
91}
92
93#[cfg(test)]
94mod tests {
95    use std::fs::create_dir_all;
96
97    use super::{init, InitOptions};
98    use crate::{config::Config, ops::testing};
99
100    #[test]
101    fn blank_config_valid_path() {
102        let dir = testing::temp_path();
103        let diary_dir = dir.join("diary");
104        let opts = InitOptions {
105            path: dir,
106            prefix: None,
107            git_repo: false,
108        };
109        let config = Config::default();
110
111        init(&opts, config.diary_path()).unwrap();
112
113        assert!(diary_dir.exists());
114    }
115
116    #[test]
117    fn blank_config_invalid_path() {
118        let dir = testing::temp_path();
119        let diary_dir = dir.join("diary");
120        let opts = InitOptions {
121            path: dir,
122            prefix: None,
123            git_repo: false,
124        };
125        let config = Config::default();
126        create_dir_all(diary_dir).unwrap();
127
128        init(&opts, config.diary_path()).expect_err("No error produced.");
129    }
130
131    #[test]
132    fn filled_config_non_existing_path() {
133        let dir = testing::temp_path();
134        let diary_dir = dir.join("diary");
135
136        let config = Config::builder().diary_path(diary_dir.clone()).build();
137
138        let opts = InitOptions {
139            path: testing::temp_path(),
140            prefix: None,
141            git_repo: false,
142        };
143
144        init(&opts, config.diary_path()).unwrap();
145
146        assert!(diary_dir.exists());
147    }
148
149    #[test]
150    fn filled_config_existing_path() {
151        let dir = testing::temp_path();
152        let diary_dir = dir.join("diary");
153        let config = Config::builder().diary_path(diary_dir.clone()).build();
154
155        let opts = InitOptions {
156            path: testing::temp_path(),
157            prefix: None,
158            git_repo: false,
159        };
160
161        create_dir_all(diary_dir).unwrap();
162
163        init(&opts, config.diary_path()).expect_err("No error produced.");
164    }
165
166    #[test]
167    fn blank_config_valid_path_git_repo() {
168        let dir = testing::temp_path();
169        let mut diary_dir = dir.join("diary");
170        let opts = InitOptions {
171            path: dir,
172            prefix: None,
173            git_repo: true,
174        };
175        let config = Config::default();
176
177        init(&opts, config.diary_path()).unwrap();
178
179        diary_dir.push(".git");
180
181        assert!(diary_dir.exists());
182    }
183
184    #[test]
185    fn filled_config_non_existing_path_git_repo() {
186        let dir = testing::temp_path();
187        let mut diary_dir = dir.join("diary");
188        let config = Config::builder().diary_path(diary_dir.clone()).build();
189
190        let opts = InitOptions {
191            path: testing::temp_path(),
192            prefix: None,
193            git_repo: true,
194        };
195
196        init(&opts, config.diary_path()).unwrap();
197
198        diary_dir.push(".git");
199
200        assert!(diary_dir.exists());
201    }
202
203    #[test]
204    fn filled_config_existing_path_git_repo() {
205        let dir = testing::temp_path();
206        let diary_dir = dir.join("diary");
207        let config = Config::builder().diary_path(diary_dir.clone()).build();
208
209        let opts = InitOptions {
210            path: testing::temp_path(),
211            prefix: None,
212            git_repo: true,
213        };
214
215        create_dir_all(diary_dir).unwrap();
216
217        init(&opts, config.diary_path()).expect_err("No error produced.");
218    }
219}