ormx/
lib.rs

1#![cfg(any(
2    feature = "mysql",
3    feature = "postgres",
4    feature = "sqlite",
5    feature = "mariadb"
6))]
7//! Lightweight derive macros for bringing orm-like features to sqlx.
8//!
9//! # Example: Table
10//! ```rust,ignore
11//! #[derive(ormx::Table)]
12//! #[ormx(table = "users", id = user_id, insertable)]
13//! struct User {
14//!     #[ormx(column = "id")]
15//!     user_id: u32,
16//!     first_name: String,
17//!     last_name: String,
18//!     #[ormx(get_optional(&str))]
19//!     email: String,
20//!     #[ormx(default, set)]
21//!     last_login: Option<NaiveDateTime>,
22//! }
23//! ```
24//!
25//! # Example: Patch
26//! ```rust,ignore
27//! #[derive(ormx::Patch)]
28//! #[ormx(table_name = "users", table = User, id = "id")]
29//! struct UpdateName {
30//!     first_name: String,
31//!     last_name: String,
32//! }
33//! ```
34//!
35//! # Documentation
36//! See the docs of [derive(Table)](derive.Table.html) and [Patch](trait.Patch.html).
37
38use std::future::Future;
39
40use futures::{Stream, TryStreamExt};
41pub use ormx_macros::*;
42use sqlx::{Executor, Result};
43
44#[doc(hidden)]
45pub mod exports {
46    pub use futures::Stream;
47
48    pub use crate::query2::map::*;
49}
50
51#[cfg(any(feature = "mysql", feature = "postgres", feature = "mariadb"))]
52mod query2;
53
54#[cfg(any(feature = "mysql", feature = "mariadb"))]
55pub type Db = sqlx::MySql;
56#[cfg(feature = "postgres")]
57pub type Db = sqlx::Postgres;
58#[cfg(feature = "sqlite")]
59pub type Db = sqlx::Sqlite;
60
61/// A database table in which each row is identified by a unique ID.
62pub trait Table
63where
64    Self: Sized + Send + Sync + 'static,
65{
66    /// Type of the ID column of this table.
67    type Id: 'static + Copy + Send;
68
69    /// Returns the id of this row.
70    fn id(&self) -> Self::Id;
71
72    /// Insert a row into the database.
73    fn insert<'a, 'c: 'a>(
74        #[cfg(not(feature = "mysql"))] db: impl Executor<'c, Database = Db> + 'a,
75        #[cfg(feature = "mysql")] db: &'c mut sqlx::MySqlConnection,
76        row: impl Insert<Table = Self>,
77    ) -> impl Future<Output = Result<Self>> + Send + 'a {
78        row.insert(db)
79    }
80
81    /// Queries the row of the given id.
82    fn get<'a, 'c: 'a>(
83        db: impl Executor<'c, Database = Db> + 'a,
84        id: Self::Id,
85    ) -> impl Future<Output = Result<Self>> + Send + 'a;
86
87    /// Stream all rows from this table.
88    /// By default, results are ordered in descending order according to their ID column.
89    /// This can be configured using `#[ormx(order_by = "some_column ASC")]`.
90    fn stream_all<'a, 'c: 'a>(
91        db: impl Executor<'c, Database = Db> + 'a,
92    ) -> impl Stream<Item = Result<Self>> + Send + 'a;
93
94    /// Streams at most `limit` rows from this table, skipping the first `offset` rows.
95    /// By default, results are ordered in descending order according to their ID column.
96    /// This can be configured using `#[ormx(order_by = "some_column ASC")]`.
97    fn stream_all_paginated<'a, 'c: 'a>(
98        db: impl Executor<'c, Database = Db> + 'a,
99        offset: i64,
100        limit: i64,
101    ) -> impl Stream<Item = Result<Self>> + Send + 'a;
102
103    /// Load all rows from this table.
104    /// By default, results are ordered in descending order according to their ID column.
105    /// This can be configured using `#[ormx(order_by = "some_column ASC")]`.
106    fn all<'a, 'c: 'a>(
107        db: impl Executor<'c, Database = Db> + 'a,
108    ) -> impl Future<Output = Result<Vec<Self>>> + Send + 'a {
109        Self::stream_all(db).try_collect()
110    }
111
112    /// Load at most `limit` rows from this table, skipping the first `offset`.
113    /// By default, results are ordered in descending order according to their ID column.
114    /// This can be configured using `#[ormx(order_by = "some_column ASC")]`.
115    fn all_paginated<'a, 'c: 'a>(
116        db: impl Executor<'c, Database = Db> + 'a,
117        offset: i64,
118        limit: i64,
119    ) -> impl Future<Output = Result<Vec<Self>>> + Send + 'a {
120        Self::stream_all_paginated(db, offset, limit).try_collect()
121    }
122
123    /// Applies a patch to this row.
124    fn patch<'a, 'c: 'a, P>(
125        &'a mut self,
126        db: impl Executor<'c, Database = Db> + 'a,
127        patch: P,
128    ) -> impl Future<Output = Result<()>> + Send + 'a
129    where
130        P: Patch<Table = Self>,
131    {
132        async move {
133            let patch: P = patch;
134            patch.patch_row(db, self.id()).send().await?;
135            patch.apply_to(self);
136            Ok(())
137        }
138    }
139
140    /// Updates all fields of this row, regardless if they have been changed or not.
141    fn update<'a, 'c: 'a>(
142        &'a self,
143        db: impl Executor<'c, Database = Db> + 'a,
144    ) -> impl Future<Output = Result<()>> + Send + 'a;
145
146    /// Refresh this row, querying all columns from the database.
147    fn reload<'a, 'c: 'a>(
148        &'a mut self,
149        db: impl Executor<'c, Database = Db> + 'a,
150    ) -> impl Future<Output = Result<()>> + Send + 'a {
151        async move {
152            *self = Self::get(db, self.id()).send().await?;
153            Ok(())
154        }
155    }
156}
157
158pub trait Delete
159where
160    Self: Table + Sized + Send + Sync + 'static,
161{
162    /// Delete a row from the database
163    fn delete_row<'a, 'c: 'a>(
164        db: impl Executor<'c, Database = Db> + 'a,
165        id: Self::Id,
166    ) -> impl Future<Output = Result<()>> + Send + 'a;
167
168    /// Deletes this row from the database
169    fn delete<'a, 'c: 'a>(
170        self,
171        db: impl Executor<'c, Database = Db> + 'a,
172    ) -> impl Future<Output = Result<()>> + Send + 'a {
173        Self::delete_row(db, self.id())
174    }
175
176    /// Deletes this row from the database
177    fn delete_ref<'a, 'c: 'a>(
178        &self,
179        db: impl Executor<'c, Database = Db> + 'a,
180    ) -> impl Future<Output = Result<()>> + Send + 'a {
181        Self::delete_row(db, self.id())
182    }
183}
184
185/// A type which can be used to "patch" a row, updating multiple fields at once.
186pub trait Patch
187where
188    Self: Sized + Send + Sync + 'static,
189{
190    type Table: Table;
191
192    /// Applies the data of this patch to the given entity.
193    /// This does not persist the change in the database.
194    fn apply_to(self, entity: &mut Self::Table);
195
196    /// Applies this patch to a row in the database.
197    fn patch_row<'a, 'c: 'a>(
198        &'a self,
199        db: impl Executor<'c, Database = Db> + 'a,
200        id: <Self::Table as Table>::Id,
201    ) -> impl Future<Output = Result<()>> + Send + 'a;
202}
203
204/// A type which can be inserted as a row into the database.
205pub trait Insert
206where
207    Self: Sized + Send + Sync + 'static,
208{
209    type Table: Table;
210
211    /// Insert a row into the database, returning the inserted row.
212    fn insert<'a, 'c: 'a>(
213        self,
214        #[cfg(not(feature = "mysql"))] db: impl Executor<'c, Database = Db> + 'a,
215        #[cfg(feature = "mysql")] db: &'c mut sqlx::MySqlConnection,
216    ) -> impl Future<Output = Result<Self::Table>> + Send + 'a;
217}
218
219// Ridiculous workaround for [#100013](https://github.com/rust-lang/rust/issues/100013#issuecomment-2210995259).
220trait SendFuture: Future {
221    fn send(self) -> impl Future<Output = Self::Output> + Send
222    where
223        Self: Sized + Send,
224    {
225        self
226    }
227}
228
229impl<T: Future> SendFuture for T {}