Skip to main content

shared/models/
user_server_group.rs

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}