use {
serde::{Deserialize, Serialize},
std::collections::BTreeMap,
};
use crate::command::{CommandId, UserCommand};
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct History {
pub history: Vec<CommandId>,
}
impl History {
const MAX_ENTRIES: usize = 100;
pub fn add_entry(&mut self, id: &CommandId) {
self.history.retain(|hid| hid != id);
self.history.push(id.clone());
if self.history.len() > Self::MAX_ENTRIES {
let excess = self.history.len() - Self::MAX_ENTRIES;
self.history.drain(..excess);
}
}
pub fn prune(&mut self, valid_ids: &BTreeMap<CommandId, UserCommand>) {
self.history.retain(|id| valid_ids.contains_key(id));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add_entry_to_empty() {
let mut h = History::default();
h.add_entry(&"a".to_string());
assert_eq!(h.history, vec!["a"]);
}
#[test]
fn add_entry_deduplicates_and_moves_to_end() {
let mut h = History {
history: vec!["a".into(), "b".into(), "c".into()],
};
h.add_entry(&"a".to_string());
assert_eq!(h.history, vec!["b", "c", "a"]);
}
#[test]
fn add_entry_new_goes_to_end() {
let mut h = History {
history: vec!["a".into(), "b".into()],
};
h.add_entry(&"c".to_string());
assert_eq!(h.history, vec!["a", "b", "c"]);
}
fn make_commands(ids: &[&str]) -> BTreeMap<CommandId, UserCommand> {
ids.iter()
.map(|id| {
(
id.to_string(),
UserCommand {
name: id.to_string(),
script: String::new(),
..default_command()
},
)
})
.collect()
}
fn default_command() -> UserCommand {
UserCommand {
id: None,
name: String::new(),
description: None,
script: String::new(),
args: vec![],
tags: vec![],
only_on: None,
shell: None,
only_in_dir: vec![],
risky: false,
after_run: None,
working_dir: None,
source_path: None,
project_dir: None,
domain: None,
}
}
#[test]
fn prune_removes_stale_ids() {
let mut h = History {
history: vec!["a".into(), "b".into(), "c".into()],
};
let commands = make_commands(&["a", "c"]);
h.prune(&commands);
assert_eq!(h.history, vec!["a", "c"]);
}
#[test]
fn prune_empty_history() {
let mut h = History::default();
let commands = make_commands(&["a"]);
h.prune(&commands);
assert!(h.history.is_empty());
}
#[test]
fn prune_all_stale() {
let mut h = History {
history: vec!["x".into(), "y".into()],
};
let commands = make_commands(&["a"]);
h.prune(&commands);
assert!(h.history.is_empty());
}
}