1use crate::{
2 models::{InsertQueryBuilder, UpdateQueryBuilder},
3 prelude::*,
4};
5use garde::Validate;
6use serde::{Deserialize, Serialize};
7use sqlx::{Row, postgres::PgRow};
8use std::{
9 collections::BTreeMap,
10 sync::{Arc, LazyLock},
11};
12use utoipa::ToSchema;
13
14#[derive(Serialize, Deserialize, Clone)]
15pub struct UserServerGroup {
16 pub uuid: uuid::Uuid,
17 pub name: compact_str::CompactString,
18 pub order: i16,
19
20 pub server_order: Vec<uuid::Uuid>,
21
22 pub created: chrono::NaiveDateTime,
23
24 extension_data: super::ModelExtensionData,
25}
26
27impl BaseModel for UserServerGroup {
28 const NAME: &'static str = "user_server_group";
29
30 fn get_extension_list() -> &'static super::ModelExtensionList {
31 static EXTENSIONS: LazyLock<super::ModelExtensionList> =
32 LazyLock::new(|| std::sync::RwLock::new(Vec::new()));
33
34 &EXTENSIONS
35 }
36
37 fn get_extension_data(&self) -> &super::ModelExtensionData {
38 &self.extension_data
39 }
40
41 #[inline]
42 fn base_columns(prefix: Option<&str>) -> BTreeMap<&'static str, compact_str::CompactString> {
43 let prefix = prefix.unwrap_or_default();
44
45 BTreeMap::from([
46 (
47 "user_server_groups.uuid",
48 compact_str::format_compact!("{prefix}uuid"),
49 ),
50 (
51 "user_server_groups.name",
52 compact_str::format_compact!("{prefix}name"),
53 ),
54 (
55 "user_server_groups.order_",
56 compact_str::format_compact!("{prefix}order"),
57 ),
58 (
59 "user_server_groups.server_order",
60 compact_str::format_compact!("{prefix}server_order"),
61 ),
62 (
63 "user_server_groups.created",
64 compact_str::format_compact!("{prefix}created"),
65 ),
66 ])
67 }
68
69 #[inline]
70 fn map(prefix: Option<&str>, row: &PgRow) -> Result<Self, crate::database::DatabaseError> {
71 let prefix = prefix.unwrap_or_default();
72
73 Ok(Self {
74 uuid: row.try_get(compact_str::format_compact!("{prefix}uuid").as_str())?,
75 name: row.try_get(compact_str::format_compact!("{prefix}name").as_str())?,
76 order: row.try_get(compact_str::format_compact!("{prefix}order").as_str())?,
77 server_order: row
78 .try_get(compact_str::format_compact!("{prefix}server_order").as_str())?,
79 created: row.try_get(compact_str::format_compact!("{prefix}created").as_str())?,
80 extension_data: Self::map_extensions(prefix, row)?,
81 })
82 }
83}
84
85impl UserServerGroup {
86 pub async fn by_user_uuid_uuid(
87 database: &crate::database::Database,
88 user_uuid: uuid::Uuid,
89 uuid: uuid::Uuid,
90 ) -> Result<Option<Self>, crate::database::DatabaseError> {
91 let row = sqlx::query(&format!(
92 r#"
93 SELECT {}
94 FROM user_server_groups
95 WHERE user_server_groups.user_uuid = $1 AND user_server_groups.uuid = $2
96 "#,
97 Self::columns_sql(None)
98 ))
99 .bind(user_uuid)
100 .bind(uuid)
101 .fetch_optional(database.read())
102 .await?;
103
104 row.try_map(|row| Self::map(None, &row))
105 }
106
107 pub async fn all_by_user_uuid(
108 database: &crate::database::Database,
109 user_uuid: uuid::Uuid,
110 ) -> Result<Vec<Self>, crate::database::DatabaseError> {
111 let rows = sqlx::query(&format!(
112 r#"
113 SELECT {}
114 FROM user_server_groups
115 WHERE user_server_groups.user_uuid = $1
116 ORDER BY user_server_groups.order_, user_server_groups.created
117 "#,
118 Self::columns_sql(None)
119 ))
120 .bind(user_uuid)
121 .fetch_all(database.read())
122 .await?;
123
124 rows.into_iter()
125 .map(|row| Self::map(None, &row))
126 .try_collect_vec()
127 }
128
129 pub async fn count_by_user_uuid(
130 database: &crate::database::Database,
131 user_uuid: uuid::Uuid,
132 ) -> Result<i64, sqlx::Error> {
133 sqlx::query_scalar(
134 r#"
135 SELECT COUNT(*)
136 FROM user_server_groups
137 WHERE user_server_groups.user_uuid = $1
138 "#,
139 )
140 .bind(user_uuid)
141 .fetch_one(database.read())
142 .await
143 }
144}
145
146#[async_trait::async_trait]
147impl IntoApiObject for UserServerGroup {
148 type ApiObject = ApiUserServerGroup;
149 type ExtraArgs<'a> = ();
150
151 async fn into_api_object<'a>(
152 self,
153 state: &crate::State,
154 _args: Self::ExtraArgs<'a>,
155 ) -> Result<Self::ApiObject, crate::database::DatabaseError> {
156 let api_object = ApiUserServerGroup::init_hooks(&self, state).await?;
157
158 let api_object = finish_extendible!(
159 ApiUserServerGroup {
160 uuid: self.uuid,
161 name: self.name,
162 order: self.order,
163 server_order: self.server_order,
164 created: self.created.and_utc(),
165 },
166 api_object,
167 state
168 )?;
169
170 Ok(api_object)
171 }
172}
173
174#[derive(ToSchema, Deserialize, Validate)]
175pub struct CreateUserServerGroupOptions {
176 #[garde(skip)]
177 pub user_uuid: uuid::Uuid,
178
179 #[garde(length(chars, min = 2, max = 31))]
180 #[schema(min_length = 2, max_length = 31)]
181 pub name: compact_str::CompactString,
182
183 #[garde(length(max = 100))]
184 #[schema(max_length = 100)]
185 pub server_order: Vec<uuid::Uuid>,
186}
187
188#[async_trait::async_trait]
189impl CreatableModel for UserServerGroup {
190 type CreateOptions<'a> = CreateUserServerGroupOptions;
191 type CreateResult = Self;
192
193 fn get_create_handlers() -> &'static LazyLock<CreateListenerList<Self>> {
194 static CREATE_LISTENERS: LazyLock<CreateListenerList<UserServerGroup>> =
195 LazyLock::new(|| Arc::new(ModelHandlerList::default()));
196
197 &CREATE_LISTENERS
198 }
199
200 async fn create_with_transaction(
201 state: &crate::State,
202 mut options: Self::CreateOptions<'_>,
203 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
204 ) -> Result<Self, crate::database::DatabaseError> {
205 options.validate()?;
206
207 let mut query_builder = InsertQueryBuilder::new("user_server_groups");
208
209 Self::run_create_handlers(&mut options, &mut query_builder, state, transaction).await?;
210
211 query_builder
212 .set("user_uuid", options.user_uuid)
213 .set("name", &options.name)
214 .set("server_order", &options.server_order);
215
216 let row = query_builder
217 .returning(&Self::columns_sql(None))
218 .fetch_one(&mut **transaction)
219 .await?;
220 let mut user_server_group = Self::map(None, &row)?;
221
222 Self::run_after_create_handlers(&mut user_server_group, &options, state, transaction)
223 .await?;
224
225 Ok(user_server_group)
226 }
227}
228
229#[derive(ToSchema, Serialize, Deserialize, Validate, Default)]
230pub struct UpdateUserServerGroupOptions {
231 #[garde(length(chars, min = 2, max = 31))]
232 #[schema(min_length = 2, max_length = 31, value_type = String)]
233 pub name: Option<compact_str::CompactString>,
234
235 #[garde(length(max = 100))]
236 #[schema(max_length = 100)]
237 pub server_order: Option<Vec<uuid::Uuid>>,
238}
239
240#[async_trait::async_trait]
241impl UpdatableModel for UserServerGroup {
242 type UpdateOptions = UpdateUserServerGroupOptions;
243
244 fn get_update_handlers() -> &'static LazyLock<UpdateHandlerList<Self>> {
245 static UPDATE_LISTENERS: LazyLock<UpdateHandlerList<UserServerGroup>> =
246 LazyLock::new(|| Arc::new(ModelHandlerList::default()));
247
248 &UPDATE_LISTENERS
249 }
250
251 async fn update_with_transaction(
252 &mut self,
253 state: &crate::State,
254 mut options: Self::UpdateOptions,
255 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
256 ) -> Result<(), crate::database::DatabaseError> {
257 options.validate()?;
258
259 let mut query_builder = UpdateQueryBuilder::new("user_server_groups");
260
261 self.run_update_handlers(&mut options, &mut query_builder, state, transaction)
262 .await?;
263
264 query_builder
265 .set("name", options.name.as_ref())
266 .set("server_order", options.server_order.as_ref())
267 .where_eq("uuid", self.uuid);
268
269 query_builder.execute(&mut **transaction).await?;
270
271 if let Some(name) = options.name {
272 self.name = name;
273 }
274 if let Some(server_order) = options.server_order {
275 self.server_order = server_order;
276 }
277
278 self.run_after_update_handlers(state, transaction).await?;
279
280 Ok(())
281 }
282}
283
284#[async_trait::async_trait]
285impl DeletableModel for UserServerGroup {
286 type DeleteOptions = ();
287
288 fn get_delete_handlers() -> &'static LazyLock<DeleteHandlerList<Self>> {
289 static DELETE_LISTENERS: LazyLock<DeleteHandlerList<UserServerGroup>> =
290 LazyLock::new(|| Arc::new(ModelHandlerList::default()));
291
292 &DELETE_LISTENERS
293 }
294
295 async fn delete_with_transaction(
296 &self,
297 state: &crate::State,
298 options: Self::DeleteOptions,
299 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
300 ) -> Result<(), anyhow::Error> {
301 self.run_delete_handlers(&options, state, transaction)
302 .await?;
303
304 sqlx::query(
305 r#"
306 DELETE FROM user_server_groups
307 WHERE user_server_groups.uuid = $1
308 "#,
309 )
310 .bind(self.uuid)
311 .execute(&mut **transaction)
312 .await?;
313
314 self.run_after_delete_handlers(&options, state, transaction)
315 .await?;
316
317 Ok(())
318 }
319}
320
321#[schema_extension_derive::extendible]
322#[init_args(UserServerGroup, crate::State)]
323#[hook_args(crate::State)]
324#[derive(ToSchema, Serialize)]
325#[schema(title = "UserServerGroup")]
326pub struct ApiUserServerGroup {
327 pub uuid: uuid::Uuid,
328
329 pub name: compact_str::CompactString,
330 pub order: i16,
331
332 pub server_order: Vec<uuid::Uuid>,
333
334 pub created: chrono::DateTime<chrono::Utc>,
335}