wind_tunnel_runner 0.1.0-alpha.2

The Wind Tunnel runner
Documentation
use std::{fmt::Debug, sync::Arc};
use wind_tunnel_core::prelude::{DelegatedShutdownListener, ShutdownHandle};
use wind_tunnel_instruments::Reporter;

use crate::executor::Executor;

pub trait UserValuesConstraint: Default + Debug + Send + Sync + 'static {}

/// The context created by the runner for a scenario run. This context is visible to all agents
/// so it is read-only from within agent hooks but can be modified by global hooks.
///
/// This type has a generic parameter for the user-defined state that can be stored in the context.
#[derive(Debug)]
pub struct RunnerContext<RV: UserValuesConstraint> {
    executor: Arc<Executor>,
    reporter: Arc<Reporter>,
    shutdown_handle: ShutdownHandle,
    connection_string: String,
    value: RV,
}

impl<RV: UserValuesConstraint> RunnerContext<RV> {
    pub(crate) fn new(
        executor: Arc<Executor>,
        reporter: Arc<Reporter>,
        shutdown_handle: ShutdownHandle,
        connection_string: String,
    ) -> Self {
        Self {
            executor,
            reporter,
            shutdown_handle,
            connection_string,
            value: Default::default(),
        }
    }

    /// A handle to the [Executor] for the runner.
    ///
    /// This is used to run async code within hooks.
    pub fn executor(&self) -> &Arc<Executor> {
        &self.executor
    }

    /// A handle to the reporter for the runner.
    ///
    /// This is used to record data in-memory. You shouldn't need to access it directly.
    /// This should be passed to an instrumented client so that it can report data to the runner.
    pub fn reporter(&self) -> Arc<Reporter> {
        self.reporter.clone()
    }

    /// Get a new shutdown listener that will be triggered when the runner is shutdown.
    ///
    /// This is provided in case you are doing something unexpected and need to hook into the shutdown process.
    /// In general, please consider using [Executor::execute_in_place] which automatically handles shutdown.
    pub fn new_shutdown_listener(&self) -> DelegatedShutdownListener {
        self.shutdown_handle.new_listener()
    }

    /// Connection string for the target service of the scenario, supplied by the user via the CLI.
    pub fn get_connection_string(&self) -> &str {
        &self.connection_string
    }

    /// Get mutable access to the user-defined state for the runner.
    pub fn get_mut(&mut self) -> &mut RV {
        &mut self.value
    }

    /// Get the user-defined state for the runner.
    pub fn get(&self) -> &RV {
        &self.value
    }

    /// Force stop the scenario.
    ///
    /// This will trigger shutdown of all agents and the runner. It is primarily exposed for testing
    /// but if you need to stop the scenario from within a hook, you can use this. It is a better
    /// alternative to using a panic if you really need the scenario to stop.
    pub fn force_stop_scenario(&self) {
        self.shutdown_handle.shutdown();
    }
}

/// The context available to an agent during a scenario run. One context is created for each agent
/// so it is safe to store agent-specific state in this context.
///
/// The context holds a reference to the [RunnerContext] so that the agent can read shared state
/// for the scenario.
///
/// This type is generic over two parameters, one for the [AgentContext] and one for the [RunnerContext].
/// These are used to store user-defined state for the agent and the runner respectively.
#[derive(Debug)]
pub struct AgentContext<RV: UserValuesConstraint, V: UserValuesConstraint> {
    agent_id: String,
    runner_context: Arc<RunnerContext<RV>>,
    shutdown_listener: DelegatedShutdownListener,
    value: V,
}

impl<RV: UserValuesConstraint, V: UserValuesConstraint> AgentContext<RV, V> {
    pub(crate) fn new(
        agent_id: String,
        runner_context: Arc<RunnerContext<RV>>,
        shutdown_listener: DelegatedShutdownListener,
    ) -> Self {
        Self {
            agent_id,
            runner_context,
            shutdown_listener,
            value: Default::default(),
        }
    }

    /// A value generated by the runner that you can use to identify yourself making requests.
    ///
    /// This value is unique within the runner but it is *not* unique across multiple runners.
    pub fn agent_id(&self) -> &str {
        &self.agent_id
    }

    /// A handle to the runner context for the scenario.
    pub fn runner_context(&self) -> &Arc<RunnerContext<RV>> {
        &self.runner_context
    }

    /// Get the shutdown listener which will be triggered when the runner is shutdown.
    ///
    /// This is provided in case you are doing something unexpected and need to hook into the shutdown process.
    /// In general, please consider using [Executor::execute_in_place] which automatically handles shutdown.
    pub fn shutdown_listener(&mut self) -> &mut DelegatedShutdownListener {
        &mut self.shutdown_listener
    }

    /// Get mutable access to the user-defined state for the agent.
    pub fn get_mut(&mut self) -> &mut V {
        &mut self.value
    }

    /// Get the user-defined state for the agent.
    pub fn get(&self) -> &V {
        &self.value
    }
}