use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct UsageReport {
pub api_key_name: String,
pub since: DateTime<Utc>,
pub until: DateTime<Utc>,
pub sessions: SessionCounts,
pub compute: ComputeTotals,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bundle_builds: Option<BundleBuildUsage>,
}
#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
pub struct BundleBuildUsage {
pub requested: u64,
}
#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
pub struct SessionCounts {
pub started: u64,
pub completed: u64,
pub failed: u64,
}
#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
pub struct ComputeTotals {
pub executed_slot_count: u64,
pub session_duration_ms: u64,
}
impl SessionCounts {
pub fn saturating_add(self, other: Self) -> Self {
Self {
started: self.started.saturating_add(other.started),
completed: self.completed.saturating_add(other.completed),
failed: self.failed.saturating_add(other.failed),
}
}
}
impl ComputeTotals {
pub fn saturating_add(self, other: Self) -> Self {
Self {
executed_slot_count: self
.executed_slot_count
.saturating_add(other.executed_slot_count),
session_duration_ms: self
.session_duration_ms
.saturating_add(other.session_duration_ms),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn report(bundle_builds: Option<BundleBuildUsage>) -> UsageReport {
UsageReport {
api_key_name: "k".to_string(),
since: "2026-01-01T00:00:00Z".parse().unwrap(),
until: "2026-01-02T00:00:00Z".parse().unwrap(),
sessions: SessionCounts::default(),
compute: ComputeTotals::default(),
bundle_builds,
}
}
#[test]
fn usage_report_absent_bundle_builds_round_trips_to_none() {
let json = serde_json::to_string(&report(None)).unwrap();
assert!(!json.contains("bundle_builds"));
let parsed: UsageReport = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.bundle_builds, None);
}
#[test]
fn usage_report_present_bundle_builds_round_trips() {
let json = serde_json::to_string(&report(Some(BundleBuildUsage { requested: 3 }))).unwrap();
let parsed: UsageReport = serde_json::from_str(&json).unwrap();
assert_eq!(
parsed.bundle_builds,
Some(BundleBuildUsage { requested: 3 })
);
}
}