1use std::marker::PhantomData;
4
5use sqlx::FromRow;
6
7use crate::query::QueryBuilder;
8use crate::Error;
9
10pub struct ModelRegistration {
13 pub class: &'static str,
14 pub table: &'static str,
15 pub columns: &'static [&'static str],
16}
17
18inventory::collect!(ModelRegistration);
19
20pub fn registered_models() -> Vec<&'static ModelRegistration> {
21 inventory::iter::<ModelRegistration>.into_iter().collect()
22}
23
24pub trait Model: Sized + Send + Sync + Unpin + 'static
25where
26 for<'r> Self: FromRow<'r, sqlx::postgres::PgRow>,
27{
28 const SOFT_DELETES: bool = false;
31
32 type PrimaryKey: sqlx::Type<sqlx::Postgres>
33 + for<'q> sqlx::Encode<'q, sqlx::Postgres>
34 + Send
35 + Sync
36 + Clone
37 + 'static;
38
39 const TABLE: &'static str;
41
42 const PK_COLUMN: &'static str = "id";
44
45 const COLUMNS: &'static [&'static str];
47
48 fn primary_key(&self) -> &Self::PrimaryKey;
50
51 fn query() -> QueryBuilder<Self> {
53 QueryBuilder::new()
54 }
55
56 fn find(
60 pool: &sqlx::PgPool,
61 id: Self::PrimaryKey,
62 ) -> futures::future::BoxFuture<'_, Result<Option<Self>, Error>> {
63 Box::pin(async move {
64 let sql = format!(
65 "SELECT {} FROM {} WHERE {} = $1 LIMIT 1",
66 Self::COLUMNS.join(", "),
67 Self::TABLE,
68 Self::PK_COLUMN,
69 );
70 let result = sqlx::query_as::<_, Self>(&sql)
71 .bind(id)
72 .fetch_optional(pool)
73 .await?;
74 Ok(result)
75 })
76 }
77
78 fn all(pool: &sqlx::PgPool) -> futures::future::BoxFuture<'_, Result<Vec<Self>, Error>> {
80 Box::pin(async move {
81 let sql = format!("SELECT {} FROM {}", Self::COLUMNS.join(", "), Self::TABLE);
82 let rows = sqlx::query_as::<_, Self>(&sql).fetch_all(pool).await?;
83 Ok(rows)
84 })
85 }
86}
87
88pub struct Loaded<M: Model, R = ()> {
91 pub items: Vec<M>,
92 pub _relations: PhantomData<R>,
93}
94
95impl<M: Model, R> Loaded<M, R> {
96 pub fn new(items: Vec<M>) -> Self {
97 Self {
98 items,
99 _relations: PhantomData,
100 }
101 }
102
103 pub fn into_inner(self) -> Vec<M> {
104 self.items
105 }
106
107 pub fn iter(&self) -> std::slice::Iter<'_, M> {
108 self.items.iter()
109 }
110}