Skip to main content

floopy/types/
experiments.rs

1use serde::{Deserialize, Serialize};
2
3/// An A/B routing experiment.
4#[derive(Debug, Clone, Deserialize)]
5pub struct Experiment {
6    /// Experiment id.
7    pub id: String,
8    /// Display name.
9    pub name: String,
10    /// Optional description.
11    pub description: Option<String>,
12    /// Lifecycle state (`active`, `rolled_back`, `completed`).
13    pub status: String,
14    /// Routing rule id for variant A.
15    pub variant_a_routing_rule_id: String,
16    /// Routing rule id for variant B.
17    pub variant_b_routing_rule_id: String,
18    /// Percentage of traffic on variant B.
19    pub split_percentage: i32,
20    /// RFC3339 creation timestamp.
21    pub created_at: String,
22    /// RFC3339 rollback timestamp, if rolled back.
23    pub rolled_back_at: Option<String>,
24}
25
26/// One page of [`crate::resources::Experiments::list`].
27#[derive(Debug, Clone, Deserialize)]
28pub struct ExperimentListPage {
29    /// Experiments in this page.
30    pub items: Vec<Experiment>,
31    /// Cursor for the next page, if any.
32    pub next_cursor: Option<String>,
33    /// Whether more pages follow.
34    pub has_more: bool,
35}
36
37/// Aggregated metrics for one experiment variant.
38#[derive(Debug, Clone, Deserialize)]
39pub struct VariantResults {
40    /// Routing rule id for this variant.
41    pub routing_rule_id: String,
42    /// Number of samples observed.
43    pub sample_size: i64,
44    /// Fraction of successful requests.
45    pub success_rate: f64,
46    /// Mean latency in milliseconds.
47    pub average_latency_ms: f64,
48    /// Mean cost in micro-USD.
49    pub average_cost_micro_usd: f64,
50}
51
52/// Computed outcome of an experiment.
53#[derive(Debug, Clone, Deserialize)]
54pub struct ExperimentResults {
55    /// Experiment id.
56    pub experiment_id: String,
57    /// Variant A metrics.
58    pub variant_a: VariantResults,
59    /// Variant B metrics.
60    pub variant_b: VariantResults,
61    /// `"A"`, `"B"`, `"tie"`, or `None` when undecided.
62    pub winner: Option<String>,
63    /// RFC3339 timestamp the results were computed.
64    pub computed_at: String,
65}
66
67/// Filters for [`crate::resources::Experiments::list`].
68#[derive(Debug, Clone, Default)]
69pub struct ExperimentListParams {
70    /// Restrict to one lifecycle state.
71    pub status: Option<String>,
72    /// Inclusive lower bound (RFC3339).
73    pub from: Option<String>,
74    /// Inclusive upper bound (RFC3339).
75    pub to: Option<String>,
76    /// Page size.
77    pub limit: Option<u32>,
78    /// Opaque pagination cursor.
79    pub cursor: Option<String>,
80}
81
82impl ExperimentListParams {
83    pub(crate) fn query(&self) -> Vec<(String, String)> {
84        let mut q = Vec::new();
85        if let Some(v) = &self.status {
86            q.push(("status".to_owned(), v.clone()));
87        }
88        if let Some(v) = &self.from {
89            q.push(("from".to_owned(), v.clone()));
90        }
91        if let Some(v) = &self.to {
92            q.push(("to".to_owned(), v.clone()));
93        }
94        if let Some(v) = self.limit {
95            q.push(("limit".to_owned(), v.to_string()));
96        }
97        if let Some(v) = &self.cursor {
98            q.push(("cursor".to_owned(), v.clone()));
99        }
100        q
101    }
102}
103
104/// Arguments for [`crate::resources::Experiments::create`].
105#[derive(Debug, Clone, Serialize)]
106pub struct ExperimentCreateParams {
107    /// Display name.
108    pub name: String,
109    /// Routing rule id for variant A.
110    pub variant_a_routing_rule_id: String,
111    /// Routing rule id for variant B.
112    pub variant_b_routing_rule_id: String,
113    /// Optional description.
114    #[serde(skip_serializing_if = "Option::is_none")]
115    pub description: Option<String>,
116    /// Optional percentage of traffic on variant B.
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub split_percentage: Option<i32>,
119}