use std::collections::VecDeque;
pub struct History {
entries: VecDeque<String>,
max_size: usize,
current_position: Option<usize>,
}
impl Default for History {
fn default() -> Self {
Self {
entries: Default::default(),
max_size: 1000,
current_position: Default::default(),
}
}
}
impl History {
pub fn add_entry(&mut self, line: String) {
self.current_position = None;
if self.entries.back() == Some(&line) || line.is_empty() {
return;
}
self.entries.push_back(line);
if self.entries.len() > self.max_size {
self.entries.pop_front();
}
}
pub fn set_max_size(&mut self, max_size: usize) {
self.max_size = max_size;
while self.entries.len() > max_size {
self.entries.pop_front();
}
self.reset_position();
}
pub fn get_entries(&self) -> &VecDeque<String> {
&self.entries
}
pub fn set_entries(&mut self, entries: impl IntoIterator<Item = String>) {
self.entries.clear();
for entry in entries.into_iter() {
self.add_entry(entry);
}
self.reset_position();
}
pub fn reset_position(&mut self) {
self.current_position = None;
}
pub fn search_next(&mut self, _current: &str) -> Option<&str> {
if let Some(index) = &mut self.current_position {
if *index > 0 {
*index -= 1;
}
Some(&self.entries[*index])
} else if let Some(last) = self.entries.back() {
self.current_position = Some(self.entries.len() - 1);
Some(last)
} else {
None
}
}
pub fn search_previous(&mut self, _current: &str) -> Option<&str> {
if let Some(index) = &mut self.current_position {
if *index == self.entries.len() - 1 {
self.current_position = None;
return Some("");
}
*index += 1;
Some(&self.entries[*index])
} else {
None
}
}
}
#[cfg(test)]
#[test]
fn test_history() {
let mut history = History::default();
history.add_entry("foo".into());
history.add_entry("bar".into());
history.add_entry("baz".into());
for _ in 0..2 {
assert_eq!(None, history.search_previous(""));
assert_eq!(Some("baz"), history.search_next(""));
assert_eq!(Some("bar"), history.search_next(""));
assert_eq!(Some("foo"), history.search_next(""));
assert_eq!(Some("foo"), history.search_next(""));
assert_eq!(Some("bar"), history.search_previous(""));
assert_eq!(Some("baz"), history.search_previous(""));
assert_eq!(Some("bar"), history.search_next(""));
assert_eq!(Some("baz"), history.search_previous(""));
assert_eq!(Some(""), history.search_previous(""));
assert_eq!(None, history.search_previous(""));
assert_eq!(Some("baz"), history.search_next(""));
assert_eq!(Some("bar"), history.search_next(""));
history.reset_position();
}
}
#[cfg(test)]
#[test]
fn test_history_limit() {
let mut history = History {
max_size: 3,
..Default::default()
};
history.add_entry("foo".into());
history.add_entry("bar".into());
history.add_entry("baz".into());
history.add_entry("qux".into());
assert_eq!(Some("qux"), history.search_next(""));
assert_eq!(Some("baz"), history.search_next(""));
assert_eq!(Some("bar"), history.search_next(""));
assert_eq!(Some("bar"), history.search_next(""));
history.set_max_size(2);
assert_eq!(Some("qux"), history.search_next(""));
assert_eq!(Some("baz"), history.search_next(""));
assert_eq!(Some("baz"), history.search_next(""));
}
#[cfg(test)]
#[test]
fn test_history_reset_on_add() {
let mut history = History::default();
history.add_entry("foo".into());
history.add_entry("bar".into());
history.add_entry("baz".into());
assert_eq!(None, history.search_previous(""));
assert_eq!(Some("baz"), history.search_next(""));
assert_eq!(Some("bar"), history.search_next(""));
history.add_entry("qux".into());
assert_eq!(None, history.search_previous(""));
assert_eq!(Some("qux"), history.search_next(""));
assert_eq!(Some("baz"), history.search_next(""));
assert_eq!(Some("bar"), history.search_next(""));
assert_eq!(Some("foo"), history.search_next(""));
}
#[cfg(test)]
#[test]
fn test_history_export() {
let mut history = History {
max_size: 3,
..Default::default()
};
assert_eq!(history.get_entries(), &VecDeque::new());
history.add_entry("foo".into());
history.add_entry("bar".into());
history.add_entry("baz".into());
assert_eq!(history.get_entries(), &["foo", "bar", "baz"]);
history.add_entry("qux".into());
assert_eq!(history.get_entries(), &["bar", "baz", "qux"]);
history.set_entries(["a".to_string(), "b".to_string(), "b".to_string()]);
assert_eq!(Some("b"), history.search_next(""));
assert_eq!(Some("a"), history.search_next(""));
assert_eq!(Some("a"), history.search_next(""));
}