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#[derive(Debug, Deserialize, Serialize)]
131#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
132#[typeshare::typeshare]
133pub struct Subscription {
134 pub id: String,
135 pub r#type: SubscriptionType,
136 pub quantity: i32,
137 pub created_at: DateTime<Utc>,
138 pub updated_at: DateTime<Utc>,
139}
140
141#[derive(Debug, Deserialize)]
142#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
143#[typeshare::typeshare]
144pub struct SubscriptionRequest {
145 pub id: String,
146 pub r#type: SubscriptionType,
147 pub quantity: i32,
148}
149
150#[derive(
151 Clone,
153 Debug,
154 Eq,
155 PartialEq,
156 Deserialize,
158 Serialize,
159 EnumString,
161 strum::Display,
162 IntoStaticStr,
163)]
164#[serde(rename_all = "lowercase")]
165#[strum(serialize_all = "lowercase")]
166#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
167#[typeshare::typeshare]
168pub enum SubscriptionType {
169 Pro,
170 Rds,
171
172 #[cfg(feature = "unknown-variants")]
174 #[doc(hidden)]
175 #[typeshare(skip)]
176 #[serde(untagged, skip_serializing)]
177 #[strum(default, to_string = "Unknown: {0}")]
178 Unknown(String),
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 #[test]
185 fn deser() {
186 assert_eq!(
187 serde_json::from_str::<AccountTier>("\"basic\"").unwrap(),
188 AccountTier::Basic
189 );
190 }
191 #[cfg(feature = "unknown-variants")]
192 #[test]
193 fn unknown_deser() {
194 assert_eq!(
195 serde_json::from_str::<AccountTier>("\"\"").unwrap(),
196 AccountTier::Unknown("".to_string())
197 );
198 assert_eq!(
199 serde_json::from_str::<AccountTier>("\"hisshiss\"").unwrap(),
200 AccountTier::Unknown("hisshiss".to_string())
201 );
202 assert!(serde_json::to_string(&AccountTier::Unknown("asdf".to_string())).is_err());
203 }
204 #[cfg(not(feature = "unknown-variants"))]
205 #[test]
206 fn not_unknown_deser() {
207 assert!(serde_json::from_str::<AccountTier>("\"\"").is_err());
208 assert!(serde_json::from_str::<AccountTier>("\"hisshiss\"").is_err());
209 }
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}