agent-tui-usecases 1.1.0

Use cases and ports for agent-tui terminal automation
Documentation
//! Wait use case.

use std::sync::Arc;
use std::time::Duration;

use crate::domain::WaitInput;
use crate::domain::WaitOutput;
use crate::usecases::ports::Clock;
use crate::usecases::ports::SessionError;
use crate::usecases::ports::SessionRepository;
use crate::usecases::wait_condition::StableTracker;
use crate::usecases::wait_condition::WaitCondition;
use crate::usecases::wait_condition::check_condition;

pub trait WaitUseCase: Send + Sync {
    fn execute(&self, input: WaitInput) -> Result<WaitOutput, SessionError>;
}

pub struct WaitUseCaseImpl<R: SessionRepository> {
    repository: Arc<R>,
    clock: Arc<dyn Clock>,
}

impl<R: SessionRepository> WaitUseCaseImpl<R> {
    pub fn new(repository: Arc<R>, clock: Arc<dyn Clock>) -> Self {
        Self { repository, clock }
    }
}

impl<R: SessionRepository> WaitUseCase for WaitUseCaseImpl<R> {
    fn execute(&self, input: WaitInput) -> Result<WaitOutput, SessionError> {
        let session = self.repository.resolve(input.session_id.as_ref())?;
        let timeout = Duration::from_millis(input.timeout_ms);
        let start = self.clock.now();

        let condition = WaitCondition::parse(input.condition, input.text.as_deref())
            .map_err(|e| SessionError::InvalidKey(e.to_string()))?;

        let mut stable_tracker = StableTracker::new(3);
        let poll_interval = Duration::from_millis(50);
        let subscription = session.stream_subscribe();

        loop {
            session.update()?;

            if check_condition(session.as_ref(), &condition, &mut stable_tracker) {
                let elapsed_ms = self.clock.elapsed_ms(start);
                return Ok(WaitOutput {
                    found: true,
                    elapsed_ms,
                });
            }

            if self.clock.elapsed(start) >= timeout {
                let elapsed_ms = self.clock.elapsed_ms(start);
                return Ok(WaitOutput {
                    found: false,
                    elapsed_ms,
                });
            }

            let remaining = timeout.saturating_sub(self.clock.elapsed(start));
            let wait_timeout = remaining.min(poll_interval);
            let _ = subscription.wait(Some(wait_timeout));
        }
    }
}

#[cfg(test)]
#[path = "wait_tests.rs"]
mod tests;