tastty-driver 0.1.0

Terminal automation driver built on tastty
//! Output-condition waits.

#[cfg(feature = "async")]
use std::sync::Arc;
use std::time::Duration;

use super::Session;
use crate::{WaitCondition, WaitError, WaitOutcome};

impl Session {
    /// Wait for a condition to become true and return the matching snapshot
    /// alongside any match metadata.
    ///
    /// Accepts any `impl Into<WaitCondition>` so the typed wrappers
    /// returned by [`WaitCondition::regex`] and [`WaitCondition::stable`]
    /// can be passed without an explicit `.into()` at the call site.
    ///
    /// # Errors
    ///
    /// Returns [`WaitError::Timeout`] when the deadline elapses before the
    /// condition matches, [`WaitError::ProcessExitedBeforeMatch`] when the
    /// child exits without satisfying the condition,
    /// [`WaitError::InvalidRegex`] for malformed regex conditions, and
    /// other [`WaitError`] variants for transport-level failures.
    pub fn wait(
        &self,
        condition: impl Into<WaitCondition>,
        timeout: Duration,
    ) -> std::result::Result<WaitOutcome, WaitError> {
        crate::wait::wait(self, condition.into(), timeout)
    }

    /// Asynchronous variant of [`Session::wait`].
    ///
    /// Returns a runtime-agnostic [`Future`](std::future::Future) built on
    /// `std` primitives only (no tokio dependency). The future registers
    /// a [`Waker`](std::task::Waker) on the session's shared output
    /// notifier; the PTY reader thread fires the notifier after each
    /// parser tick, prompting the future to re-probe its condition. The
    /// future also registers a deadline on the notifier's timer thread so
    /// the timeout (and any active [`WaitCondition::stable`] settle
    /// window) is enforced without polling. There is no per-future worker
    /// thread.
    ///
    /// # Cancellation
    ///
    /// Dropping the returned future removes its waker entries from the
    /// notifier. No background thread is spawned on its behalf, so
    /// cancellation is immediate and leak-free. This behaves correctly
    /// regardless of executor: tokio `JoinHandle::abort`, smol task
    /// cancellation, and manual drop all trigger the same path.
    ///
    /// The future holds [`Arc`]s of the underlying terminal and the
    /// session's output notifier, so it can finish unwinding even if the
    /// originating [`Session`] is dropped while the wait is in flight.
    ///
    /// # Send bound
    ///
    /// The returned future is `Send + 'static` so it can be spawned on
    /// any standard executor. This requires `tastty::Terminal: Send`,
    /// which holds on every platform tastty currently supports.
    #[cfg(feature = "async")]
    pub fn wait_async(
        &self,
        condition: impl Into<WaitCondition>,
        timeout: Duration,
    ) -> impl std::future::Future<Output = std::result::Result<WaitOutcome, WaitError>> + Send + 'static
    {
        crate::wait::async_future::WaitFuture::new(
            Arc::clone(&self.terminal),
            Arc::clone(&self.output_notifier),
            condition.into(),
            timeout,
        )
    }
}