1use crate::database::DatabaseError;
2use compact_str::CompactStringExt;
3use futures_util::{StreamExt, TryStreamExt};
4use garde::Validate;
5use serde::{Deserialize, Serialize, de::DeserializeOwned};
6use sqlx::{
7 Arguments, Postgres, QueryBuilder, Row,
8 encode::IsNull,
9 error::BoxDynError,
10 postgres::{PgArgumentBuffer, PgArguments, PgRow, PgTypeInfo},
11};
12use std::{
13 collections::{BTreeMap, HashSet},
14 marker::PhantomData,
15 pin::Pin,
16 sync::{Arc, LazyLock},
17};
18use tokio::sync::RwLock;
19use utoipa::ToSchema;
20
21pub mod admin_activity;
22pub mod announcement;
23pub mod backup_configuration;
24pub mod database_host;
25pub mod egg_configuration;
26pub mod egg_repository;
27pub mod egg_repository_egg;
28pub mod location;
29pub mod location_database_host;
30pub mod mount;
31pub mod nest;
32pub mod nest_egg;
33pub mod nest_egg_mount;
34pub mod nest_egg_variable;
35pub mod node;
36pub mod node_allocation;
37pub mod node_mount;
38pub mod oauth_provider;
39pub mod oauth_provider_mapping;
40pub mod role;
41pub mod server;
42pub mod server_activity;
43pub mod server_allocation;
44pub mod server_backup;
45pub mod server_database;
46pub mod server_mount;
47pub mod server_schedule;
48pub mod server_schedule_step;
49pub mod server_subuser;
50pub mod server_variable;
51pub mod user;
52pub mod user_activity;
53pub mod user_api_key;
54pub mod user_command_snippet;
55pub mod user_oauth_link;
56pub mod user_password_reset;
57pub mod user_recovery_code;
58pub mod user_security_key;
59pub mod user_server_group;
60pub mod user_session;
61pub mod user_ssh_key;
62
63#[derive(ToSchema, Validate, Deserialize, Serialize)]
64pub struct PaginationParams {
65 #[garde(range(min = 1))]
66 #[schema(minimum = 1)]
67 #[serde(default = "Pagination::default_page")]
68 pub page: i64,
69 #[garde(range(min = 1, max = 100))]
70 #[schema(minimum = 1, maximum = 100)]
71 #[serde(default = "Pagination::default_per_page")]
72 pub per_page: i64,
73}
74
75#[derive(ToSchema, Validate, Deserialize, Serialize)]
76pub struct PaginationParamsWithSearch {
77 #[garde(range(min = 1))]
78 #[schema(minimum = 1)]
79 #[serde(default = "Pagination::default_page")]
80 pub page: i64,
81 #[garde(range(min = 1, max = 100))]
82 #[schema(minimum = 1, maximum = 100)]
83 #[serde(default = "Pagination::default_per_page")]
84 pub per_page: i64,
85 #[garde(length(chars, min = 1, max = 128))]
86 #[schema(min_length = 1, max_length = 128)]
87 #[serde(
88 default,
89 deserialize_with = "crate::deserialize::deserialize_string_option"
90 )]
91 pub search: Option<compact_str::CompactString>,
92}
93
94#[derive(ToSchema, Deserialize, Serialize)]
95pub struct Pagination<T: Serialize = serde_json::Value> {
96 pub total: i64,
97 pub per_page: i64,
98 pub page: i64,
99
100 pub data: Vec<T>,
101}
102
103impl Pagination {
104 #[inline]
105 pub const fn default_page() -> i64 {
106 1
107 }
108
109 #[inline]
110 pub const fn default_per_page() -> i64 {
111 25
112 }
113}
114
115impl<T: Serialize> Pagination<T> {
116 pub async fn async_map<R: serde::Serialize, Fut: Future<Output = R>>(
117 self,
118 mapper: impl Fn(T) -> Fut,
119 ) -> Pagination<R> {
120 let mut results = Vec::new();
121 results.reserve_exact(self.data.len());
122 let mut result_stream =
123 futures_util::stream::iter(self.data.into_iter().map(mapper)).buffered(25);
124
125 while let Some(result) = result_stream.next().await {
126 results.push(result);
127 }
128
129 Pagination {
130 total: self.total,
131 per_page: self.per_page,
132 page: self.page,
133 data: results,
134 }
135 }
136
137 pub async fn try_async_map<R: serde::Serialize, E, Fut: Future<Output = Result<R, E>>>(
138 self,
139 mapper: impl Fn(T) -> Fut,
140 ) -> Result<Pagination<R>, E> {
141 let mut results = Vec::new();
142 results.reserve_exact(self.data.len());
143 let mut result_stream =
144 futures_util::stream::iter(self.data.into_iter().map(mapper)).buffered(25);
145
146 while let Some(result) = result_stream.try_next().await? {
147 results.push(result);
148 }
149
150 Ok(Pagination {
151 total: self.total,
152 per_page: self.per_page,
153 page: self.page,
154 data: results,
155 })
156 }
157}
158
159pub type ModelExtensionList = parking_lot::RwLock<Vec<Box<dyn ModelExtension + Send + Sync>>>;
160pub type ModelExtensionData = Vec<(compact_str::CompactString, Vec<u8>)>;
161pub type ModelExtensionMapType = Box<dyn erased_serde::Serialize>;
162
163pub trait ModelExtension {
164 fn extension_name(&self) -> &'static str;
165
166 fn extended_columns(&self, prefix: &str) -> BTreeMap<&'static str, compact_str::CompactString>;
167
168 fn map_extended(
169 &self,
170 prefix: &str,
171 row: &PgRow,
172 ) -> Result<ModelExtensionMapType, crate::database::DatabaseError>;
173}
174
175pub trait SafeModelExtension: ModelExtension {
176 type Value: Serialize + DeserializeOwned;
177
178 fn name() -> &'static str;
179}
180
181pub trait BaseModel: Serialize + DeserializeOwned {
182 const NAME: &'static str;
183
184 fn get_extension_list() -> &'static ModelExtensionList;
185 fn get_extension_data(&self) -> &ModelExtensionData;
186
187 fn register_model_extension(extension: impl ModelExtension + Send + Sync + 'static) {
189 let mut extensions = Self::get_extension_list().write();
190
191 if extensions
192 .iter()
193 .any(|e| e.extension_name() == extension.extension_name())
194 {
195 return;
196 }
197
198 extensions.push(Box::new(extension));
199 }
200
201 fn parse_model_extension<Extension: SafeModelExtension>(
205 &self,
206 ) -> Result<Extension::Value, crate::database::DatabaseError>
207 where
208 Extension::Value: Serialize + DeserializeOwned,
209 {
210 let data = self.get_extension_data();
211
212 for (name, value) in data.iter() {
213 if name.as_str() == Extension::name() {
214 let deserialized =
215 rmp_serde::from_slice::<Extension::Value>(value).map_err(anyhow::Error::new)?;
216
217 return Ok(deserialized);
218 }
219 }
220
221 Err(crate::database::DatabaseError::Any(anyhow::anyhow!(
222 "model extension not found"
223 )))
224 }
225
226 fn base_columns(prefix: Option<&str>) -> BTreeMap<&'static str, compact_str::CompactString>;
227 fn columns(prefix: Option<&str>) -> BTreeMap<&'static str, compact_str::CompactString> {
228 let extensions = Self::get_extension_list().read();
229
230 let mut columns = Self::base_columns(prefix);
231
232 for extension in extensions.iter() {
233 columns.extend(extension.extended_columns(prefix.unwrap_or_default()));
234 }
235
236 columns
237 }
238
239 #[inline]
240 fn columns_sql(prefix: Option<&str>) -> compact_str::CompactString {
241 Self::columns(prefix)
242 .iter()
243 .map(|(key, value)| compact_str::format_compact!("{key} as {value}"))
244 .join_compact(", ")
245 }
246
247 fn map_extensions(
248 prefix: &str,
249 row: &PgRow,
250 ) -> Result<ModelExtensionData, crate::database::DatabaseError> {
251 let mut data = Vec::new();
252
253 let extensions = Self::get_extension_list().read();
254 for extension in extensions.iter() {
255 let value = extension.map_extended(prefix, row)?;
256 let serialized = rmp_serde::to_vec(&value).map_err(anyhow::Error::new)?;
257
258 data.push((
259 compact_str::CompactString::const_new(extension.extension_name()),
260 serialized,
261 ));
262 }
263
264 Ok(data)
265 }
266
267 fn map(prefix: Option<&str>, row: &PgRow) -> Result<Self, crate::database::DatabaseError>;
268}
269
270#[async_trait::async_trait]
271pub trait EventEmittingModel: BaseModel {
272 type Event: Send + Sync + 'static;
273
274 fn get_event_emitter() -> &'static crate::events::EventEmitter<Self::Event>;
275
276 async fn register_event_handler<
277 F: Fn(crate::State, Arc<Self::Event>) -> Fut + Send + Sync + 'static,
278 Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
279 >(
280 listener: F,
281 ) -> crate::events::EventHandlerHandle {
282 Self::get_event_emitter()
283 .register_event_handler(listener)
284 .await
285 }
286
287 fn blocking_register_event_handler<
290 F: Fn(crate::State, Arc<Self::Event>) -> Fut + Send + Sync + 'static,
291 Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
292 >(
293 listener: F,
294 ) -> crate::events::EventHandlerHandle {
295 Self::get_event_emitter().blocking_register_event_handler(listener)
296 }
297}
298
299type CreateHandlerResult<'a> =
300 Pin<Box<dyn Future<Output = Result<(), crate::database::DatabaseError>> + Send + 'a>>;
301type CreateHandler<M> = dyn for<'a> Fn(
302 &'a mut <M as CreatableModel>::CreateOptions<'_>,
303 &'a mut InsertQueryBuilder,
304 &'a crate::State,
305 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
306 ) -> CreateHandlerResult<'a>
307 + Send
308 + Sync;
309type CreateAfterHandler<M> = dyn for<'a> Fn(
310 &'a mut <M as CreatableModel>::CreateResult,
311 &'a <M as CreatableModel>::CreateOptions<'_>,
312 &'a crate::State,
313 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
314 ) -> CreateHandlerResult<'a>
315 + Send
316 + Sync;
317pub type CreateListenerList<M> =
318 Arc<ModelHandlerList<Box<CreateHandler<M>>, Box<CreateAfterHandler<M>>>>;
319
320#[async_trait::async_trait]
321pub trait CreatableModel: BaseModel + Send + Sync + 'static {
322 type CreateOptions<'a>: Send + Sync + Validate;
323 type CreateResult: Send;
324
325 fn get_create_handlers() -> &'static LazyLock<CreateListenerList<Self>>;
326
327 async fn register_create_handler<
328 F: for<'a> Fn(
329 &'a mut Self::CreateOptions<'_>,
330 &'a mut InsertQueryBuilder,
331 &'a crate::State,
332 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
333 ) -> Pin<
334 Box<dyn Future<Output = Result<(), crate::database::DatabaseError>> + Send + 'a>,
335 > + Send
336 + Sync
337 + 'static,
338 >(
339 priority: ListenerPriority,
340 callback: F,
341 ) {
342 let erased = Box::new(callback) as Box<CreateHandler<Self>>;
343
344 Self::get_create_handlers()
345 .register_handler(priority, erased)
346 .await;
347 }
348
349 async fn register_after_create_handler<
350 F: for<'a> Fn(
351 &'a mut Self::CreateResult,
352 &'a Self::CreateOptions<'_>,
353 &'a crate::State,
354 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
355 ) -> Pin<
356 Box<dyn Future<Output = Result<(), crate::database::DatabaseError>> + Send + 'a>,
357 > + Send
358 + Sync
359 + 'static,
360 >(
361 priority: ListenerPriority,
362 callback: F,
363 ) {
364 let erased = Box::new(callback) as Box<CreateAfterHandler<Self>>;
365
366 Self::get_create_handlers()
367 .register_after_handler(priority, erased)
368 .await;
369 }
370
371 fn blocking_register_create_handler<
374 F: for<'a> Fn(
375 &'a mut Self::CreateOptions<'_>,
376 &'a mut InsertQueryBuilder,
377 &'a crate::State,
378 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
379 ) -> Pin<
380 Box<dyn Future<Output = Result<(), crate::database::DatabaseError>> + Send + 'a>,
381 > + Send
382 + Sync
383 + 'static,
384 >(
385 priority: ListenerPriority,
386 callback: F,
387 ) {
388 let erased = Box::new(callback) as Box<CreateHandler<Self>>;
389
390 Self::get_create_handlers().blocking_register_handler(priority, erased);
391 }
392
393 fn blocking_register_after_create_handler<
396 F: for<'a> Fn(
397 &'a mut Self::CreateResult,
398 &'a Self::CreateOptions<'_>,
399 &'a crate::State,
400 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
401 ) -> Pin<
402 Box<dyn Future<Output = Result<(), crate::database::DatabaseError>> + Send + 'a>,
403 > + Send
404 + Sync
405 + 'static,
406 >(
407 priority: ListenerPriority,
408 callback: F,
409 ) {
410 let erased = Box::new(callback) as Box<CreateAfterHandler<Self>>;
411
412 Self::get_create_handlers().blocking_register_after_handler(priority, erased);
413 }
414
415 async fn run_create_handlers(
416 options: &mut Self::CreateOptions<'_>,
417 query_builder: &mut InsertQueryBuilder,
418 state: &crate::State,
419 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
420 ) -> Result<(), crate::database::DatabaseError> {
421 let listeners = Self::get_create_handlers().before_handlers.read().await;
422
423 for listener in listeners.iter() {
424 (*listener.callback)(options, query_builder, state, transaction).await?;
425 }
426
427 Ok(())
428 }
429
430 async fn run_after_create_handlers(
431 result: &mut Self::CreateResult,
432 options: &Self::CreateOptions<'_>,
433 state: &crate::State,
434 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
435 ) -> Result<(), crate::database::DatabaseError> {
436 let listeners = Self::get_create_handlers().after_handlers.read().await;
437
438 for listener in listeners.iter() {
439 (*listener.callback)(result, options, state, transaction).await?;
440 }
441
442 Ok(())
443 }
444
445 async fn create_with_transaction(
446 state: &crate::State,
447 options: Self::CreateOptions<'_>,
448 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
449 ) -> Result<Self::CreateResult, crate::database::DatabaseError>;
450
451 async fn create(
452 state: &crate::State,
453 options: Self::CreateOptions<'_>,
454 ) -> Result<Self::CreateResult, crate::database::DatabaseError> {
455 let mut transaction = state.database.write().begin().await?;
456
457 let result = match Self::create_with_transaction(state, options, &mut transaction).await {
458 Ok(result) => result,
459 Err(err) => {
460 transaction.rollback().await?;
461 return Err(err);
462 }
463 };
464
465 transaction.commit().await?;
466
467 Ok(result)
468 }
469}
470
471type UpdateHandlerResult<'a> =
472 Pin<Box<dyn Future<Output = Result<(), crate::database::DatabaseError>> + Send + 'a>>;
473type UpdateHandler<M> = dyn for<'a> Fn(
474 &'a mut M,
475 &'a mut <M as UpdatableModel>::UpdateOptions,
476 &'a mut UpdateQueryBuilder,
477 &'a crate::State,
478 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
479 ) -> UpdateHandlerResult<'a>
480 + Send
481 + Sync;
482type UpdateAfterHandler<M> = dyn for<'a> Fn(
483 &'a mut M,
484 &'a crate::State,
485 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
486 ) -> UpdateHandlerResult<'a>
487 + Send
488 + Sync;
489pub type UpdateHandlerList<M> =
490 Arc<ModelHandlerList<Box<UpdateHandler<M>>, Box<UpdateAfterHandler<M>>>>;
491
492#[async_trait::async_trait]
493pub trait UpdatableModel: BaseModel + Send + Sync + 'static {
494 type UpdateOptions: Send + Sync + Default + ToSchema + DeserializeOwned + Serialize + Validate;
495
496 fn get_update_handlers() -> &'static LazyLock<UpdateHandlerList<Self>>;
497
498 async fn register_update_handler<
499 F: for<'a> Fn(
500 &'a mut Self,
501 &'a mut Self::UpdateOptions,
502 &'a mut UpdateQueryBuilder,
503 &'a crate::State,
504 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
505 ) -> Pin<
506 Box<dyn Future<Output = Result<(), crate::database::DatabaseError>> + Send + 'a>,
507 > + Send
508 + Sync
509 + 'static,
510 >(
511 priority: ListenerPriority,
512 callback: F,
513 ) {
514 let erased = Box::new(callback) as Box<UpdateHandler<Self>>;
515
516 Self::get_update_handlers()
517 .register_handler(priority, erased)
518 .await;
519 }
520
521 async fn register_after_update_handler<
522 F: for<'a> Fn(
523 &'a mut Self,
524 &'a crate::State,
525 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
526 ) -> Pin<
527 Box<dyn Future<Output = Result<(), crate::database::DatabaseError>> + Send + 'a>,
528 > + Send
529 + Sync
530 + 'static,
531 >(
532 priority: ListenerPriority,
533 callback: F,
534 ) {
535 let erased = Box::new(callback) as Box<UpdateAfterHandler<Self>>;
536
537 Self::get_update_handlers()
538 .register_after_handler(priority, erased)
539 .await;
540 }
541
542 fn blocking_register_update_handler<
545 F: for<'a> Fn(
546 &'a mut Self,
547 &'a mut Self::UpdateOptions,
548 &'a mut UpdateQueryBuilder,
549 &'a crate::State,
550 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
551 ) -> Pin<
552 Box<dyn Future<Output = Result<(), crate::database::DatabaseError>> + Send + 'a>,
553 > + Send
554 + Sync
555 + 'static,
556 >(
557 priority: ListenerPriority,
558 callback: F,
559 ) {
560 let erased = Box::new(callback) as Box<UpdateHandler<Self>>;
561
562 Self::get_update_handlers().blocking_register_handler(priority, erased);
563 }
564
565 fn blocking_register_after_update_handler<
568 F: for<'a> Fn(
569 &'a mut Self,
570 &'a crate::State,
571 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
572 ) -> Pin<
573 Box<dyn Future<Output = Result<(), crate::database::DatabaseError>> + Send + 'a>,
574 > + Send
575 + Sync
576 + 'static,
577 >(
578 priority: ListenerPriority,
579 callback: F,
580 ) {
581 let erased = Box::new(callback) as Box<UpdateAfterHandler<Self>>;
582
583 Self::get_update_handlers().blocking_register_after_handler(priority, erased);
584 }
585
586 async fn run_update_handlers(
587 &mut self,
588 options: &mut Self::UpdateOptions,
589 query_builder: &mut UpdateQueryBuilder,
590 state: &crate::State,
591 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
592 ) -> Result<(), crate::database::DatabaseError> {
593 let listeners = Self::get_update_handlers().before_handlers.read().await;
594
595 for listener in listeners.iter() {
596 (*listener.callback)(self, options, query_builder, state, transaction).await?;
597 }
598
599 Ok(())
600 }
601
602 async fn run_after_update_handlers(
603 &mut self,
604 state: &crate::State,
605 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
606 ) -> Result<(), crate::database::DatabaseError> {
607 let listeners = Self::get_update_handlers().after_handlers.read().await;
608
609 for listener in listeners.iter() {
610 (*listener.callback)(self, state, transaction).await?;
611 }
612
613 Ok(())
614 }
615
616 async fn update_with_transaction(
617 &mut self,
618 state: &crate::State,
619 options: Self::UpdateOptions,
620 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
621 ) -> Result<(), crate::database::DatabaseError>;
622
623 async fn update(
624 &mut self,
625 state: &crate::State,
626 options: Self::UpdateOptions,
627 ) -> Result<(), crate::database::DatabaseError> {
628 let mut transaction = state.database.write().begin().await?;
629
630 if let Err(err) = self
631 .update_with_transaction(state, options, &mut transaction)
632 .await
633 {
634 transaction.rollback().await?;
635 return Err(err);
636 }
637
638 transaction.commit().await?;
639
640 Ok(())
641 }
642}
643
644type DeleteHandlerResult<'a> = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>> + Send + 'a>>;
645type DeleteHandler<M> = dyn for<'a> Fn(
646 &'a M,
647 &'a <M as DeletableModel>::DeleteOptions,
648 &'a crate::State,
649 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
650 ) -> DeleteHandlerResult<'a>
651 + Send
652 + Sync;
653type DeleteAfterHandler<M> = dyn for<'a> Fn(
654 &'a M,
655 &'a <M as DeletableModel>::DeleteOptions,
656 &'a crate::State,
657 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
658 ) -> DeleteHandlerResult<'a>
659 + Send
660 + Sync;
661pub type DeleteHandlerList<M> =
662 Arc<ModelHandlerList<Box<DeleteHandler<M>>, Box<DeleteAfterHandler<M>>>>;
663
664#[async_trait::async_trait]
665pub trait DeletableModel: BaseModel + Send + Sync + 'static {
666 type DeleteOptions: Send + Sync + Default + Clone;
667
668 fn get_delete_handlers() -> &'static LazyLock<DeleteHandlerList<Self>>;
669
670 async fn register_delete_handler<
671 F: for<'a> Fn(
672 &'a Self,
673 &'a Self::DeleteOptions,
674 &'a crate::State,
675 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
676 )
677 -> Pin<Box<dyn Future<Output = Result<(), anyhow::Error>> + Send + 'a>>
678 + Send
679 + Sync
680 + 'static,
681 >(
682 priority: ListenerPriority,
683 callback: F,
684 ) {
685 let erased = Box::new(callback) as Box<DeleteHandler<Self>>;
686
687 Self::get_delete_handlers()
688 .register_handler(priority, erased)
689 .await;
690 }
691
692 async fn register_after_delete_handler<
693 F: for<'a> Fn(
694 &'a Self,
695 &'a Self::DeleteOptions,
696 &'a crate::State,
697 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
698 )
699 -> Pin<Box<dyn Future<Output = Result<(), anyhow::Error>> + Send + 'a>>
700 + Send
701 + Sync
702 + 'static,
703 >(
704 priority: ListenerPriority,
705 callback: F,
706 ) {
707 let erased = Box::new(callback) as Box<DeleteAfterHandler<Self>>;
708
709 Self::get_delete_handlers()
710 .register_after_handler(priority, erased)
711 .await;
712 }
713
714 fn blocking_register_delete_handler<
717 F: for<'a> Fn(
718 &'a Self,
719 &'a Self::DeleteOptions,
720 &'a crate::State,
721 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
722 )
723 -> Pin<Box<dyn Future<Output = Result<(), anyhow::Error>> + Send + 'a>>
724 + Send
725 + Sync
726 + 'static,
727 >(
728 priority: ListenerPriority,
729 callback: F,
730 ) {
731 let erased = Box::new(callback) as Box<DeleteHandler<Self>>;
732
733 Self::get_delete_handlers().blocking_register_handler(priority, erased);
734 }
735
736 fn blocking_register_after_delete_handler<
739 F: for<'a> Fn(
740 &'a Self,
741 &'a Self::DeleteOptions,
742 &'a crate::State,
743 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
744 )
745 -> Pin<Box<dyn Future<Output = Result<(), anyhow::Error>> + Send + 'a>>
746 + Send
747 + Sync
748 + 'static,
749 >(
750 priority: ListenerPriority,
751 callback: F,
752 ) {
753 let erased = Box::new(callback) as Box<DeleteAfterHandler<Self>>;
754
755 Self::get_delete_handlers().blocking_register_after_handler(priority, erased);
756 }
757
758 async fn run_delete_handlers(
759 &self,
760 options: &Self::DeleteOptions,
761 state: &crate::State,
762 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
763 ) -> Result<(), anyhow::Error> {
764 let listeners = Self::get_delete_handlers().before_handlers.read().await;
765
766 for listener in listeners.iter() {
767 (*listener.callback)(self, options, state, transaction).await?;
768 }
769
770 Ok(())
771 }
772
773 async fn run_after_delete_handlers(
774 &self,
775 options: &Self::DeleteOptions,
776 state: &crate::State,
777 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
778 ) -> Result<(), anyhow::Error> {
779 let listeners = Self::get_delete_handlers().after_handlers.read().await;
780
781 for listener in listeners.iter() {
782 (*listener.callback)(self, options, state, transaction).await?;
783 }
784
785 Ok(())
786 }
787
788 async fn delete_with_transaction(
789 &self,
790 state: &crate::State,
791 options: Self::DeleteOptions,
792 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
793 ) -> Result<(), anyhow::Error>;
794
795 async fn delete(
796 &self,
797 state: &crate::State,
798 options: Self::DeleteOptions,
799 ) -> Result<(), anyhow::Error> {
800 let mut transaction = state.database.write().begin().await?;
801
802 if let Err(err) = self
803 .delete_with_transaction(state, options, &mut transaction)
804 .await
805 {
806 transaction.rollback().await?;
807 return Err(err);
808 }
809
810 transaction.commit().await?;
811
812 Ok(())
813 }
814}
815
816type DuplicateHandlerResult<'a> =
817 Pin<Box<dyn Future<Output = Result<(), crate::database::DatabaseError>> + Send + 'a>>;
818type DuplicateHandler<M> = dyn for<'a> Fn(
819 &'a M,
820 &'a <M as DuplicableModel>::DuplicateOptions<'_>,
821 &'a crate::State,
822 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
823 ) -> DuplicateHandlerResult<'a>
824 + Send
825 + Sync;
826type DuplicateAfterHandler<M> = dyn for<'a> Fn(
827 &'a M,
828 &'a mut M,
829 &'a <M as DuplicableModel>::DuplicateOptions<'_>,
830 &'a crate::State,
831 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
832 ) -> DuplicateHandlerResult<'a>
833 + Send
834 + Sync;
835pub type DuplicateHandlerList<M> =
836 Arc<ModelHandlerList<Box<DuplicateHandler<M>>, Box<DuplicateAfterHandler<M>>>>;
837
838#[async_trait::async_trait]
839pub trait DuplicableModel: BaseModel + Send + Sync + 'static {
840 type DuplicateOptions<'a>: Send + Sync + Validate;
841
842 fn get_duplicate_handlers() -> &'static LazyLock<DuplicateHandlerList<Self>>;
843
844 async fn register_duplicate_handler<
845 F: for<'a> Fn(
846 &'a Self,
847 &'a Self::DuplicateOptions<'_>,
848 &'a crate::State,
849 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
850 ) -> DuplicateHandlerResult<'a>
851 + Send
852 + Sync
853 + 'static,
854 >(
855 priority: ListenerPriority,
856 callback: F,
857 ) {
858 let erased = Box::new(callback) as Box<DuplicateHandler<Self>>;
859
860 Self::get_duplicate_handlers()
861 .register_handler(priority, erased)
862 .await;
863 }
864
865 async fn register_after_duplicate_handler<
866 F: for<'a> Fn(
867 &'a Self,
868 &'a mut Self,
869 &'a Self::DuplicateOptions<'_>,
870 &'a crate::State,
871 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
872 ) -> DuplicateHandlerResult<'a>
873 + Send
874 + Sync
875 + 'static,
876 >(
877 priority: ListenerPriority,
878 callback: F,
879 ) {
880 let erased = Box::new(callback) as Box<DuplicateAfterHandler<Self>>;
881
882 Self::get_duplicate_handlers()
883 .register_after_handler(priority, erased)
884 .await;
885 }
886
887 fn blocking_register_duplicate_handler<
890 F: for<'a> Fn(
891 &'a Self,
892 &'a Self::DuplicateOptions<'_>,
893 &'a crate::State,
894 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
895 ) -> DuplicateHandlerResult<'a>
896 + Send
897 + Sync
898 + 'static,
899 >(
900 priority: ListenerPriority,
901 callback: F,
902 ) {
903 let erased = Box::new(callback) as Box<DuplicateHandler<Self>>;
904
905 Self::get_duplicate_handlers().blocking_register_handler(priority, erased);
906 }
907
908 fn blocking_register_after_duplicate_handler<
911 F: for<'a> Fn(
912 &'a Self,
913 &'a mut Self,
914 &'a Self::DuplicateOptions<'_>,
915 &'a crate::State,
916 &'a mut sqlx::Transaction<'_, sqlx::Postgres>,
917 ) -> DuplicateHandlerResult<'a>
918 + Send
919 + Sync
920 + 'static,
921 >(
922 priority: ListenerPriority,
923 callback: F,
924 ) {
925 let erased = Box::new(callback) as Box<DuplicateAfterHandler<Self>>;
926
927 Self::get_duplicate_handlers().blocking_register_after_handler(priority, erased);
928 }
929
930 async fn run_duplicate_handlers(
931 &self,
932 options: &Self::DuplicateOptions<'_>,
933 state: &crate::State,
934 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
935 ) -> Result<(), crate::database::DatabaseError> {
936 let listeners = Self::get_duplicate_handlers().before_handlers.read().await;
937
938 for listener in listeners.iter() {
939 (*listener.callback)(self, options, state, transaction).await?;
940 }
941
942 Ok(())
943 }
944
945 async fn run_after_duplicate_handlers(
946 &self,
947 duplicated: &mut Self,
948 options: &Self::DuplicateOptions<'_>,
949 state: &crate::State,
950 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
951 ) -> Result<(), crate::database::DatabaseError> {
952 let listeners = Self::get_duplicate_handlers().after_handlers.read().await;
953
954 for listener in listeners.iter() {
955 (*listener.callback)(self, duplicated, options, state, transaction).await?;
956 }
957
958 Ok(())
959 }
960
961 async fn duplicate_with_transaction(
962 &self,
963 state: &crate::State,
964 options: Self::DuplicateOptions<'_>,
965 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
966 ) -> Result<Self, crate::database::DatabaseError>;
967
968 async fn duplicate(
969 &self,
970 state: &crate::State,
971 options: Self::DuplicateOptions<'_>,
972 ) -> Result<Self, crate::database::DatabaseError> {
973 let mut transaction = state.database.write().begin().await?;
974
975 let duplicated = match self
976 .duplicate_with_transaction(state, options, &mut transaction)
977 .await
978 {
979 Ok(duplicated) => duplicated,
980 Err(err) => {
981 transaction.rollback().await?;
982 return Err(err);
983 }
984 };
985
986 transaction.commit().await?;
987
988 Ok(duplicated)
989 }
990}
991
992#[async_trait::async_trait]
993pub trait ByUuid: BaseModel {
994 async fn by_uuid(
995 database: &crate::database::Database,
996 uuid: uuid::Uuid,
997 ) -> Result<Self, DatabaseError>;
998
999 async fn by_uuid_with_transaction(
1000 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
1001 uuid: uuid::Uuid,
1002 ) -> Result<Self, DatabaseError>;
1003
1004 async fn by_uuid_cached(
1005 database: &crate::database::Database,
1006 uuid: uuid::Uuid,
1007 ) -> Result<Self, anyhow::Error> {
1008 database
1009 .cache
1010 .cached(&format!("{}::{uuid}", Self::NAME), 10, || {
1011 Self::by_uuid(database, uuid)
1012 })
1013 .await
1014 }
1015
1016 async fn by_uuid_optional(
1017 database: &crate::database::Database,
1018 uuid: uuid::Uuid,
1019 ) -> Result<Option<Self>, DatabaseError> {
1020 match Self::by_uuid(database, uuid).await {
1021 Ok(res) => Ok(Some(res)),
1022 Err(DatabaseError::Sqlx(sqlx::Error::RowNotFound)) => Ok(None),
1023 Err(err) => Err(err),
1024 }
1025 }
1026
1027 async fn by_uuid_optional_with_transaction(
1028 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
1029 uuid: uuid::Uuid,
1030 ) -> Result<Option<Self>, DatabaseError> {
1031 match Self::by_uuid_with_transaction(transaction, uuid).await {
1032 Ok(res) => Ok(Some(res)),
1033 Err(DatabaseError::Sqlx(sqlx::Error::RowNotFound)) => Ok(None),
1034 Err(err) => Err(err),
1035 }
1036 }
1037
1038 async fn by_uuid_optional_cached(
1039 database: &crate::database::Database,
1040 uuid: uuid::Uuid,
1041 ) -> Result<Option<Self>, anyhow::Error> {
1042 match Self::by_uuid_cached(database, uuid).await {
1043 Ok(res) => Ok(Some(res)),
1044 Err(err) => {
1045 if let Some(DatabaseError::Sqlx(sqlx::Error::RowNotFound)) =
1046 err.downcast_ref::<DatabaseError>()
1047 {
1048 Ok(None)
1049 } else {
1050 Err(err)
1051 }
1052 }
1053 }
1054 }
1055
1056 #[inline]
1057 fn get_fetchable(uuid: uuid::Uuid) -> Fetchable<Self> {
1058 Fetchable {
1059 uuid,
1060 _model: PhantomData,
1061 }
1062 }
1063
1064 #[inline]
1065 fn get_fetchable_from_row(row: &PgRow, column: impl AsRef<str>) -> Option<Fetchable<Self>> {
1066 match row.try_get(column.as_ref()) {
1067 Ok(uuid) => Some(Fetchable {
1068 uuid,
1069 _model: PhantomData,
1070 }),
1071 Err(_) => None,
1072 }
1073 }
1074}
1075
1076#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
1077pub enum ListenerPriority {
1078 Highest,
1079 High,
1080 #[default]
1081 Normal,
1082 Low,
1083 Lowest,
1084}
1085
1086impl ListenerPriority {
1087 #[inline]
1088 fn rank(self) -> u8 {
1089 match self {
1090 Self::Highest => 5,
1091 Self::High => 4,
1092 Self::Normal => 3,
1093 Self::Low => 2,
1094 Self::Lowest => 1,
1095 }
1096 }
1097}
1098
1099impl PartialOrd for ListenerPriority {
1100 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1101 Some(self.cmp(other))
1102 }
1103}
1104
1105impl Ord for ListenerPriority {
1106 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1107 let self_rank = self.rank();
1108 let other_rank = other.rank();
1109
1110 other_rank.cmp(&self_rank)
1111 }
1112}
1113
1114#[async_trait::async_trait]
1115impl<F: Send + Sync, AfterF: Send + Sync> crate::events::DisconnectEventHandler
1116 for ModelHandlerList<F, AfterF>
1117{
1118 #[inline]
1119 async fn disconnect(&self, id: uuid::Uuid) {
1120 self.before_handlers.write().await.retain(|l| l.uuid != id);
1121 self.after_handlers.write().await.retain(|l| l.uuid != id);
1122 }
1123
1124 #[inline]
1125 fn blocking_disconnect(&self, id: uuid::Uuid) {
1126 self.before_handlers
1127 .blocking_write()
1128 .retain(|l| l.uuid != id);
1129 self.after_handlers
1130 .blocking_write()
1131 .retain(|l| l.uuid != id);
1132 }
1133}
1134
1135pub struct ModelHandlerList<F: Send + Sync + 'static, AfterF: Send + Sync + 'static> {
1136 before_handlers: RwLock<Vec<ModelHandler<F>>>,
1137 after_handlers: RwLock<Vec<ModelHandler<AfterF>>>,
1138}
1139
1140impl<F: Send + Sync + 'static, AfterF: Send + Sync + 'static> Default
1141 for ModelHandlerList<F, AfterF>
1142{
1143 fn default() -> Self {
1144 Self {
1145 before_handlers: RwLock::new(Vec::new()),
1146 after_handlers: RwLock::new(Vec::new()),
1147 }
1148 }
1149}
1150
1151impl<F: Send + Sync + 'static, AfterF: Send + Sync + 'static> ModelHandlerList<F, AfterF> {
1152 pub async fn register_handler(
1153 self: &Arc<Self>,
1154 priority: ListenerPriority,
1155 callback: F,
1156 ) -> ModelHandlerHandle {
1157 let (listener, aborter) = ModelHandler::new(callback, priority, self.clone());
1158
1159 let mut self_listeners = self.before_handlers.write().await;
1160 self_listeners.push(listener);
1161 self_listeners.sort_by_key(|a| a.priority);
1162
1163 aborter
1164 }
1165
1166 pub async fn register_after_handler(
1167 self: &Arc<Self>,
1168 priority: ListenerPriority,
1169 callback: AfterF,
1170 ) -> ModelHandlerHandle {
1171 let (listener, aborter) = ModelHandler::new(callback, priority, self.clone());
1172
1173 let mut self_listeners = self.after_handlers.write().await;
1174 self_listeners.push(listener);
1175 self_listeners.sort_by_key(|a| a.priority);
1176
1177 aborter
1178 }
1179
1180 pub fn blocking_register_handler(
1183 self: &Arc<Self>,
1184 priority: ListenerPriority,
1185 callback: F,
1186 ) -> ModelHandlerHandle {
1187 let (listener, aborter) = ModelHandler::new(callback, priority, self.clone());
1188
1189 let mut self_listeners = self.before_handlers.blocking_write();
1190 self_listeners.push(listener);
1191 self_listeners.sort_by_key(|a| a.priority);
1192
1193 aborter
1194 }
1195
1196 pub fn blocking_register_after_handler(
1199 self: &Arc<Self>,
1200 priority: ListenerPriority,
1201 callback: AfterF,
1202 ) -> ModelHandlerHandle {
1203 let (listener, aborter) = ModelHandler::new(callback, priority, self.clone());
1204
1205 let mut self_listeners = self.after_handlers.blocking_write();
1206 self_listeners.push(listener);
1207 self_listeners.sort_by_key(|a| a.priority);
1208
1209 aborter
1210 }
1211}
1212
1213pub struct ModelHandler<F: Send + Sync + 'static> {
1214 uuid: uuid::Uuid,
1215 priority: ListenerPriority,
1216
1217 pub callback: F,
1218}
1219
1220impl<F: Send + Sync + 'static> ModelHandler<F> {
1221 pub(crate) fn new(
1222 callback: F,
1223 priority: ListenerPriority,
1224 list: Arc<dyn crate::events::DisconnectEventHandler + Send + Sync>,
1225 ) -> (Self, ModelHandlerHandle) {
1226 let handler = Self {
1227 uuid: uuid::Uuid::new_v4(),
1228 priority,
1229 callback,
1230 };
1231 let handle = ModelHandlerHandle {
1232 list_ref: list,
1233 id: handler.uuid,
1234 };
1235 (handler, handle)
1236 }
1237}
1238
1239pub struct ModelHandlerHandle {
1240 list_ref: Arc<dyn crate::events::DisconnectEventHandler + Send + Sync>,
1241 id: uuid::Uuid,
1242}
1243
1244impl ModelHandlerHandle {
1245 pub async fn disconnect(&self) {
1246 self.list_ref.disconnect(self.id).await;
1247 }
1248
1249 pub fn blocking_disconnect(&self) {
1252 self.list_ref.blocking_disconnect(self.id);
1253 }
1254}
1255
1256#[derive(Serialize, Deserialize, Clone, Copy)]
1257pub struct Fetchable<M: ByUuid> {
1258 pub uuid: uuid::Uuid,
1259 #[serde(skip)]
1260 _model: PhantomData<M>,
1261}
1262
1263impl<M: ByUuid + Send> Fetchable<M> {
1264 #[inline]
1265 pub async fn fetch(&self, database: &crate::database::Database) -> Result<M, DatabaseError> {
1266 M::by_uuid(database, self.uuid).await
1267 }
1268
1269 #[inline]
1270 pub async fn fetch_cached(
1271 &self,
1272 database: &crate::database::Database,
1273 ) -> Result<M, anyhow::Error> {
1274 M::by_uuid_cached(database, self.uuid).await
1275 }
1276
1277 #[inline]
1278 pub async fn fetch_optional(
1279 &self,
1280 database: &crate::database::Database,
1281 ) -> Result<Option<M>, DatabaseError> {
1282 M::by_uuid_optional(database, self.uuid).await
1283 }
1284
1285 #[inline]
1286 pub async fn fetch_optional_cached(
1287 &self,
1288 database: &crate::database::Database,
1289 ) -> Result<Option<M>, anyhow::Error> {
1290 M::by_uuid_optional_cached(database, self.uuid).await
1291 }
1292}
1293
1294pub struct InsertQueryBuilder<'a> {
1295 table: &'a str,
1296 columns: Vec<&'a str>,
1297 expressions: Vec<String>,
1298 arguments: PgArguments,
1299 returning_clause: Option<&'a str>,
1300}
1301
1302impl<'a> InsertQueryBuilder<'a> {
1303 pub fn new(table: &'a str) -> Self {
1304 Self {
1305 table,
1306 columns: Vec::new(),
1307 expressions: Vec::new(),
1308 arguments: PgArguments::default(),
1309 returning_clause: None,
1310 }
1311 }
1312
1313 pub fn set<T: 'a + sqlx::Encode<'a, Postgres> + sqlx::Type<Postgres> + Send>(
1314 &mut self,
1315 column: &'a str,
1316 value: T,
1317 ) -> &mut Self {
1318 if self.columns.contains(&column) {
1319 return self;
1320 }
1321
1322 if self.arguments.add(value).is_ok() {
1323 self.columns.push(column);
1324 let idx = self.arguments.len();
1325 self.expressions.push(format!("${}", idx));
1326 }
1327
1328 self
1329 }
1330
1331 pub fn set_expr<T: 'a + sqlx::Encode<'a, Postgres> + sqlx::Type<Postgres> + Send>(
1332 &mut self,
1333 column: &'a str,
1334 expression: &str,
1335 values: Vec<T>,
1336 ) -> &mut Self {
1337 if self.columns.contains(&column) {
1338 return self;
1339 }
1340
1341 let start_len = self.arguments.len();
1342
1343 for value in values {
1344 if self.arguments.add(value).is_err() {
1345 return self;
1346 }
1347 }
1348
1349 let mut expr = expression.to_string();
1350 let added_count = self.arguments.len() - start_len;
1351
1352 for i in (1..=added_count).rev() {
1353 let global_idx = start_len + i;
1354 expr = expr.replace(&format!("${}", i), &format!("${}", global_idx));
1355 }
1356
1357 self.columns.push(column);
1358 self.expressions.push(expr);
1359
1360 self
1361 }
1362
1363 pub fn returning(mut self, clause: &'a str) -> Self {
1364 self.returning_clause = Some(clause);
1365 self
1366 }
1367
1368 fn build_sql(&self) -> String {
1369 let columns_sql = self.columns.join(", ");
1370 let values_sql = self.expressions.join(", ");
1371
1372 let mut sql = format!(
1373 "INSERT INTO {} ({}) VALUES ({})",
1374 self.table, columns_sql, values_sql
1375 );
1376
1377 if let Some(clause) = self.returning_clause {
1378 sql.push_str(" RETURNING ");
1379 sql.push_str(clause);
1380 }
1381
1382 sql
1383 }
1384
1385 pub async fn execute(
1386 self,
1387 executor: impl sqlx::Executor<'a, Database = Postgres>,
1388 ) -> Result<sqlx::postgres::PgQueryResult, sqlx::Error> {
1389 let sql = self.build_sql();
1390 sqlx::query_with(sqlx::AssertSqlSafe(sql), self.arguments)
1391 .execute(executor)
1392 .await
1393 }
1394
1395 pub async fn fetch_one(
1396 self,
1397 executor: impl sqlx::Executor<'a, Database = Postgres>,
1398 ) -> Result<sqlx::postgres::PgRow, sqlx::Error> {
1399 let sql = self.build_sql();
1400 sqlx::query_with(sqlx::AssertSqlSafe(sql), self.arguments)
1401 .fetch_one(executor)
1402 .await
1403 }
1404}
1405
1406pub struct UpdateQueryBuilder<'a> {
1407 builder: QueryBuilder<Postgres>,
1408 updated_fields: HashSet<&'a str>,
1409 has_set_fields: bool,
1410 has_where: bool,
1411}
1412
1413impl<'a> UpdateQueryBuilder<'a> {
1414 pub fn new(table: &'a str) -> Self {
1415 let mut builder = QueryBuilder::new("UPDATE ");
1416 builder.push(table);
1417 builder.push(" SET ");
1418
1419 Self {
1420 builder,
1421 updated_fields: HashSet::new(),
1422 has_set_fields: false,
1423 has_where: false,
1424 }
1425 }
1426
1427 pub fn set<T: 'a + sqlx::Encode<'a, Postgres> + sqlx::Type<Postgres> + Send>(
1430 &mut self,
1431 column: &'a str,
1432 value: Option<T>,
1433 ) -> &mut Self {
1434 let Some(value) = value else {
1435 return self;
1436 };
1437
1438 if !self.updated_fields.insert(column) {
1439 return self;
1440 }
1441
1442 if self.has_set_fields {
1443 self.builder.push(", ");
1444 }
1445
1446 self.builder.push(column);
1447 self.builder.push(" = ");
1448 self.builder.push_bind(value);
1449
1450 self.has_set_fields = true;
1451 self
1452 }
1453
1454 pub fn where_eq<T: 'a + sqlx::Encode<'a, Postgres> + sqlx::Type<Postgres> + Send>(
1455 &mut self,
1456 column: &'a str,
1457 value: T,
1458 ) -> &mut Self {
1459 if self.has_where {
1460 self.builder.push(" AND ");
1461 } else {
1462 self.builder.push(" WHERE ");
1463 self.has_where = true;
1464 }
1465
1466 self.builder.push(column);
1467 self.builder.push(" = ");
1468 self.builder.push_bind(value);
1469 self
1470 }
1471
1472 pub async fn execute(
1473 mut self,
1474 executor: impl sqlx::Executor<'a, Database = Postgres>,
1475 ) -> Result<sqlx::any::AnyQueryResult, sqlx::Error> {
1476 if !self.has_set_fields {
1477 return Ok(sqlx::any::AnyQueryResult::default());
1478 }
1479
1480 let query = self.builder.build();
1481 query.execute(executor).await.map(|r| r.into())
1482 }
1483}
1484
1485pub struct OrderedJson<T>(pub T);
1487
1488impl<T: Serialize> sqlx::Encode<'_, sqlx::Postgres> for OrderedJson<T> {
1489 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
1490 serde_json::to_writer(&mut **buf, &self.0)?;
1491 Ok(IsNull::No)
1492 }
1493}
1494
1495impl<T> sqlx::Type<sqlx::Postgres> for OrderedJson<T> {
1496 fn type_info() -> PgTypeInfo {
1497 PgTypeInfo::with_oid(sqlx::postgres::types::Oid(114))
1499 }
1500}
1501
1502#[async_trait::async_trait]
1503pub trait IntoApiObject {
1504 type ApiObject: Send;
1505 type ExtraArgs<'a>: Send;
1506
1507 async fn into_api_object<'a>(
1508 self,
1509 state: &crate::State,
1510 args: Self::ExtraArgs<'a>,
1511 ) -> Result<Self::ApiObject, DatabaseError>;
1512}
1513
1514#[async_trait::async_trait]
1515pub trait IntoAdminApiObject {
1516 type AdminApiObject: Send;
1517 type ExtraArgs<'a>: Send;
1518
1519 async fn into_admin_api_object<'a>(
1520 self,
1521 state: &crate::State,
1522 args: Self::ExtraArgs<'a>,
1523 ) -> Result<Self::AdminApiObject, DatabaseError>;
1524}