windsh_core/
history.rs

1use crate::dirs::WindshDirs;
2use chrono::prelude::*;
3use serde::{Deserialize, Serialize};
4use std::fs;
5use std::path::PathBuf;
6
7#[derive(Debug, Deserialize, Serialize)]
8pub struct HistoryItem {
9    datetime: Option<String>,
10    cmd: Option<String>,
11}
12
13#[derive(Debug, Clone)]
14pub struct History {
15    datetime: DateTime<Utc>,
16    cmd: String,
17}
18
19impl History {
20    pub fn new() -> Self {
21        Self {
22            datetime: Utc::now(),
23            cmd: String::new(),
24        }
25    }
26
27    pub fn from(cmd: &str) -> Self {
28        Self {
29            datetime: Utc::now(),
30            cmd: String::from(cmd),
31        }
32    }
33
34    pub fn save(&self, history_file: Option<&str>) {
35        let mut history_file_path = PathBuf::new();
36
37        #[cfg(unix)]
38        if history_file == None {
39            history_file_path.push(
40                format!(
41                    "{}/history.yml",
42                    WindshDirs::load()
43                        .config_dir()
44                        .to_str()
45                        .expect("Cannot convert config_dir path to str")
46                )
47                .as_str(),
48            );
49        } else {
50            history_file_path
51                .push(history_file.expect("Cannot get the history file"));
52        }
53
54        #[cfg(windows)]
55        if history_file == None {
56            history_file_path.push(
57                format!(
58                    "{}/history.yml",
59                    WindshDirs::load()
60                        .config_dir()
61                        .to_str()
62                        .expect("Cannot convert config_dir path to str")
63                )
64                .as_str(),
65            );
66        } else {
67            history_file_path
68                .push(history_file.expect("Cannot get the history file"));
69        }
70
71        if !history_file_path.exists() {
72            fs::File::create(history_file_path.to_str().unwrap()).unwrap();
73            let default_content: Vec<HistoryItem> = vec![HistoryItem {
74                datetime: Some(String::from("DEFAULT")),
75                cmd: Some(String::from("DEFAULT")),
76            }];
77            fs::write(
78                &history_file_path,
79                serde_yaml::to_string(&default_content).unwrap(),
80            )
81            .unwrap();
82        }
83
84        let content_raw =
85            fs::read_to_string(&history_file_path.to_str().unwrap()).unwrap();
86
87        let content_yaml: Vec<HistoryItem> =
88            serde_yaml::from_str(content_raw.as_str()).unwrap();
89
90        let mut history_vec: Vec<HistoryItem> = Vec::new();
91
92        for item in content_yaml {
93            history_vec.push(HistoryItem {
94                datetime: item.datetime,
95                cmd: item.cmd,
96            })
97        }
98
99        history_vec.push(HistoryItem {
100            datetime: Some(self.datetime.to_string()),
101            cmd: Some(self.cmd.to_string()),
102        });
103
104        let all_history = serde_yaml::to_string(&history_vec).unwrap();
105
106        fs::write(history_file_path, all_history).unwrap();
107    }
108
109    /// Set the datetime of history
110    /// ```rust
111    /// use windsh_core::history::History;
112    /// use chrono::prelude::*;
113    ///
114    /// let mut my_history = History::new();
115    ///
116    /// my_history.set_datetime(Utc::now());
117    /// ```
118    pub fn set_datetime(&mut self, datetime: DateTime<Utc>) {
119        self.datetime = datetime;
120    }
121
122    /// Set the cmd of history
123    /// ```rust
124    /// use windsh_core::history::History;
125    /// use chrono::prelude::*;
126    ///
127    /// let mut my_history = History::new();
128    ///
129    /// my_history.set_cmd("exec docker run -ti ubuntu:20.04 bash");
130    /// ```
131    pub fn set_cmd(&mut self, cmd: &str) {
132        self.cmd = String::from(cmd);
133    }
134
135    pub fn get_datetime(&self) -> DateTime<Utc> {
136        self.datetime.clone()
137    }
138
139    pub fn get_cmd(&self) -> String {
140        self.cmd.clone()
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147    use std::thread;
148    use std::time;
149
150    #[test]
151    fn test_history_new() {
152        let my_history = History::new();
153
154        assert_eq!(my_history.cmd, "");
155
156        thread::sleep(time::Duration::from_millis(5000));
157        assert_ne!(my_history.datetime, Utc::now());
158    }
159
160    #[test]
161    fn test_history_from() {
162        let my_history = History::from("ls -lsa");
163
164        assert_eq!(my_history.cmd, "ls -lsa");
165        assert_ne!(my_history.cmd, "ls -lsa ");
166
167        thread::sleep(time::Duration::from_millis(5000));
168        assert_ne!(my_history.datetime, Utc::now());
169    }
170
171    #[test]
172    fn test_set_datetime() {
173        let mut my_history = History::from("print Hello, World!");
174        let datetime = Utc::now();
175
176        my_history.set_datetime(datetime);
177
178        assert_eq!(my_history.datetime, datetime);
179    }
180
181    #[test]
182    fn test_set_cmd() {
183        let mut my_history = History::from("print Hello, World!");
184
185        my_history.set_cmd("print This is awesome");
186        assert_eq!(my_history.cmd, "print This is awesome");
187
188        my_history.set_cmd("ls -lsa \n");
189        assert_eq!(my_history.cmd, "ls -lsa \n");
190    }
191
192    #[test]
193    fn test_get_datetime() {
194        let mut my_history = History::from("ls -lsa");
195
196        let datetime = Utc::now();
197        my_history.datetime = datetime;
198
199        assert_eq!(my_history.datetime, datetime);
200
201        thread::sleep(time::Duration::from_millis(5000));
202        assert_ne!(my_history.datetime, Utc::now());
203    }
204
205    #[test]
206    fn test_get_cmd() {
207        let mut my_history = History::from("ls -lsa");
208
209        assert_eq!(my_history.cmd, "ls -lsa");
210
211        my_history.cmd = String::from("");
212
213        assert_eq!(my_history.cmd, "");
214        assert_ne!(my_history.cmd, "\n");
215    }
216}