Skip to main content

ironflow_store/entities/
stats.rs

1//! [`RunStats`] — aggregated statistics across all runs.
2
3use rust_decimal::Decimal;
4use serde::{Deserialize, Serialize};
5
6/// Aggregated statistics for all runs in the store.
7///
8/// Computed efficiently by the store implementation (single SQL query in PostgreSQL,
9/// in-memory aggregation in InMemoryStore).
10///
11/// # Examples
12///
13/// ```
14/// use ironflow_store::entities::RunStats;
15/// use rust_decimal::Decimal;
16///
17/// let stats = RunStats {
18///     total_runs: 100,
19///     completed_runs: 80,
20///     failed_runs: 15,
21///     cancelled_runs: 5,
22///     active_runs: 0,
23///     total_cost_usd: Decimal::new(4250, 2),
24///     total_duration_ms: 3600000,
25/// };
26/// assert_eq!(stats.total_runs, 100);
27/// ```
28#[derive(Debug, Clone, Default, Serialize, Deserialize)]
29pub struct RunStats {
30    /// Total number of runs ever created.
31    pub total_runs: u64,
32    /// Runs that reached the `Completed` state.
33    pub completed_runs: u64,
34    /// Runs that reached the `Failed` state.
35    pub failed_runs: u64,
36    /// Runs that reached the `Cancelled` state.
37    pub cancelled_runs: u64,
38    /// Runs in an active state: `Pending`, `Running`, or `Retrying`.
39    pub active_runs: u64,
40    /// Total cost in USD across all runs.
41    pub total_cost_usd: Decimal,
42    /// Total execution time in milliseconds across all runs.
43    pub total_duration_ms: u64,
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[test]
51    fn default_is_zeros() {
52        let stats = RunStats::default();
53        assert_eq!(stats.total_runs, 0);
54        assert_eq!(stats.completed_runs, 0);
55        assert_eq!(stats.failed_runs, 0);
56        assert_eq!(stats.cancelled_runs, 0);
57        assert_eq!(stats.active_runs, 0);
58        assert_eq!(stats.total_cost_usd, Decimal::ZERO);
59        assert_eq!(stats.total_duration_ms, 0);
60    }
61
62    #[test]
63    fn serde_roundtrip() {
64        let stats = RunStats {
65            total_runs: 100,
66            completed_runs: 80,
67            failed_runs: 15,
68            cancelled_runs: 5,
69            active_runs: 0,
70            total_cost_usd: Decimal::new(4250, 2),
71            total_duration_ms: 3600000,
72        };
73        let json = serde_json::to_string(&stats).expect("serialize");
74        let back: RunStats = serde_json::from_str(&json).expect("deserialize");
75        assert_eq!(stats.total_runs, back.total_runs);
76        assert_eq!(stats.completed_runs, back.completed_runs);
77        assert_eq!(stats.failed_runs, back.failed_runs);
78        assert_eq!(stats.cancelled_runs, back.cancelled_runs);
79        assert_eq!(stats.active_runs, back.active_runs);
80        assert_eq!(stats.total_cost_usd, back.total_cost_usd);
81        assert_eq!(stats.total_duration_ms, back.total_duration_ms);
82    }
83}