1use 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 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}