atmosphere_core/schema/
read.rs

1use crate::{
2    Bind, Error, Result,
3    hooks::{self, HookInput, HookStage, Hooks},
4    query::{QueryError, QueryResult},
5    schema::Table,
6};
7
8use async_trait::async_trait;
9use sqlx::{Executor, IntoArguments, database::Database};
10
11/// Trait for reading rows from a database.
12///
13/// This trait provides the functionality for reading data from tables in a SQL database. It
14/// defines several asynchronous methods for retrieving rows either by their primary key, reloading
15/// existing entities, or fetching all rows in a table. The trait incorporates hooks at various
16/// stages, allowing for custom logic to be executed as part of the reading process.
17#[async_trait]
18pub trait Read: Table + Bind + Hooks + Send + Sync + Unpin + 'static {
19    /// Finds and retrieves a row by its primary key. This method constructs a query to fetch
20    /// a single row based on the primary key, executes it, and returns the result, optionally
21    /// triggering hooks before and after execution.
22    async fn read<'e, E>(executor: E, pk: &Self::PrimaryKey) -> Result<Self>
23    where
24        E: Executor<'e, Database = crate::Driver>,
25        for<'q> <crate::Driver as Database>::Arguments<'q>: IntoArguments<'q, crate::Driver> + Send;
26
27    /// Finds and retrieves a row by its primary key. This method constructs a query to fetch
28    /// a single row based on the primary key, executes it, and returns the result, optionally
29    /// triggering hooks before and after execution.
30    async fn find<'e, E>(executor: E, pk: &Self::PrimaryKey) -> Result<Option<Self>>
31    where
32        E: Executor<'e, Database = crate::Driver>,
33        for<'q> <crate::Driver as Database>::Arguments<'q>: IntoArguments<'q, crate::Driver> + Send;
34
35    /// Retrieves all rows from the table. This method is useful for fetching the complete
36    /// dataset of a table, executing a query to return all rows, and applying hooks as needed.
37    async fn read_all<'e, E>(executor: E) -> Result<Vec<Self>>
38    where
39        E: Executor<'e, Database = crate::Driver>,
40        for<'q> <crate::Driver as Database>::Arguments<'q>: IntoArguments<'q, crate::Driver> + Send;
41
42    /// Reloads the current entity from the database. This method is designed to update the entity
43    /// instance with the latest data from the database, ensuring that it reflects the current
44    /// state of the corresponding row.
45    async fn reload<'e, E>(&mut self, executor: E) -> Result<()>
46    where
47        E: Executor<'e, Database = crate::Driver>,
48        for<'q> <crate::Driver as Database>::Arguments<'q>: IntoArguments<'q, crate::Driver> + Send;
49}
50
51#[async_trait]
52impl<T> Read for T
53where
54    T: Table + Bind + Hooks + Send + Sync + Unpin + 'static,
55{
56    async fn read<'e, E>(executor: E, pk: &Self::PrimaryKey) -> Result<Self>
57    where
58        E: Executor<'e, Database = crate::Driver>,
59        for<'q> <crate::Driver as Database>::Arguments<'q>: IntoArguments<'q, crate::Driver> + Send,
60    {
61        let query = crate::runtime::sql::select::<T>();
62
63        hooks::execute(HookStage::PreBind, &query, HookInput::PrimaryKey(pk)).await?;
64
65        assert!(query.bindings().columns().len() == 1);
66        assert!(query.bindings().columns()[0].field() == Self::PRIMARY_KEY.field);
67        assert!(query.bindings().columns()[0].sql() == Self::PRIMARY_KEY.sql);
68
69        hooks::execute(HookStage::PreExec, &query, HookInput::None).await?;
70
71        let res = sqlx::query_as(query.sql())
72            .bind(pk)
73            .persistent(false)
74            .fetch_one(executor)
75            .await
76            .map_err(QueryError::from)
77            .map_err(Error::Query);
78
79        hooks::execute(
80            hooks::HookStage::PostExec,
81            &query,
82            QueryResult::One(&res).into(),
83        )
84        .await?;
85
86        res
87    }
88
89    async fn find<'e, E>(executor: E, pk: &Self::PrimaryKey) -> Result<Option<Self>>
90    where
91        E: Executor<'e, Database = crate::Driver>,
92        for<'q> <crate::Driver as Database>::Arguments<'q>: IntoArguments<'q, crate::Driver> + Send,
93    {
94        let query = crate::runtime::sql::select::<T>();
95
96        hooks::execute(HookStage::PreBind, &query, HookInput::PrimaryKey(pk)).await?;
97
98        assert!(query.bindings().columns().len() == 1);
99        assert!(query.bindings().columns()[0].field() == Self::PRIMARY_KEY.field);
100        assert!(query.bindings().columns()[0].sql() == Self::PRIMARY_KEY.sql);
101
102        hooks::execute(HookStage::PreExec, &query, HookInput::None).await?;
103
104        let res = sqlx::query_as(query.sql())
105            .bind(pk)
106            .persistent(false)
107            .fetch_optional(executor)
108            .await
109            .map_err(QueryError::from)
110            .map_err(Error::Query);
111
112        hooks::execute(
113            hooks::HookStage::PostExec,
114            &query,
115            QueryResult::Optional(&res).into(),
116        )
117        .await?;
118
119        res
120    }
121
122    async fn read_all<'e, E>(executor: E) -> Result<Vec<Self>>
123    where
124        E: Executor<'e, Database = crate::Driver>,
125        for<'q> <crate::Driver as Database>::Arguments<'q>: IntoArguments<'q, crate::Driver> + Send,
126    {
127        let query = crate::runtime::sql::select_all::<T>();
128
129        hooks::execute(HookStage::PreBind, &query, HookInput::None).await?;
130        hooks::execute(HookStage::PreExec, &query, HookInput::None).await?;
131
132        let res = sqlx::query_as(query.sql())
133            .persistent(false)
134            .fetch_all(executor)
135            .await
136            .map_err(QueryError::from)
137            .map_err(Error::Query);
138
139        hooks::execute(
140            hooks::HookStage::PostExec,
141            &query,
142            QueryResult::Many(&res).into(),
143        )
144        .await?;
145
146        res
147    }
148
149    async fn reload<'e, E>(&mut self, executor: E) -> Result<()>
150    where
151        E: Executor<'e, Database = crate::Driver>,
152        for<'q> <crate::Driver as Database>::Arguments<'q>: IntoArguments<'q, crate::Driver> + Send,
153    {
154        let query = crate::runtime::sql::select_by::<T>(T::PRIMARY_KEY.as_col());
155
156        hooks::execute(HookStage::PreBind, &query, HookInput::Row(self)).await?;
157
158        let mut sql = sqlx::query_as(query.sql());
159
160        for c in query.bindings().columns() {
161            sql = self.bind(c, sql).unwrap();
162        }
163
164        hooks::execute(HookStage::PreExec, &query, HookInput::None).await?;
165
166        let res = sql
167            .persistent(false)
168            .fetch_one(executor)
169            .await
170            .map_err(QueryError::from)
171            .map_err(Error::Query);
172
173        hooks::execute(
174            hooks::HookStage::PostExec,
175            &query,
176            QueryResult::One(&res).into(),
177        )
178        .await?;
179
180        *self = res?;
181
182        Ok(())
183    }
184}