1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//! The [`Scenario`] struct defines the workload definition layer of karga
//!
//! A *scenario* represents a complete test or benchmark definition — it specifies
//! what to run (`action`), and how the results will be aggregated (`Aggregate`).
//!
//! Typically, a scenario is constructed using [`typed_builder::TypedBuilder`] and then passed
//! down to an [`Executor`], acting as a configuration object
//!
//! # Example
//! ```rust,ignore
//! use std::time::Duration;
//! use karga::Scenario;
//! use karga::metric::BasicMetric;
//! use karga::aggregate::BasicAggregate;
//!
//! // Build a scenario. The `action` produces a metric; the executor runs it.
//! let scenario = Scenario::builder()
//! .name("example")
//! .action(|| async {
//! // Simulate work and produce a metric
//! BasicMetric { latency: Duration::from_millis(10), success: true, bytes: 512 }
//! })
//! .build();
//! ```
//!
//! # Design goals
//! - **Composability:** all major components remain generic.
//! - **Determinism:** scenarios define repeatable, isolated executions.
//!
//! # Notes on `action`
//!
//! The `action` is the user-provided function (typically an async closure) that produces a
//! single metric sample. Important guidelines:
//!
//! - **Closure capture for shared state:** the action cannot receive arguments, so capture
//! any shared clients or resources in the closure (for example, an `reqwest::Client`).
//! - **No heavy initialization inside the action:** constructing heavy objects inside the
//! action (such as creating a new HTTP client on every invocation) will drastically
//! reduce throughput. In extreme cases this can change performance by orders of magnitude
//! (e.g., a benchmark running at hundreds of thousands of RPS could collapse to only
//! a few hundred RPS if the action creates expensive resources each call).
//! - **Prefer cloning lightweight handles:** if a client is cheaply clonable, clone it
//! inside the closure (captured from the outer scope) and reuse the underlying connection
//! pool or socket as appropriate.
use crateAggregate;
use Future;
use PhantomData;
use TypedBuilder;
/// Represents a complete execution setup — action, executor, and aggregation logic.
///
/// `Scenario` is generic over four parameters:
/// - `A`: the [`Aggregate`] implementation used to accumulate metrics.
/// - `F`: the function type that produces the asynchronous operation.
/// - `Fut`: the future returned by the action.
+ Send + Sync + Clone + 'static,
Fut: + Send,