1#[cfg(feature = "display")]
2use std::fmt::Write;
3
4use chrono::{DateTime, Utc};
5#[cfg(feature = "display")]
6use crossterm::style::Stylize;
7use serde::{Deserialize, Serialize};
8use strum::{EnumString, IntoStaticStr};
9
10#[derive(Debug, Deserialize, Serialize)]
11#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
12#[typeshare::typeshare]
13pub struct UserResponse {
14 pub id: String,
15 pub name: Option<String>,
17 pub auth0_id: Option<String>,
19 pub key: Option<String>,
21 pub account_tier: AccountTier,
22 pub subscriptions: Vec<Subscription>,
23 pub flags: Option<Vec<String>>,
24}
25
26impl UserResponse {
27 #[cfg(feature = "display")]
28 pub fn to_string_colored(&self) -> String {
29 let mut s = String::new();
30 writeln!(&mut s, "{}", "Account info:".bold()).unwrap();
31 writeln!(&mut s, " User ID: {}", self.id).unwrap();
32 writeln!(
33 &mut s,
34 " Account tier: {}",
35 self.account_tier.to_string_fancy()
36 )
37 .unwrap();
38 if !self.subscriptions.is_empty() {
39 writeln!(&mut s, " Subscriptions:").unwrap();
40 for sub in &self.subscriptions {
41 writeln!(
42 &mut s,
43 " - {}: Type: {}, Quantity: {}, Created: {}, Updated: {}",
44 sub.id, sub.r#type, sub.quantity, sub.created_at, sub.updated_at,
45 )
46 .unwrap();
47 }
48 }
49 if let Some(flags) = self.flags.as_ref() {
50 if !flags.is_empty() {
51 writeln!(&mut s, " Feature flags:").unwrap();
52 for flag in flags {
53 writeln!(&mut s, " - {}", flag).unwrap();
54 }
55 }
56 }
57
58 s
59 }
60}
61
62#[derive(
63 Clone,
65 Debug,
66 Default,
67 Eq,
68 PartialEq,
69 Ord,
70 PartialOrd,
71 Deserialize,
73 Serialize,
74 EnumString,
76 IntoStaticStr,
77 strum::Display,
78)]
79#[serde(rename_all = "lowercase")]
80#[strum(serialize_all = "lowercase")]
81#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
82#[typeshare::typeshare]
83pub enum AccountTier {
84 #[default]
85 Basic,
86 ProTrial,
88 PendingPaymentPro,
91 CancelledPro,
94 Pro,
95 Growth,
96 Employee,
98 Admin,
100
101 #[cfg(feature = "unknown-variants")]
103 #[doc(hidden)]
104 #[typeshare(skip)]
105 #[serde(untagged, skip_serializing)]
106 #[strum(default, to_string = "Unknown: {0}")]
107 Unknown(String),
108}
109impl AccountTier {
110 pub fn to_string_fancy(&self) -> String {
111 match self {
112 Self::Basic => "Community".to_owned(),
113 Self::ProTrial => "Pro Trial".to_owned(),
114 Self::PendingPaymentPro => "Community (pending payment for Pro)".to_owned(),
115 Self::CancelledPro => "Pro (subscription cancelled)".to_owned(),
116 Self::Pro => "Pro".to_owned(),
117 Self::Growth => "Growth".to_owned(),
118 Self::Employee => "Employee".to_owned(),
119 Self::Admin => "Admin".to_owned(),
120 #[cfg(feature = "unknown-variants")]
121 Self::Unknown(_) => self.to_string(),
122 }
123 }
124}
125
126#[derive(Debug, Deserialize, Serialize)]
127#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
128#[typeshare::typeshare]
129pub struct Subscription {
130 pub id: String,
131 pub r#type: SubscriptionType,
132 pub quantity: i32,
133 pub created_at: DateTime<Utc>,
134 pub updated_at: DateTime<Utc>,
135}
136
137#[derive(Debug, Deserialize)]
138#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
139#[typeshare::typeshare]
140pub struct SubscriptionRequest {
141 pub id: String,
142 pub r#type: SubscriptionType,
143 pub quantity: i32,
144}
145
146#[derive(
147 Clone,
149 Debug,
150 Eq,
151 PartialEq,
152 Deserialize,
154 Serialize,
155 EnumString,
157 strum::Display,
158 IntoStaticStr,
159)]
160#[serde(rename_all = "lowercase")]
161#[strum(serialize_all = "lowercase")]
162#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
163#[typeshare::typeshare]
164pub enum SubscriptionType {
165 Pro,
166 Rds,
167
168 #[cfg(feature = "unknown-variants")]
170 #[doc(hidden)]
171 #[typeshare(skip)]
172 #[serde(untagged, skip_serializing)]
173 #[strum(default, to_string = "Unknown: {0}")]
174 Unknown(String),
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180 #[test]
181 fn deser() {
182 assert_eq!(
183 serde_json::from_str::<AccountTier>("\"basic\"").unwrap(),
184 AccountTier::Basic
185 );
186 }
187 #[cfg(feature = "unknown-variants")]
188 #[test]
189 fn unknown_deser() {
190 assert_eq!(
191 serde_json::from_str::<AccountTier>("\"\"").unwrap(),
192 AccountTier::Unknown("".to_string())
193 );
194 assert_eq!(
195 serde_json::from_str::<AccountTier>("\"hisshiss\"").unwrap(),
196 AccountTier::Unknown("hisshiss".to_string())
197 );
198 assert!(serde_json::to_string(&AccountTier::Unknown("asdf".to_string())).is_err());
199 }
200 #[cfg(not(feature = "unknown-variants"))]
201 #[test]
202 fn not_unknown_deser() {
203 assert!(serde_json::from_str::<AccountTier>("\"\"").is_err());
204 assert!(serde_json::from_str::<AccountTier>("\"hisshiss\"").is_err());
205 }
206}
207
208#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
209#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
210#[typeshare::typeshare]
211pub struct CreateAccountRequest {
212 pub auth0_id: String,
213 pub account_tier: AccountTier,
214}
215
216#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
217#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
218#[typeshare::typeshare]
219pub struct UpdateAccountTierRequest {
220 pub account_tier: AccountTier,
221}