1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
//! sqlx-crud is an extension to [sqlx] to derive Create, Read, Update, and
//! Delete (CRUD) methods for a struct representing a table in a sqlx database.
//!
//! This removes much of the common, repetitive code needed when dealing with
//! the typical CRUD operations.
//!
//! This library strives to do a few, narrowly defined things well in an effort
//! to reduce 80% of the redundant code you might write for a database
//! application when operating on a single table at a time. It provides
//! mechanisms: [Schema] and [Crud], to access and reuse the generated id,
//! column, and query metadata. This can help with writing more complex queries
//! outside of the single table CRUD paradigm.
//!
//! [sqlx]: https://github.com/launchbadge/sqlx
//!
//! # Design Considerations
//!
//! The code currently assumes identifiers are assigned outside of the database.
//! This likely means the identifier is a UUID. Database generated IDs will be
//! added in a subsequent release.
//!
//! The primary key for the table can be indicated by use of the [sqlx_crud_macros::SqlxCrud]
//! `#[id]` field attribute. If no field is tagged as the [sqlx_crud_macros::SqlxCrud] `#[id]`
//! then the first field in the struct is assumed to be the ID.
//!
//! The ordering of the columns used by queries and which columns are present
//! is controlled by the field order of the struct. Ignored fields are not
//! currently supported but will be added.
//!
//! # Features
//!
//! Because sqlx-crud depends on sqlx you need to use the same executor and TLS
//! feature pair as you did with sqlx. If for example you used `tokio-rustls`
//! with sqlx you should also use the same feature with sqlx-crud.
//!
//! Hopefully I can figure out a way to remove this requirement. I think
//! I might need to use a build.rs script and interrogate the sqlx features that way.
//!
//! # Examples
//!
//! Given a table `users` defined as:
//!
//! ```sql
//! CREATE TABLE users (
//! id INTEGER PRIMARY KEY NOT NULL,
//! username TEXT NOT NULL
//! );
//! ```
//!
//! To define a `User` struct with generated [Crud] methods:
//!
//! ```rust
//! use sqlx::FromRow;
//! use sqlx_crud::SqlxCrud;
//!
//! #[derive(Debug, FromRow, SqlxCrud)]
//! pub struct User {
//! pub user_id: i32,
//! pub name: String,
//! }
//! ```
//!
//! [Crud]: traits/trait.Crud.html
//!
//! To create a new `User` in the database:
//!
//! ```rust
//! # sqlx_crud::doctest_setup! { |pool| {
//! use sqlx_crud::Crud;
//!
//! let new_user = User { user_id: 2, name: "new_user".to_string() };
//! new_user.create(&pool).await?;
//! # }}
//! ```
//!
//! To query for a `User` where `user_id = 1`:
//!
//! ```rust
//! # sqlx_crud::doctest_setup! { |pool| {
//! use sqlx_crud::Crud;
//!
//! if let Some(user) = User::by_id(&pool, 1).await? {
//! println!("User: {:?}", user);
//! }
//! # }}
//! ```
//!
//! To update an existing record:
//!
//! ```rust
//! # sqlx_crud::doctest_setup! { |pool| {
//! use sqlx_crud::Crud;
//!
//! if let Some(mut user) = User::by_id(&pool, 1).await? {
//! user.name = "something else".to_string();
//! user.update(&pool).await?;
//! }
//! # }}
//! ```
//!
//! To delete a record:
//!
//! ```rust
//! # sqlx_crud::doctest_setup! { |pool| {
//! use sqlx_crud::Crud;
//!
//! if let Some(mut user) = User::by_id(&pool, 1).await? {
//! user.delete(&pool).await?;
//! }
//! # }}
//! ```
//!
//! Reusing and modifying the [select_sql] query string:
//!
//! ```rust
//! # sqlx_crud::doctest_setup! { |pool| {
//! use futures::stream::TryStreamExt;
//! use sqlx_crud::{Schema, SqlxCrud};
//!
//! #[derive(Debug, FromRow, SqlxCrud)]
//! pub struct User {
//! pub user_id: i32,
//! pub name: String,
//! }
//!
//! impl User {
//! pub async fn all_limit(pool: &SqlitePool, limit: i32) -> Result<Vec<Self>, sqlx::Error> {
//! let query = format!(
//! "{} ORDER BY users.id ASC LIMIT ?",
//! <Self as Schema>::select_sql()
//! );
//!
//! let mut users = Vec::new();
//! let mut stream = sqlx::query_as::<_, Self>(&query)
//! .bind(limit)
//! .fetch(pool);
//!
//! while let Some(user) = stream.try_next().await? {
//! users.push(user);
//! }
//!
//! Ok(users)
//! }
//! }
//! # }}
//! ```
//!
//! # Planned Future Improvements
//!
//! Subsequent updates will extend the library to be more useful in a larger
//! variety of situations.
//!
//! * Allow database assigned primary keys
//! * Crud::create() should return the assigned ID
//! * Add a field attribute to ignore fields
pub mod schema;
pub mod traits;
pub use sqlx_crud_macros::SqlxCrud;
pub use traits::{Crud, Schema};
#[macro_export]
#[doc(hidden)]
macro_rules! doctest_setup {
(|$pool:ident| $($t:tt)*) => {
use sqlx::FromRow;
use sqlx::SqlitePool;
use sqlx_crud::SqlxCrud;
#[derive(Debug, FromRow, SqlxCrud)]
pub struct User {
pub user_id: i32,
pub name: String,
}
fn main() -> Result<(), sqlx::Error> {
tokio_test::block_on(async {
let $pool = SqlitePool::connect(":memory:")
.await?;
sqlx::query("CREATE TABLE users (user_id INTEGER NOT NULL, name TEXT NOT NULL)")
.execute(&$pool)
.await?;
sqlx::query("INSERT INTO users (user_id, name) VALUES(?, ?)")
.bind::<i32>(1)
.bind("test")
.execute(&$pool)
.await?;
$($t)*;
Ok::<(), sqlx::Error>(())
});
Ok::<(), sqlx::Error>(())
}
}
}