use std::time::{Duration, Instant};
pub const TIMEOUT: Duration = Duration::from_millis(900);
pub enum Action {
Append,
StartNew,
PassThrough,
}
pub fn active(now: Instant, last: Option<Instant>, timeout: Duration) -> bool {
match last {
Some(last) => now.duration_since(last) < timeout,
None => false,
}
}
pub fn action(active: bool, key_is_bound: bool) -> Action {
if active {
Action::Append
} else if key_is_bound {
Action::PassThrough
} else {
Action::StartNew
}
}
pub fn match_prefix<S: AsRef<str>>(names: &[S], buffer: &str) -> Option<usize> {
if buffer.is_empty() {
return None;
}
let needle = buffer.to_lowercase();
names
.iter()
.position(|n| n.as_ref().to_lowercase().starts_with(&needle))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn active_within_and_after_timeout() {
let now = Instant::now();
let recent = now.checked_sub(TIMEOUT / 2).unwrap();
assert!(active(now, Some(recent), TIMEOUT));
let stale = now.checked_sub(TIMEOUT + Duration::from_millis(1)).unwrap();
assert!(!active(now, Some(stale), TIMEOUT));
assert!(!active(now, None, TIMEOUT));
}
#[test]
fn action_three_cases() {
assert!(matches!(action(true, true), Action::Append));
assert!(matches!(action(true, false), Action::Append));
assert!(matches!(action(false, true), Action::PassThrough));
assert!(matches!(action(false, false), Action::StartNew));
}
#[test]
fn match_prefix_first_in_order() {
let names = ["Cargo.toml", "README.md", "readme.txt", "src"];
assert_eq!(match_prefix(&names, "rea"), Some(1));
assert_eq!(match_prefix(&names, "READ"), Some(1));
assert_eq!(match_prefix(&names, "s"), Some(3));
assert_eq!(match_prefix(&names, ""), None);
assert_eq!(match_prefix(&names, "zzz"), None);
}
}