1use crate::{models::InsertQueryBuilder, prelude::*};
2use garde::Validate;
3use serde::{Deserialize, Serialize};
4use sqlx::{Row, postgres::PgRow};
5use std::{
6 collections::BTreeMap,
7 sync::{Arc, LazyLock},
8};
9use utoipa::ToSchema;
10
11#[derive(Serialize, Deserialize)]
12pub struct NestEggMount {
13 pub mount: Fetchable<super::mount::Mount>,
14 pub nest_egg: Fetchable<super::nest_egg::NestEgg>,
15
16 pub created: chrono::NaiveDateTime,
17
18 extension_data: super::ModelExtensionData,
19}
20
21impl BaseModel for NestEggMount {
22 const NAME: &'static str = "nest_egg_mount";
23
24 fn get_extension_list() -> &'static super::ModelExtensionList {
25 static EXTENSIONS: LazyLock<super::ModelExtensionList> =
26 LazyLock::new(|| parking_lot::RwLock::new(Vec::new()));
27
28 &EXTENSIONS
29 }
30
31 fn get_extension_data(&self) -> &super::ModelExtensionData {
32 &self.extension_data
33 }
34
35 #[inline]
36 fn base_columns(prefix: Option<&str>) -> BTreeMap<&'static str, compact_str::CompactString> {
37 let prefix = prefix.unwrap_or_default();
38
39 BTreeMap::from([
40 (
41 "nest_egg_mounts.mount_uuid",
42 compact_str::format_compact!("{prefix}mount_uuid"),
43 ),
44 (
45 "nest_egg_mounts.egg_uuid",
46 compact_str::format_compact!("{prefix}egg_uuid"),
47 ),
48 (
49 "nest_egg_mounts.created",
50 compact_str::format_compact!("{prefix}created"),
51 ),
52 ])
53 }
54
55 #[inline]
56 fn map(prefix: Option<&str>, row: &PgRow) -> Result<Self, crate::database::DatabaseError> {
57 let prefix = prefix.unwrap_or_default();
58
59 Ok(Self {
60 mount: super::mount::Mount::get_fetchable(
61 row.try_get(compact_str::format_compact!("{prefix}mount_uuid").as_str())?,
62 ),
63 nest_egg: super::nest_egg::NestEgg::get_fetchable(
64 row.try_get(compact_str::format_compact!("{prefix}egg_uuid").as_str())?,
65 ),
66 created: row.try_get(compact_str::format_compact!("{prefix}created").as_str())?,
67 extension_data: Self::map_extensions(prefix, row)?,
68 })
69 }
70}
71
72impl NestEggMount {
73 pub async fn by_egg_uuid_mount_uuid(
74 database: &crate::database::Database,
75 egg_uuid: uuid::Uuid,
76 mount_uuid: uuid::Uuid,
77 ) -> Result<Option<Self>, crate::database::DatabaseError> {
78 let row = sqlx::query(sqlx::AssertSqlSafe(format!(
79 r#"
80 SELECT {}
81 FROM nest_egg_mounts
82 WHERE nest_egg_mounts.egg_uuid = $1 AND nest_egg_mounts.mount_uuid = $2
83 "#,
84 Self::columns_sql(None)
85 )))
86 .bind(egg_uuid)
87 .bind(mount_uuid)
88 .fetch_optional(database.read())
89 .await?;
90
91 row.try_map(|row| Self::map(None, &row))
92 }
93
94 pub async fn by_egg_uuid_with_pagination(
95 database: &crate::database::Database,
96 egg_uuid: uuid::Uuid,
97 page: i64,
98 per_page: i64,
99 search: Option<&str>,
100 ) -> Result<super::Pagination<Self>, crate::database::DatabaseError> {
101 super::PaginatedQueryBuilder::new(
102 Self::columns_sql(None),
103 format!(
104 r#"
105 FROM nest_egg_mounts
106 JOIN mounts ON mounts.uuid = nest_egg_mounts.mount_uuid
107 WHERE nest_egg_mounts.egg_uuid = $1 AND {search}
108 "#,
109 search = super::search_sql(2, &["mounts.name"], &["mounts.uuid"]),
110 ),
111 "nest_egg_mounts.created, nest_egg_mounts.egg_uuid, nest_egg_mounts.mount_uuid",
112 )
113 .bind(egg_uuid)
114 .bind(search)
115 .fetch(database, page, per_page)
116 .await
117 }
118
119 pub async fn by_mount_uuid_with_pagination(
120 database: &crate::database::Database,
121 mount_uuid: uuid::Uuid,
122 page: i64,
123 per_page: i64,
124 search: Option<&str>,
125 ) -> Result<super::Pagination<Self>, crate::database::DatabaseError> {
126 super::PaginatedQueryBuilder::new(
127 Self::columns_sql(None),
128 format!(
129 r#"
130 FROM nest_egg_mounts
131 JOIN nest_eggs ON nest_eggs.uuid = nest_egg_mounts.egg_uuid
132 WHERE nest_egg_mounts.mount_uuid = $1 AND {search}
133 "#,
134 search = super::search_sql(2, &["nest_eggs.name"], &["nest_eggs.uuid"]),
135 ),
136 "nest_egg_mounts.created, nest_egg_mounts.egg_uuid, nest_egg_mounts.mount_uuid",
137 )
138 .bind(mount_uuid)
139 .bind(search)
140 .fetch(database, page, per_page)
141 .await
142 }
143
144 pub async fn into_admin_nest_egg_api_object(
145 self,
146 state: &crate::State,
147 _args: (),
148 ) -> Result<AdminApiNestEggNestEggMount, crate::database::DatabaseError> {
149 let nest_egg = self.nest_egg.fetch_cached(&state.database).await?;
150 let nest = nest_egg.nest.clone();
151 let (nest, nest_egg) = tokio::try_join!(nest.fetch_cached(&state.database), async {
152 Ok(nest_egg.into_admin_api_object(state, ()).await?)
153 })?;
154
155 Ok(AdminApiNestEggNestEggMount {
156 nest: nest.into_admin_api_object(state, ()).await?,
157 nest_egg,
158 created: self.created.and_utc(),
159 })
160 }
161}
162
163#[async_trait::async_trait]
164impl IntoAdminApiObject for NestEggMount {
165 type AdminApiObject = AdminApiNestEggMount;
166 type ExtraArgs<'a> = ();
167
168 async fn into_admin_api_object<'a>(
169 self,
170 state: &crate::State,
171 _args: Self::ExtraArgs<'a>,
172 ) -> Result<Self::AdminApiObject, crate::database::DatabaseError> {
173 let api_object = AdminApiNestEggMount::init_hooks(&self, state).await?;
174
175 let api_object = finish_extendible!(
176 AdminApiNestEggMount {
177 mount: self
178 .mount
179 .fetch_cached(&state.database)
180 .await?
181 .into_admin_api_object(state, ())
182 .await?,
183 created: self.created.and_utc(),
184 },
185 api_object,
186 state
187 )?;
188
189 Ok(api_object)
190 }
191}
192
193#[derive(ToSchema, Deserialize, Validate)]
194pub struct CreateNestEggMountOptions {
195 #[garde(skip)]
196 pub egg_uuid: uuid::Uuid,
197 #[garde(skip)]
198 pub mount_uuid: uuid::Uuid,
199}
200
201#[async_trait::async_trait]
202impl CreatableModel for NestEggMount {
203 type CreateOptions<'a> = CreateNestEggMountOptions;
204 type CreateResult = Self;
205
206 fn get_create_handlers() -> &'static LazyLock<CreateListenerList<Self>> {
207 static CREATE_LISTENERS: LazyLock<CreateListenerList<NestEggMount>> =
208 LazyLock::new(|| Arc::new(ModelHandlerList::default()));
209
210 &CREATE_LISTENERS
211 }
212
213 async fn create_with_transaction(
214 state: &crate::State,
215 mut options: Self::CreateOptions<'_>,
216 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
217 ) -> Result<Self, crate::database::DatabaseError> {
218 options.validate()?;
219
220 super::mount::Mount::by_uuid_optional_cached(&state.database, options.mount_uuid)
221 .await?
222 .ok_or(crate::database::InvalidRelationError("mount"))?;
223
224 let mut query_builder = InsertQueryBuilder::new("nest_egg_mounts");
225
226 Self::run_create_handlers(&mut options, &mut query_builder, state, transaction).await?;
227
228 query_builder
229 .set("egg_uuid", options.egg_uuid)
230 .set("mount_uuid", options.mount_uuid);
231
232 let row = query_builder
233 .returning(&Self::columns_sql(None))
234 .fetch_one(&mut **transaction)
235 .await?;
236 let mut nest_egg_mount = Self::map(None, &row)?;
237
238 Self::run_after_create_handlers(&mut nest_egg_mount, &options, state, transaction).await?;
239
240 Ok(nest_egg_mount)
241 }
242}
243
244#[async_trait::async_trait]
245impl DeletableModel for NestEggMount {
246 type DeleteOptions = ();
247
248 fn get_delete_handlers() -> &'static LazyLock<DeleteHandlerList<Self>> {
249 static DELETE_LISTENERS: LazyLock<DeleteHandlerList<NestEggMount>> =
250 LazyLock::new(|| Arc::new(ModelHandlerList::default()));
251
252 &DELETE_LISTENERS
253 }
254
255 async fn delete_with_transaction(
256 &self,
257 state: &crate::State,
258 options: Self::DeleteOptions,
259 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
260 ) -> Result<(), anyhow::Error> {
261 self.run_delete_handlers(&options, state, transaction)
262 .await?;
263
264 sqlx::query(
265 r#"
266 DELETE FROM nest_egg_mounts
267 WHERE nest_egg_mounts.egg_uuid = $1 AND nest_egg_mounts.mount_uuid = $2
268 "#,
269 )
270 .bind(self.nest_egg.uuid)
271 .bind(self.mount.uuid)
272 .execute(&mut **transaction)
273 .await?;
274
275 self.run_after_delete_handlers(&options, state, transaction)
276 .await?;
277
278 Ok(())
279 }
280}
281
282#[derive(ToSchema, Serialize)]
283#[schema(title = "AdminNestEggNestEggMount")]
284pub struct AdminApiNestEggNestEggMount {
285 pub nest: super::nest::AdminApiNest,
286 pub nest_egg: super::nest_egg::AdminApiNestEgg,
287
288 pub created: chrono::DateTime<chrono::Utc>,
289}
290
291#[schema_extension_derive::extendible]
292#[init_args(NestEggMount, crate::State)]
293#[hook_args(crate::State)]
294#[derive(ToSchema, Serialize)]
295#[schema(title = "AdminNestEggMount")]
296pub struct AdminApiNestEggMount {
297 pub mount: super::mount::AdminApiMount,
298
299 pub created: chrono::DateTime<chrono::Utc>,
300}