1use std::collections::HashMap;
2#[cfg(feature = "display")]
3use std::fmt::Write;
4
5use chrono::{DateTime, NaiveDate, Utc};
6#[cfg(feature = "display")]
7use crossterm::style::Stylize;
8use serde::{Deserialize, Serialize};
9use strum::{EnumString, IntoStaticStr};
10
11use super::project::ProjectUsageResponse;
12
13#[derive(Debug, Deserialize, Serialize)]
14#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
15#[typeshare::typeshare]
16pub struct UserResponse {
17 pub id: String,
18 pub auth0_id: Option<String>,
20 pub created_at: DateTime<Utc>,
21 pub key: Option<String>,
23 pub account_tier: AccountTier,
24 pub subscriptions: Option<Vec<Subscription>>,
25 pub flags: Option<Vec<String>>,
26}
27
28impl UserResponse {
29 #[cfg(feature = "display")]
30 pub fn to_string_colored(&self) -> String {
31 let mut s = String::new();
32 writeln!(&mut s, "{}", "Account info:".bold()).unwrap();
33 writeln!(&mut s, " User ID: {}", self.id).unwrap();
34 writeln!(
35 &mut s,
36 " Account tier: {}",
37 self.account_tier.to_string_fancy()
38 )
39 .unwrap();
40 if let Some(subs) = self.subscriptions.as_ref() {
41 if !subs.is_empty() {
42 writeln!(&mut s, " Subscriptions:").unwrap();
43 for sub in subs {
44 writeln!(
45 &mut s,
46 " - {}: Type: {}, Quantity: {}, Created: {}, Updated: {}",
47 sub.id, sub.r#type, sub.quantity, sub.created_at, sub.updated_at,
48 )
49 .unwrap();
50 }
51 }
52 }
53 if let Some(flags) = self.flags.as_ref() {
54 if !flags.is_empty() {
55 writeln!(&mut s, " Feature flags:").unwrap();
56 for flag in flags {
57 writeln!(&mut s, " - {}", flag).unwrap();
58 }
59 }
60 }
61
62 s
63 }
64}
65
66#[derive(
67 Clone,
69 Debug,
70 Default,
71 Eq,
72 PartialEq,
73 Ord,
74 PartialOrd,
75 Deserialize,
77 Serialize,
78 EnumString,
80 IntoStaticStr,
81 strum::Display,
82)]
83#[serde(rename_all = "lowercase")]
84#[strum(serialize_all = "lowercase")]
85#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
86#[typeshare::typeshare]
87pub enum AccountTier {
88 #[default]
89 Basic,
90 ProTrial,
92 PendingPaymentPro,
95 CancelledPro,
98 Pro,
99 Growth,
100 Employee,
102 Admin,
104
105 #[cfg(feature = "unknown-variants")]
107 #[doc(hidden)]
108 #[typeshare(skip)]
109 #[serde(untagged, skip_serializing)]
110 #[strum(default, to_string = "Unknown: {0}")]
111 Unknown(String),
112}
113impl AccountTier {
114 pub fn to_string_fancy(&self) -> String {
115 match self {
116 Self::Basic => "Community".to_owned(),
117 Self::ProTrial => "Pro Trial".to_owned(),
118 Self::PendingPaymentPro => "Community (pending payment for Pro)".to_owned(),
119 Self::CancelledPro => "Pro (subscription cancelled)".to_owned(),
120 Self::Pro => "Pro".to_owned(),
121 Self::Growth => "Growth".to_owned(),
122 Self::Employee => "Employee".to_owned(),
123 Self::Admin => "Admin".to_owned(),
124 #[cfg(feature = "unknown-variants")]
125 Self::Unknown(_) => self.to_string(),
126 }
127 }
128}
129
130#[cfg(test)]
131mod account_tier_tests {
132 use super::*;
133 #[test]
134 fn deser() {
135 assert_eq!(
136 serde_json::from_str::<AccountTier>("\"basic\"").unwrap(),
137 AccountTier::Basic
138 );
139 }
140 #[cfg(feature = "unknown-variants")]
141 #[test]
142 fn unknown_deser() {
143 assert_eq!(
144 serde_json::from_str::<AccountTier>("\"\"").unwrap(),
145 AccountTier::Unknown("".to_string())
146 );
147 assert_eq!(
148 serde_json::from_str::<AccountTier>("\"hisshiss\"").unwrap(),
149 AccountTier::Unknown("hisshiss".to_string())
150 );
151 assert!(serde_json::to_string(&AccountTier::Unknown("asdf".to_string())).is_err());
152 }
153 #[cfg(not(feature = "unknown-variants"))]
154 #[test]
155 fn not_unknown_deser() {
156 assert!(serde_json::from_str::<AccountTier>("\"\"").is_err());
157 assert!(serde_json::from_str::<AccountTier>("\"hisshiss\"").is_err());
158 }
159}
160
161#[derive(Debug, Deserialize, Serialize)]
162#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
163#[typeshare::typeshare]
164pub struct Subscription {
165 pub id: String,
166 pub r#type: SubscriptionType,
167 pub quantity: i32,
168 pub created_at: DateTime<Utc>,
169 pub updated_at: DateTime<Utc>,
170}
171
172#[derive(Debug, Deserialize)]
173#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
174#[typeshare::typeshare]
175pub struct SubscriptionRequest {
176 pub id: String,
177 pub r#type: SubscriptionType,
178 pub quantity: i32,
179}
180
181#[derive(
182 Clone,
184 Debug,
185 Eq,
186 PartialEq,
187 Deserialize,
189 Serialize,
190 EnumString,
192 strum::Display,
193 IntoStaticStr,
194)]
195#[serde(rename_all = "lowercase")]
196#[strum(serialize_all = "lowercase")]
197#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
198#[typeshare::typeshare]
199pub enum SubscriptionType {
200 Pro,
201 Rds,
202
203 #[cfg(feature = "unknown-variants")]
205 #[doc(hidden)]
206 #[typeshare(skip)]
207 #[serde(untagged, skip_serializing)]
208 #[strum(default, to_string = "Unknown: {0}")]
209 Unknown(String),
210}
211
212#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
213#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
214#[typeshare::typeshare]
215pub struct CreateAccountRequest {
216 pub auth0_id: String,
217 pub account_tier: AccountTier,
218}
219
220#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
221#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
222#[typeshare::typeshare]
223pub struct UpdateAccountTierRequest {
224 pub account_tier: AccountTier,
225}
226
227#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
229#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
230#[typeshare::typeshare]
231pub struct UserBillingCycle {
232 pub start: NaiveDate,
235
236 pub end: NaiveDate,
239}
240
241#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
242#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
243#[typeshare::typeshare]
244pub struct UserUsageCustomDomains {
245 pub used: u32,
246 pub limit: u32,
247}
248
249#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
250#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
251#[typeshare::typeshare]
252pub struct UserUsageProjects {
253 pub used: u32,
254 pub limit: u32,
255}
256
257#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
258#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
259#[typeshare::typeshare]
260pub struct UserUsageTeamMembers {
261 pub used: u32,
262 pub limit: u32,
263}
264
265#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
266#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
267#[typeshare::typeshare]
268pub struct UserOverviewResponse {
269 pub custom_domains: UserUsageCustomDomains,
270 pub projects: UserUsageProjects,
271 pub team_members: Option<UserUsageTeamMembers>,
272}
273
274#[derive(Debug, Default, Deserialize, Serialize, Clone)]
276#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
277#[typeshare::typeshare]
278pub struct UserUsageResponse {
279 pub billing_cycle: Option<UserBillingCycle>,
281
282 pub user: Option<UserOverviewResponse>,
284 pub projects: HashMap<String, ProjectUsageResponse>,
287}
288
289#[derive(Clone, Debug, Deserialize, Serialize)]
290#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
291#[typeshare::typeshare]
292pub struct AccountLimitsResponse {
293 pub projects_count: Option<u32>,
295 pub projects_limit: Option<u32>,
297 pub active_projects_count: Option<u32>,
299 pub active_projects_limit: Option<u32>,
301 pub certificate_count: Option<u32>,
303 pub certificate_limit: Option<u32>,
305}