Skip to main content

agent_tui_usecases/usecases/
wait.rs

1//! Wait use case.
2
3use std::sync::Arc;
4use std::time::Duration;
5
6use crate::domain::WaitInput;
7use crate::domain::WaitOutput;
8use crate::usecases::ports::Clock;
9use crate::usecases::ports::SessionError;
10use crate::usecases::ports::SessionRepository;
11use crate::usecases::wait_condition::StableTracker;
12use crate::usecases::wait_condition::WaitCondition;
13use crate::usecases::wait_condition::check_condition;
14
15pub trait WaitUseCase: Send + Sync {
16    fn execute(&self, input: WaitInput) -> Result<WaitOutput, SessionError>;
17}
18
19pub struct WaitUseCaseImpl<R: SessionRepository> {
20    repository: Arc<R>,
21    clock: Arc<dyn Clock>,
22}
23
24impl<R: SessionRepository> WaitUseCaseImpl<R> {
25    pub fn new(repository: Arc<R>, clock: Arc<dyn Clock>) -> Self {
26        Self { repository, clock }
27    }
28}
29
30impl<R: SessionRepository> WaitUseCase for WaitUseCaseImpl<R> {
31    fn execute(&self, input: WaitInput) -> Result<WaitOutput, SessionError> {
32        let session = self.repository.resolve(input.session_id.as_ref())?;
33        let timeout = Duration::from_millis(input.timeout_ms);
34        let start = self.clock.now();
35
36        let condition = WaitCondition::parse(input.condition, input.text.as_deref())
37            .map_err(|e| SessionError::InvalidKey(e.to_string()))?;
38
39        let mut stable_tracker = StableTracker::new(3);
40        let poll_interval = Duration::from_millis(50);
41        let subscription = session.stream_subscribe();
42
43        loop {
44            session.update()?;
45
46            if check_condition(session.as_ref(), &condition, &mut stable_tracker) {
47                let elapsed_ms = self.clock.elapsed_ms(start);
48                return Ok(WaitOutput {
49                    found: true,
50                    elapsed_ms,
51                });
52            }
53
54            if self.clock.elapsed(start) >= timeout {
55                let elapsed_ms = self.clock.elapsed_ms(start);
56                return Ok(WaitOutput {
57                    found: false,
58                    elapsed_ms,
59                });
60            }
61
62            let remaining = timeout.saturating_sub(self.clock.elapsed(start));
63            let wait_timeout = remaining.min(poll_interval);
64            let _ = subscription.wait(Some(wait_timeout));
65        }
66    }
67}
68
69#[cfg(test)]
70#[path = "wait_tests.rs"]
71mod tests;