1use super::*;
2
3use std::{
4 collections::{vec_deque, VecDeque},
5 fs::File,
6 io::{self, Write},
7 io::{BufRead, BufReader, BufWriter},
8 iter::IntoIterator,
9 ops::Index,
10 ops::IndexMut,
11 path::Path,
12 };
14
15const DEFAULT_MAX_SIZE: usize = 1000;
16
17pub struct History {
19 pub buffers: VecDeque<Buffer>,
22 file_name: Option<String>,
24 max_buffers_size: usize,
27 max_file_size: usize,
30 pub append_duplicate_entries: bool,
32 pub inc_append: bool,
34 pub share: bool,
36 pub file_size: u64,
38 pub load_duplicates: bool,
40 compaction_writes: usize,
42}
43
44impl Default for History {
45 fn default() -> Self {
46 Self::new()
47 }
48}
49
50impl History {
51 pub fn new() -> History {
53 History {
54 buffers: VecDeque::with_capacity(DEFAULT_MAX_SIZE),
55 file_name: None,
56 max_buffers_size: DEFAULT_MAX_SIZE,
57 max_file_size: DEFAULT_MAX_SIZE,
58 append_duplicate_entries: false,
59 inc_append: false,
60 share: false,
61 file_size: 0,
62 load_duplicates: true,
63 compaction_writes: 0,
64 }
65 }
66
67 pub fn clear_history(&mut self) {
69 self.buffers.clear();
70 }
71
72 pub fn load_history(&mut self, append: bool) -> io::Result<u64> {
75 if let Some(path) = self.file_name.clone() {
76 let file_size = self.file_size;
77 self.load_history_file_test(&path, file_size, append)
78 .inspect(|&l| {
79 self.file_size = l;
80 })
81 } else {
82 Err(io::Error::new(
83 io::ErrorKind::Other,
84 "History filename not set!",
85 ))
86 }
87 }
88
89 pub fn load_history_file<P: AsRef<Path>>(&mut self, path: P, append: bool) -> io::Result<u64> {
91 self.load_history_file_test(path, 0, append)
92 }
93
94 fn load_history_file_test<P: AsRef<Path>>(
97 &mut self,
98 path: P,
99 length: u64,
100 append: bool,
101 ) -> io::Result<u64> {
102 let path = path.as_ref();
103 let file = if path.exists() {
104 File::open(path)?
105 } else {
106 let status = format!("File not found {:?}", path);
107 return Err(io::Error::new(io::ErrorKind::Other, status));
108 };
109 let new_length = file.metadata()?.len();
110 if new_length == 0 && length == 0 && !append {
111 self.clear_history();
113 }
114 if new_length != length {
115 if !append {
116 self.clear_history();
117 }
118 let reader = BufReader::new(file);
119 for line in reader.lines() {
120 match line {
121 Ok(line) => {
122 if !line.starts_with('#') {
123 self.buffers.push_back(Buffer::from(line));
124 }
125 }
126 Err(_) => break,
127 }
128 }
129 self.truncate();
130 if !self.load_duplicates {
131 let mut tmp_buffers: Vec<Buffer> = Vec::with_capacity(self.buffers.len());
132 while let Some(buf) = self.buffers.pop_back() {
134 self.remove_duplicates(&buf.to_string()[..]);
135 tmp_buffers.push(buf);
136 }
137 while let Some(buf) = tmp_buffers.pop() {
138 self.buffers.push_back(buf);
139 }
140 }
141 }
142 Ok(new_length)
143 }
144
145 fn deduplicate_history_file<P: AsRef<Path>>(
149 path: P,
150 max_file_size: usize,
151 ) -> io::Result<String> {
152 let path = path.as_ref();
153 let file = if path.exists() {
154 File::open(path)?
155 } else {
156 let status = format!("File not found {:?}", path);
157 return Err(io::Error::new(io::ErrorKind::Other, status));
158 };
159 let mut buf: VecDeque<String> = VecDeque::new();
160 let reader = BufReader::new(file);
161 for line in reader.lines() {
162 match line {
163 Ok(line) => {
164 if !line.starts_with('#') {
165 buf.push_back(line);
166 }
167 }
168 Err(_) => break,
169 }
170 }
171 let org_length = buf.len();
172 if buf.len() >= max_file_size {
173 let pop_out = buf.len() - max_file_size;
174 for _ in 0..pop_out {
175 buf.pop_front();
176 }
177 }
178 let mut tmp_buffers: Vec<String> = Vec::with_capacity(buf.len());
179 while let Some(line) = buf.pop_back() {
181 buf.retain(|buffer| *buffer != line);
182 tmp_buffers.push(line);
183 }
184 while let Some(line) = tmp_buffers.pop() {
185 buf.push_back(line);
186 }
187
188 if org_length != buf.len() {
189 let mut file = BufWriter::new(File::create(path)?);
191 for command in buf.into_iter() {
193 let _ = file.write_all(command.as_bytes());
194 let _ = file.write_all(b"\n");
195 }
196 }
197 Ok("De-duplicated history file.".to_string())
198 }
199
200 pub fn set_file_name_and_load_history<P: AsRef<Path>>(&mut self, path: P) -> io::Result<u64> {
202 let path = path.as_ref();
203 self.file_name = path.to_str().map(|s| s.to_owned());
204 self.file_size = 0;
205 if path.exists() {
206 self.load_history_file(path, false).inspect(|&l| {
207 self.file_size = l;
208 })
209 } else {
210 File::create(path)?;
211 Ok(0)
212 }
213 }
214
215 pub fn set_max_buffers_size(&mut self, size: usize) {
217 self.max_buffers_size = size;
218 }
219
220 pub fn set_max_file_size(&mut self, size: usize) {
222 self.max_file_size = size;
223 }
224
225 #[inline(always)]
227 pub fn len(&self) -> usize {
228 self.buffers.len()
229 }
230
231 pub fn is_empty(&self) -> bool {
233 self.buffers.is_empty()
234 }
235
236 pub fn push(&mut self, new_item: Buffer) -> io::Result<()> {
240 if !self.append_duplicate_entries
243 && self.buffers.back().map(|b| b.to_string()) == Some(new_item.to_string())
244 {
245 return Ok(());
246 }
247
248 let item_str = String::from(new_item.clone());
249 self.buffers.push_back(new_item);
250 while self.buffers.len() > self.max_buffers_size {
252 self.buffers.pop_front();
253 }
254
255 if self.inc_append && self.file_name.is_some() {
256 if !self.load_duplicates {
257 self.compaction_writes += 1;
259 if self.compaction_writes > 29 {
263 if self.share {
264 let _ = self.load_history(false);
266 if let Some(file_name) = self.file_name.clone() {
268 let _ = self.overwrite_history(file_name);
269 }
270 } else {
271 if let Some(file_name) = self.file_name.clone() {
274 let _ =
275 History::deduplicate_history_file(file_name, self.max_file_size);
276 }
277 }
278 self.compaction_writes = 0;
279 }
280 } else {
281 self.compaction_writes = 1;
283 }
284 let file_name = self.file_name.clone().unwrap();
285 if let Ok(inner_file) = std::fs::OpenOptions::new().append(true).open(&file_name) {
286 if self.compaction_writes > 0 {
288 let mut file = BufWriter::new(inner_file);
290 let _ = file.write_all(item_str.as_bytes());
291 let _ = file.write_all(b"\n");
292 self.file_size += item_str.len() as u64 + 1;
294 }
295 }
296 }
297 Ok(())
298 }
299
300 pub fn remove_duplicates(&mut self, input: &str) {
302 self.buffers.retain(|buffer| {
303 let command = buffer.lines().concat();
304 command != input
305 });
306 }
307
308 fn get_match<I>(&self, vals: I, search_term: &Buffer) -> Option<usize>
309 where
310 I: Iterator<Item = usize>,
311 {
312 vals.filter_map(|i| self.buffers.get(i).map(|t| (i, t)))
313 .find(|(_i, tested)| tested.starts_with(search_term))
314 .map(|(i, _)| i)
315 }
316
317 pub fn get_newest_match(
320 &self,
321 curr_position: Option<usize>,
322 new_buff: &Buffer,
323 ) -> Option<usize> {
324 let pos = curr_position.unwrap_or(self.buffers.len());
325 if pos > 0 {
326 self.get_match((0..pos).rev(), new_buff)
327 } else {
328 None
329 }
330 }
331
332 pub fn get_history_subset(&self, search_term: &Buffer) -> Vec<usize> {
333 let mut v: Vec<usize> = Vec::new();
334 let mut ret: Vec<usize> = (0..self.len())
335 .filter(|i| {
336 if let Some(tested) = self.buffers.get(*i) {
337 let starts = tested.starts_with(search_term);
338 let contains = tested.contains(search_term);
339 if starts {
340 v.push(*i);
341 }
342 contains && !starts && tested != search_term
343 } else {
344 false
345 }
346 })
347 .collect();
348 ret.append(&mut v);
349 ret
350 }
351
352 pub fn search_index(&self, search_term: &Buffer) -> Vec<usize> {
353 (0..self.len())
354 .filter_map(|i| self.buffers.get(i).map(|t| (i, t)))
355 .filter(|(_i, tested)| tested.contains(search_term))
356 .map(|(i, _)| i)
357 .collect()
358 }
359
360 #[inline(always)]
362 pub fn file_name(&self) -> Option<&str> {
363 self.file_name.as_deref()
364 }
365
366 fn truncate(&mut self) {
367 if self.buffers.len() >= self.max_file_size {
370 let pop_out = self.buffers.len() - self.max_file_size;
371 for _ in 0..pop_out {
372 self.buffers.pop_front();
373 }
374 }
375 }
376
377 fn overwrite_history<P: AsRef<Path>>(&mut self, path: P) -> io::Result<String> {
378 self.truncate();
379 let mut file = BufWriter::new(File::create(&path)?);
380
381 for command in self.buffers.iter().cloned() {
383 let _ = file.write_all(String::from(command).as_bytes());
384 let _ = file.write_all(b"\n");
385 }
386 Ok("Wrote history to file.".to_string())
387 }
388
389 pub fn commit_to_file_path<P: AsRef<Path>>(&mut self, path: P) -> io::Result<String> {
390 if self.inc_append {
391 Ok("Nothing to commit.".to_string())
392 } else {
393 self.overwrite_history(path)
394 }
395 }
396
397 pub fn commit_to_file(&mut self) {
398 if let Some(file_name) = self.file_name.clone() {
399 let _ = self.commit_to_file_path(file_name);
400 }
401 }
402}
403
404impl<'a> IntoIterator for &'a History {
405 type Item = &'a Buffer;
406 type IntoIter = vec_deque::Iter<'a, Buffer>;
407
408 fn into_iter(self) -> Self::IntoIter {
409 self.buffers.iter()
410 }
411}
412
413impl Index<usize> for History {
414 type Output = Buffer;
415
416 fn index(&self, index: usize) -> &Buffer {
417 &self.buffers[index]
418 }
419}
420
421impl IndexMut<usize> for History {
422 fn index_mut(&mut self, index: usize) -> &mut Buffer {
423 &mut self.buffers[index]
424 }
425}