#![cfg(any( feature = "mysql", feature = "postgres", feature = "sqlite", feature = "mariadb" ))] //! Lightweight derive macros for bringing orm-like features to sqlx. //! //! # Example: Table //! ```rust,ignore //! #[derive(ormx::Table)] //! #[ormx(table = "users", id = user_id, insertable)] //! struct User { //! #[ormx(column = "id")] //! user_id: u32, //! first_name: String, //! last_name: String, //! #[ormx(get_optional(&str))] //! email: String, //! #[ormx(default, set)] //! last_login: Option, //! } //! ``` //! //! # Example: Patch //! ```rust,ignore //! #[derive(ormx::Patch)] //! #[ormx(table_name = "users", table = User, id = "id")] //! struct UpdateName { //! first_name: String, //! last_name: String, //! } //! ``` //! //! # Documentation //! See the docs of [derive(Table)](derive.Table.html) and [Patch](trait.Patch.html). use std::future::Future; use futures::{Stream, TryStreamExt}; pub use ormx_macros::*; use sqlx::{Executor, Result}; #[doc(hidden)] pub mod exports { pub use futures::Stream; pub use crate::query2::map::*; } #[cfg(any(feature = "mysql", feature = "postgres", feature = "mariadb"))] mod query2; #[cfg(any(feature = "mysql", feature = "mariadb"))] pub type Db = sqlx::MySql; #[cfg(feature = "postgres")] pub type Db = sqlx::Postgres; #[cfg(feature = "sqlite")] pub type Db = sqlx::Sqlite; /// A database table in which each row is identified by a unique ID. pub trait Table where Self: Sized + Send + Sync + 'static, { /// Type of the ID column of this table. type Id: 'static + Copy + Send; /// Returns the id of this row. fn id(&self) -> Self::Id; /// Insert a row into the database. fn insert<'a, 'c: 'a>( #[cfg(not(feature = "mysql"))] db: impl Executor<'c, Database = Db> + 'a, #[cfg(feature = "mysql")] db: &'c mut sqlx::MySqlConnection, row: impl Insert, ) -> impl Future> + Send + 'a { row.insert(db) } /// Queries the row of the given id. fn get<'a, 'c: 'a>( db: impl Executor<'c, Database = Db> + 'a, id: Self::Id, ) -> impl Future> + Send + 'a; /// Stream all rows from this table. /// By default, results are ordered in descending order according to their ID column. /// This can be configured using `#[ormx(order_by = "some_column ASC")]`. fn stream_all<'a, 'c: 'a>( db: impl Executor<'c, Database = Db> + 'a, ) -> impl Stream> + Send + 'a; /// Streams at most `limit` rows from this table, skipping the first `offset` rows. /// By default, results are ordered in descending order according to their ID column. /// This can be configured using `#[ormx(order_by = "some_column ASC")]`. fn stream_all_paginated<'a, 'c: 'a>( db: impl Executor<'c, Database = Db> + 'a, offset: i64, limit: i64, ) -> impl Stream> + Send + 'a; /// Load all rows from this table. /// By default, results are ordered in descending order according to their ID column. /// This can be configured using `#[ormx(order_by = "some_column ASC")]`. fn all<'a, 'c: 'a>( db: impl Executor<'c, Database = Db> + 'a, ) -> impl Future>> + Send + 'a { Self::stream_all(db).try_collect() } /// Load at most `limit` rows from this table, skipping the first `offset`. /// By default, results are ordered in descending order according to their ID column. /// This can be configured using `#[ormx(order_by = "some_column ASC")]`. fn all_paginated<'a, 'c: 'a>( db: impl Executor<'c, Database = Db> + 'a, offset: i64, limit: i64, ) -> impl Future>> + Send + 'a { Self::stream_all_paginated(db, offset, limit).try_collect() } /// Applies a patch to this row. fn patch<'a, 'c: 'a, P>( &'a mut self, db: impl Executor<'c, Database = Db> + 'a, patch: P, ) -> impl Future> + Send + 'a where P: Patch
, { async move { let patch: P = patch; patch.patch_row(db, self.id()).send().await?; patch.apply_to(self); Ok(()) } } /// Updates all fields of this row, regardless if they have been changed or not. fn update<'a, 'c: 'a>( &'a self, db: impl Executor<'c, Database = Db> + 'a, ) -> impl Future> + Send + 'a; /// Refresh this row, querying all columns from the database. fn reload<'a, 'c: 'a>( &'a mut self, db: impl Executor<'c, Database = Db> + 'a, ) -> impl Future> + Send + 'a { async move { *self = Self::get(db, self.id()).send().await?; Ok(()) } } } pub trait Delete where Self: Table + Sized + Send + Sync + 'static, { /// Delete a row from the database fn delete_row<'a, 'c: 'a>( db: impl Executor<'c, Database = Db> + 'a, id: Self::Id, ) -> impl Future> + Send + 'a; /// Deletes this row from the database fn delete<'a, 'c: 'a>( self, db: impl Executor<'c, Database = Db> + 'a, ) -> impl Future> + Send + 'a { Self::delete_row(db, self.id()) } /// Deletes this row from the database fn delete_ref<'a, 'c: 'a>( &self, db: impl Executor<'c, Database = Db> + 'a, ) -> impl Future> + Send + 'a { Self::delete_row(db, self.id()) } } /// A type which can be used to "patch" a row, updating multiple fields at once. pub trait Patch where Self: Sized + Send + Sync + 'static, { type Table: Table; /// Applies the data of this patch to the given entity. /// This does not persist the change in the database. fn apply_to(self, entity: &mut Self::Table); /// Applies this patch to a row in the database. fn patch_row<'a, 'c: 'a>( &'a self, db: impl Executor<'c, Database = Db> + 'a, id: ::Id, ) -> impl Future> + Send + 'a; } /// A type which can be inserted as a row into the database. pub trait Insert where Self: Sized + Send + Sync + 'static, { type Table: Table; /// Insert a row into the database, returning the inserted row. fn insert<'a, 'c: 'a>( self, #[cfg(not(feature = "mysql"))] db: impl Executor<'c, Database = Db> + 'a, #[cfg(feature = "mysql")] db: &'c mut sqlx::MySqlConnection, ) -> impl Future> + Send + 'a; } // Ridiculous workaround for [#100013](https://github.com/rust-lang/rust/issues/100013#issuecomment-2210995259). trait SendFuture: Future { fn send(self) -> impl Future + Send where Self: Sized + Send, { self } } impl SendFuture for T {}