use std::{
collections::VecDeque,
fs::File,
io::{BufRead, BufReader, Write},
path::Path,
};
use crate::cursor::Cursor;
#[derive(Clone)]
pub struct History {
cursor: Cursor<VecDeque<String>>,
pub limit_size: Option<usize>,
}
impl Default for History {
fn default() -> Self {
Self {
cursor: Cursor::new(VecDeque::from([String::new()]), 0, false),
limit_size: None,
}
}
}
impl History {
pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()> {
let mut file = File::create(path)?;
let mut contents = self.cursor.contents().clone();
contents.pop_back();
let items_to_save: Vec<_> = if let Some(limit) = self.limit_size {
contents.iter().rev().take(limit).collect()
} else {
contents.iter().rev().collect()
};
for item in items_to_save {
writeln!(file, "{}", item)?;
}
Ok(())
}
pub fn load_from_file<P: AsRef<Path>>(
path: P,
limit_size: Option<usize>,
) -> anyhow::Result<Self> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let mut ret = Self {
limit_size,
..Default::default()
};
for line in reader.lines() {
let line = line?;
if !line.is_empty() {
ret.cursor.contents_mut().push_back(line);
}
}
ret.cursor.contents_mut().push_back(String::new());
ret.move_to_tail(); Ok(ret)
}
pub fn insert<T: AsRef<str>>(&mut self, item: T) {
let item = item.as_ref().to_string();
if !self.exists(&item) {
let init_state = self.cursor.contents_mut().pop_back().unwrap();
self.cursor.contents_mut().push_back(item);
if let Some(limit) = self.limit_size
&& limit < self.cursor.contents_mut().len()
{
self.cursor.contents_mut().pop_front();
}
self.cursor.contents_mut().push_back(init_state);
}
self.move_to_tail();
}
pub fn get(&self) -> String {
self.cursor
.contents()
.get(self.cursor.position())
.unwrap_or(&String::new())
.to_string()
}
fn exists<T: AsRef<str>>(&self, item: T) -> bool {
self.cursor.contents().iter().any(|i| i == item.as_ref())
}
pub fn backward(&mut self) -> bool {
self.cursor.backward()
}
pub fn forward(&mut self) -> bool {
self.cursor.forward()
}
pub fn move_to_tail(&mut self) {
self.cursor.move_to_tail()
}
}
#[cfg(test)]
mod test {
mod insert {
use super::super::*;
#[test]
fn test() {
let mut h = History::default();
h.insert("item");
assert_eq!(
VecDeque::from([String::from("item"), String::new()]),
*h.cursor.contents()
);
}
#[test]
fn test_with_multiple_items() {
let mut h = History::default();
h.insert("item1");
h.insert("item2");
assert_eq!(
VecDeque::from([String::from("item1"), String::from("item2"), String::new()]),
*h.cursor.contents()
);
}
#[test]
fn test_with_limit_size() {
let mut h = History {
limit_size: Some(2),
..Default::default()
};
h.insert("item1");
h.insert("item2");
h.insert("item3");
assert_eq!(
VecDeque::from([String::from("item2"), String::from("item3"), String::new()]),
*h.cursor.contents()
);
}
}
mod exists {
use super::super::*;
#[test]
fn test() {
let mut h = History::default();
h.insert("existed");
assert!(h.exists("existed"));
assert!(!h.exists("not_found"));
}
}
}