tui_pages/command/
resolver.rs1use crate::command::{CommandRegistry, CommandResponse};
2use std::time::{Duration, Instant};
3use tracing::debug;
4
5#[derive(Debug, Clone)]
6pub struct CommandResolver<A> {
7 pub registry: CommandRegistry<A>,
8 last_input_time: Option<Instant>,
9 timeout: Duration,
10}
11
12impl<A> CommandResolver<A> {
13 pub fn new(registry: CommandRegistry<A>, timeout_ms: u64) -> Self {
14 Self {
15 registry,
16 last_input_time: None,
17 timeout: Duration::from_millis(timeout_ms),
18 }
19 }
20
21 pub fn touch(&mut self) {
22 self.last_input_time = Some(Instant::now());
23 }
24
25 pub fn reset(&mut self) {
26 self.last_input_time = None;
27 }
28
29 pub fn is_idle(&self) -> bool {
30 self.last_input_time
31 .map(|time| time.elapsed() > self.timeout)
32 .unwrap_or(true)
33 }
34}
35
36impl<A: Clone> CommandResolver<A> {
37 pub fn process(&self, input: &str) -> CommandResponse<A> {
38 let input = input.trim();
39 if input.is_empty() {
40 return CommandResponse::Empty;
41 }
42
43 debug!(input, "processing command input");
44
45 if let Some(action) = self.registry.match_action(input) {
46 CommandResponse::Execute(action)
47 } else if self.registry.is_prefix(input) {
48 CommandResponse::Incomplete(self.registry.get_hints(input))
49 } else {
50 CommandResponse::Unknown
51 }
52 }
53
54 pub fn get_feedback(&self, input: &str) -> Option<String> {
55 if input.trim().is_empty() || !self.is_idle() {
56 return None;
57 }
58
59 match self.process(input) {
60 CommandResponse::Execute(_) | CommandResponse::Empty => None,
61 CommandResponse::Unknown => Some("[Unknown command]".to_string()),
62 CommandResponse::Incomplete(hints) => {
63 let hint_str: Vec<String> = hints
64 .iter()
65 .take(5)
66 .map(|hint| format!("{} ({})", hint.alias, hint.action_name))
67 .collect();
68 Some(format!("[Incomplete: {}]", hint_str.join(", ")))
69 }
70 }
71 }
72}