use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
pub struct History {
lines: Vec<String>,
size: usize,
pos: usize,
cmds: CmdToIdx,
path: String, nbr_of_empty_lines: u8,
}
type CmdToIdx = HashMap<String, usize>;
impl History {
pub fn new(path: String) -> Self {
let cmds: CmdToIdx = CmdToIdx::new();
let lines: Vec<String> = Vec::new();
let size = 0;
let pos = 0;
let nbr_of_empty_lines = 0;
let mut h = Self {
lines,
size,
cmds,
pos,
nbr_of_empty_lines,
path,
};
h.load();
return h;
}
pub fn get_previous(&mut self) -> String {
if self.pos < self.size {
let res = self.lines[self.size - self.pos - 1].to_owned();
self.pos += 1;
if res == "" {
self.get_previous()
} else {
res
}
} else {
"".to_owned()
}
}
pub fn get_next(&mut self) -> String {
if self.pos > 0 {
self.pos -= 1;
let res = self.lines[self.size - self.pos - 1].to_owned();
if res == "" {
self.get_next()
} else {
res
}
} else {
"".to_owned()
}
}
pub fn add(&mut self, line: String) {
if let Some(idx) = self.cmds.get(&line) {
self.lines[*idx] = "".to_owned();
self.nbr_of_empty_lines += 1;
if self.nbr_of_empty_lines == MAX_EMPTY_LINES {
self.remove_empty_lines();
self.nbr_of_empty_lines = 0;
}
}
self.cmds.insert(line.to_owned(), self.size);
self.lines.push(line);
self.size += 1;
self.pos = 0;
}
pub fn save(&self) -> std::io::Result<()> {
use std::io::Write;
let mut output = File::create(&self.path)?;
for line in self.lines.iter() {
if line != "" {
write!(output, "{}\n", line)?;
}
}
Ok(())
}
fn remove_empty_lines(&mut self) {
self.size -= MAX_EMPTY_LINES as usize;
let mut new_lines: Vec<String> = Vec::with_capacity(self.size);
let mut i = 0;
while i < self.size {
for s in &self.lines {
if s != "" {
new_lines.push(s.to_owned());
self.cmds.insert(s.to_owned(), i);
i += 1;
}
}
}
self.lines = new_lines;
}
fn load(&mut self) {
let mut idx = 0;
if let Ok(lines) = read_lines(&self.path) {
for line in lines {
if let Ok(ip) = line {
if ip != "" {
self.cmds.insert(ip.to_owned(), idx);
self.lines.push(ip);
idx += 1;
}
}
}
self.size = idx;
}
}
fn _show_lines(&self) {
for i in 0..self.size {
eprintln!("{}->{}", i, &self.lines[i]);
}
eprintln!("-----------------");
}
}
const MAX_EMPTY_LINES: u8 = 25;
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}