diary/ops/
new.rs

1//! # New operations
2//!
3//! The new module contains functionality relating to the new command,
4//! independent of the CLI.
5use std::fs::OpenOptions;
6
7use chrono::prelude::*;
8
9use crate::{
10    errors::DiaryError,
11    utils::{editing, file_system},
12    Diary, EntryContent,
13};
14
15/// The options available to the new command.
16pub struct NewOptions {
17    /// Whether or not to open the new entry for an initial entry.
18    pub open: bool,
19}
20
21/// Creates a new diary entry.
22///
23/// # Arguments
24///
25/// * `opts` - The options passed by the user at runtime.
26/// * `diary` - Struct representing the diary.
27/// * `date` - The date for which to create the new entry.
28/// * `string_getter` - The function that obtains the string to add to the file.
29///
30/// # Returns
31///
32/// The unit upon successful creation of the entry.
33/// DiaryError if the entry already exists.
34/// DiaryError on any other IO issues.
35pub fn new(
36    opts: &NewOptions,
37    diary: &Diary,
38    date: &DateTime<Local>,
39    string_getter: editing::StringGetter,
40) -> Result<(), DiaryError> {
41    let mut new_entry_path = file_system::month_folder(diary.diary_path(), date);
42    file_system::create_month_folder(&new_entry_path)?;
43
44    let entry_name = diary.file_name(date);
45
46    new_entry_path.push(entry_name);
47    let result = OpenOptions::new()
48        .write(true)
49        .create_new(true)
50        .open(new_entry_path);
51
52    let mut file = match result {
53        Ok(mut file) => {
54            editing::add_user_content_to_file(&mut file, diary.file_type().title(date))?;
55            file
56        }
57        Err(e) => return Err(e.into()),
58    };
59    if opts.open {
60        let contents = string_getter("".to_owned())?;
61        editing::add_user_content_to_file(&mut file, contents)?;
62    };
63    Ok(())
64}
65
66#[cfg(test)]
67mod test {
68    use std::fs;
69
70    use chrono::prelude::*;
71
72    use super::{new, NewOptions};
73    use crate::{config::Config, ops::testing, utils::editing::test::test_string_getter, Diary};
74
75    #[test]
76    fn new_success() {
77        let config = testing::temp_config();
78        testing::default_init(config.diary_path());
79
80        let diary = Diary::from_config(&config).unwrap();
81
82        let new_opts = NewOptions { open: false };
83        let date = Local.with_ymd_and_hms(2021, 11, 6, 0, 0, 0).unwrap();
84
85        new(&new_opts, &diary, &date, test_string_getter).unwrap();
86
87        let test_path = diary.get_entry_path(&date);
88
89        assert!(test_path.exists());
90    }
91
92    #[test]
93    #[should_panic(expected = "value: UnInitialised")]
94    fn new_not_init() {
95        let config = testing::temp_config();
96        let diary = Diary::from_config(&config).unwrap();
97
98        let date = Local.with_ymd_and_hms(2021, 11, 6, 0, 0, 0).unwrap();
99        let new_opts = NewOptions { open: false };
100
101        new(&new_opts, &diary, &date, test_string_getter).unwrap();
102    }
103
104    #[test]
105    #[should_panic(expected = "kind: AlreadyExists")]
106    fn new_fail_second_time() {
107        let config = testing::temp_config();
108        testing::default_init(config.diary_path());
109        let diary = Diary::from_config(&config).unwrap();
110
111        let new_opts = NewOptions { open: false };
112        let date = Local.with_ymd_and_hms(2021, 11, 6, 0, 0, 0).unwrap();
113
114        new(&new_opts, &diary, &date, test_string_getter).unwrap();
115        new(&new_opts, &diary, &date, test_string_getter).unwrap();
116    }
117
118    #[test]
119    #[should_panic(expected = "value: UnInitialised")]
120    fn new_not_init_default_config() {
121        let config = Config::default();
122        let diary = Diary::from_config(&config).unwrap();
123
124        let new_opts = NewOptions { open: false };
125        let date = Local.with_ymd_and_hms(2021, 11, 6, 0, 0, 0).unwrap();
126
127        new(&new_opts, &diary, &date, test_string_getter).unwrap();
128    }
129
130    #[test]
131    fn new_open_file_success() {
132        let config = testing::temp_config();
133        testing::default_init(config.diary_path());
134        let diary = Diary::from_config(&config).unwrap();
135
136        let new_opts = NewOptions { open: true };
137        let date = Local.with_ymd_and_hms(2021, 11, 6, 0, 0, 0).unwrap();
138
139        new(&new_opts, &diary, &date, test_string_getter).unwrap();
140
141        let diary_file = Diary::from_config(&config).unwrap();
142
143        let test_path = diary_file.get_entry_path(&date);
144
145        let content = fs::read_to_string(test_path).unwrap();
146        assert!(content.contains("Test content"));
147    }
148}