Skip to main content

super_types/
usage.rs

1//! Account usage reports shared by the platform and CLI.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use uuid::Uuid;
6
7#[derive(Clone, Debug, Serialize, Deserialize)]
8#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
9pub struct Repository {
10    pub id: Uuid,
11    pub name: String,
12}
13
14#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
15#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
16#[serde(rename_all = "snake_case")]
17pub enum CheckStatus {
18    Pass,
19    Fail,
20    Conditional,
21    Checking,
22    Queued,
23    Blocked,
24    Canceled,
25    Error,
26}
27
28impl CheckStatus {
29    #[must_use]
30    pub fn from_check(check: &crate::Check) -> Self {
31        if let Some(result) = &check.result {
32            return match result.outcome {
33                crate::CheckOutcome::Pass => CheckStatus::Pass,
34                crate::CheckOutcome::Fail => CheckStatus::Fail,
35                crate::CheckOutcome::Conditional => CheckStatus::Conditional,
36            };
37        }
38        if let Some(error) = check.operational_error {
39            return match error {
40                crate::CheckOperationalError::Blocked => CheckStatus::Blocked,
41                crate::CheckOperationalError::Canceled => CheckStatus::Canceled,
42                crate::CheckOperationalError::Error | crate::CheckOperationalError::MissingConclusion => {
43                    CheckStatus::Error
44                }
45            };
46        }
47        if check.terminal {
48            return CheckStatus::Error;
49        }
50        match check.presentation.status.label.as_str() {
51            "queued" | "pending" => CheckStatus::Queued,
52            _ => CheckStatus::Checking,
53        }
54    }
55}
56
57#[derive(Clone, Debug, Serialize, Deserialize)]
58#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
59#[serde(rename_all = "camelCase")]
60pub struct UsageCheck {
61    pub id: String,
62    #[cfg_attr(feature = "typescript", ts(type = "number"))]
63    pub number: u64,
64    pub repository_id: Uuid,
65    pub name: String,
66    pub path: String,
67    pub revision: String,
68    pub started_at: DateTime<Utc>,
69    pub status: CheckStatus,
70    #[cfg_attr(feature = "typescript", ts(type = "number | null"))]
71    pub period_tokens: Option<u64>,
72    pub usage_pending: bool,
73}
74
75#[derive(Clone, Debug, Serialize, Deserialize)]
76#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
77#[serde(rename_all = "camelCase")]
78pub struct UsageBucket {
79    pub start: DateTime<Utc>,
80    pub end: DateTime<Utc>,
81    #[cfg_attr(feature = "typescript", ts(type = "number | null"))]
82    pub tokens: Option<u64>,
83    pub current: bool,
84}
85
86#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
87#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
88pub enum Period {
89    #[serde(rename = "today")]
90    Today,
91    #[serde(rename = "7d")]
92    Week,
93    #[default]
94    #[serde(rename = "30d")]
95    Month,
96}
97
98#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
99#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
100#[serde(rename_all = "camelCase")]
101pub enum UsageSort {
102    #[default]
103    StartedAt,
104    Tokens,
105}
106
107#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
108#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
109#[serde(rename_all = "lowercase")]
110pub enum Direction {
111    Ascending,
112    #[default]
113    Descending,
114}
115
116#[derive(Clone, Debug, Serialize, Deserialize)]
117#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
118#[serde(rename_all = "camelCase", deny_unknown_fields)]
119pub struct UsageQuery {
120    #[serde(default)]
121    pub period: Period,
122    pub timezone: String,
123    pub repository_id: Option<Uuid>,
124    #[serde(default)]
125    pub sort: UsageSort,
126    #[serde(default)]
127    pub direction: Direction,
128    #[serde(default = "first_page")]
129    pub page: u32,
130    #[serde(default, skip_serializing_if = "Option::is_none")]
131    pub page_size: Option<u32>,
132    pub snapshot: Option<Uuid>,
133}
134const fn first_page() -> u32 {
135    1
136}
137
138#[derive(Clone, Debug, Serialize, Deserialize)]
139#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
140#[serde(rename_all = "camelCase")]
141pub struct UsageReport {
142    pub snapshot: Uuid,
143    pub as_of: DateTime<Utc>,
144    pub start: DateTime<Utc>,
145    pub end: DateTime<Utc>,
146    pub repositories: Vec<Repository>,
147    pub checks: Vec<UsageCheck>,
148    pub buckets: Vec<UsageBucket>,
149    #[cfg_attr(feature = "typescript", ts(type = "number | null"))]
150    pub total_tokens: Option<u64>,
151    pub total_checks: u32,
152    pub page: u32,
153    pub page_size: u32,
154    pub usage_available: bool,
155    pub pending: bool,
156    /// Account-wide allowance; independent of this report's filters and pagination snapshot.
157    pub quota: Option<TokenQuota>,
158}
159
160#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
161#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
162#[serde(rename_all = "camelCase")]
163pub struct TokenQuota {
164    pub as_of: DateTime<Utc>,
165    pub window_start: DateTime<Utc>,
166    #[cfg_attr(feature = "typescript", ts(type = "number"))]
167    pub limit: u64,
168    #[cfg_attr(feature = "typescript", ts(type = "number"))]
169    pub used: u64,
170    #[cfg_attr(feature = "typescript", ts(type = "number"))]
171    pub remaining: u64,
172    pub exhausted: bool,
173    pub next_available_at: Option<DateTime<Utc>>,
174}