use std::collections::HashMap;
use chrono::{DateTime, Utc};
use log::debug;
use crate::history::CommandEntry;
const RECENCY_WEIGHT: f32 = 0.6;
const FREQUENCY_WEIGHT: f32 = 0.4;
pub fn search_commands(
term: &str,
history: &[CommandEntry],
max_results: usize,
) -> Vec<CommandEntry> {
debug!("Search commands with term: {}", term);
let term = term.to_lowercase();
let mut command_scores: HashMap<String, f32> = HashMap::new();
for entry in history.iter() {
let match_score = match entry.command.to_lowercase().find(&term) {
Some(0) => 1.0, Some(pos) => 0.5 - pos as f32 / entry.command.len() as f32, None => 0.0, };
if match_score > 0.0 {
let seconds_ago = (Utc::now() - entry.timestamp).num_seconds() as f32;
let recency_weight = 1.0 / (1.0 + seconds_ago.log10());
let frequency_weight = command_scores
.get(&entry.command)
.map_or(1.0, |&score| score + 1.0);
let total_score = match_score
* (RECENCY_WEIGHT * recency_weight + FREQUENCY_WEIGHT * frequency_weight);
command_scores
.entry(entry.command.clone())
.and_modify(|e| *e = f32::max(*e, total_score))
.or_insert(total_score);
}
}
let mut sorted_commands: Vec<_> = command_scores.into_iter().collect();
sorted_commands.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
sorted_commands
.into_iter()
.take(max_results)
.map(|(cmd, _)| CommandEntry {
command: cmd,
timestamp: DateTime::<Utc>::default(), })
.collect()
}
pub fn get_frequent_commands(history: &[CommandEntry], max_results: usize) -> Vec<CommandEntry> {
debug!("Get frequent commands");
let mut command_data: HashMap<String, (usize, DateTime<Utc>)> = HashMap::new();
for entry in history.iter() {
command_data
.entry(entry.command.clone())
.and_modify(|(count, timestamp)| {
*count += 1;
if entry.timestamp > *timestamp {
*timestamp = entry.timestamp;
}
})
.or_insert((1, entry.timestamp));
}
let mut scored_commands: Vec<_> = command_data
.into_iter()
.map(|(cmd, (count, timestamp))| {
let seconds_ago = (Utc::now() - timestamp).num_seconds() as f32;
let recency_weight = 1.0 / (1.0 + seconds_ago.log10());
let frequency_weight = count as f32;
let total_score = RECENCY_WEIGHT * recency_weight + FREQUENCY_WEIGHT * frequency_weight;
(cmd, total_score)
})
.collect();
scored_commands.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
scored_commands
.into_iter()
.take(max_results)
.map(|(cmd, _)| CommandEntry {
command: cmd,
timestamp: DateTime::<Utc>::default(), })
.collect()
}