Skip to content

Commit

Permalink
refactor: rename Read trait functions for alignment (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
cdbrkfxrpt authored Jun 29, 2024
1 parent 51700e2 commit 03e2a0e
Show file tree
Hide file tree
Showing 12 changed files with 91 additions and 88 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
members = [".", "atmosphere-core", "atmosphere-macros"]

[workspace.package]
version = "0.2.0"
version = "0.3.0"
license = "Apache-2.0"
edition = "2021"
exclude = ["/.github", "/tests"]
Expand All @@ -16,8 +16,8 @@ repository = "https://github.com/helsing-ai/atmosphere"
keywords = ["sqlx", "postgres", "database", "orm", "backend"]

[workspace.dependencies]
atmosphere-core = { version = "=0.2.0", path = "atmosphere-core" }
atmosphere-macros = { version = "=0.2.0", path = "atmosphere-macros" }
atmosphere-core = { version = "=0.3.0", path = "atmosphere-core" }
atmosphere-macros = { version = "=0.3.0", path = "atmosphere-macros" }
async-trait = "0.1"
lazy_static = "1"
sqlx = { version = "0.7", features = ["chrono"] }
Expand Down
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

[![SQLx](https://img.shields.io/badge/sqlx-framework-blueviolet.svg)](https://github.com/launchbadge/sqlx)
[![Crate](https://img.shields.io/crates/v/atmosphere.svg)](https://crates.io/crates/atmosphere)
[![Book](https://img.shields.io/badge/book-latest-0f5225.svg)](https://bmc-labs.github.io/atmosphere)
[![Book](https://img.shields.io/badge/book-latest-0f5225.svg)](https://helsing-ai.github.io/atmosphere)
[![Docs](https://img.shields.io/badge/docs-latest-153f66.svg)](https://docs.rs/atmosphere)

</div>
Expand Down Expand Up @@ -70,7 +70,7 @@ async fn main() -> sqlx::Result<()> {
// Field Queries

assert_eq!(
User::find(&pool, &0).await?,
User::read(&pool, &0).await?,
User::find_by_email(&pool, "[email protected]").await?.unwrap()
);

Expand Down Expand Up @@ -167,14 +167,15 @@ Atmosphere is able to derive and generate the following queries:

#### `atmosphere::Read`

- `Model::find`
- `Model::find_all`
- `Model::read`: read a `Model` by its primary key, returning a `Model`.
- `Model::find`: find a `Model` by its primary key, returning an `Option<Model>`.
- `Model::read_all`: read all `Model`s, returning a `Vec<Model>`.
- `Model::reload`

#### `atmosphere::Update`

- `Model::update`
- `Model::save`
- `Model::upsert`

#### `atmosphere::Delete`

Expand All @@ -187,8 +188,8 @@ Each struct field that is marked with `#[sql(unique)]` becomes queryable.

In the above example `b` was marked as unique so atmosphere implements:

- `Model::find_by_b`
- `Model::delete_by_b`
- `Model::find_by_b`: find a `Model` by its `b` field, returning an `Option<Model>`.
- `Model::delete_by_b`: delete a `Model` by its `b` field.

### Relationships & Inter-Table Queries

Expand Down
8 changes: 4 additions & 4 deletions atmosphere-core/src/schema/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ use sqlx::{database::HasArguments, Database, Executor, IntoArguments};

/// Trait for deleting rows from a database.
///
/// Provides functionality for deleting rows from a table in the database. Implementors of this trait can delete
/// entities either by their instance or by their primary key. The trait ensures proper execution of hooks at
/// various stages of the delete operation, enhancing flexibility and allowing for custom behavior during the
/// deletion process.
/// Provides functionality for deleting rows from a table in the database. Implementors of this
/// trait can delete entities either by their instance or by their primary key. The trait ensures
/// proper execution of hooks at various stages of the delete operation, enhancing flexibility and
/// allowing for custom behavior during the deletion process.
#[async_trait]
pub trait Delete: Table + Bind + Hooks + Send + Sync + Unpin + 'static {
/// Deletes the row represented by the instance from the database. Builds and executes a delete
Expand Down
56 changes: 28 additions & 28 deletions atmosphere-core/src/schema/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub trait Read: Table + Bind + Hooks + Send + Sync + Unpin + 'static {
/// Finds and retrieves a row by its primary key. This method constructs a query to fetch
/// a single row based on the primary key, executes it, and returns the result, optionally
/// triggering hooks before and after execution.
async fn find<'e, E>(executor: E, pk: &Self::PrimaryKey) -> Result<Self>
async fn read<'e, E>(executor: E, pk: &Self::PrimaryKey) -> Result<Self>
where
E: Executor<'e, Database = crate::Driver>,
for<'q> <crate::Driver as HasArguments<'q>>::Arguments:
Expand All @@ -28,15 +28,15 @@ pub trait Read: Table + Bind + Hooks + Send + Sync + Unpin + 'static {
/// Finds and retrieves a row by its primary key. This method constructs a query to fetch
/// a single row based on the primary key, executes it, and returns the result, optionally
/// triggering hooks before and after execution.
async fn find_optional<'e, E>(executor: E, pk: &Self::PrimaryKey) -> Result<Option<Self>>
async fn find<'e, E>(executor: E, pk: &Self::PrimaryKey) -> Result<Option<Self>>
where
E: Executor<'e, Database = crate::Driver>,
for<'q> <crate::Driver as HasArguments<'q>>::Arguments:
IntoArguments<'q, crate::Driver> + Send;

/// Retrieves all rows from the table. This method is useful for fetching the complete
/// dataset of a table, executing a query to return all rows, and applying hooks as needed.
async fn find_all<'e, E>(executor: E) -> Result<Vec<Self>>
async fn read_all<'e, E>(executor: E) -> Result<Vec<Self>>
where
E: Executor<'e, Database = crate::Driver>,
for<'q> <crate::Driver as HasArguments<'q>>::Arguments:
Expand All @@ -57,7 +57,7 @@ impl<T> Read for T
where
T: Table + Bind + Hooks + Send + Sync + Unpin + 'static,
{
async fn find<'e, E>(executor: E, pk: &Self::PrimaryKey) -> Result<Self>
async fn read<'e, E>(executor: E, pk: &Self::PrimaryKey) -> Result<Self>
where
E: Executor<'e, Database = crate::Driver>,
for<'q> <crate::Driver as HasArguments<'q>>::Arguments:
Expand Down Expand Up @@ -91,7 +91,7 @@ where
res
}

async fn find_optional<'e, E>(executor: E, pk: &Self::PrimaryKey) -> Result<Option<Self>>
async fn find<'e, E>(executor: E, pk: &Self::PrimaryKey) -> Result<Option<Self>>
where
E: Executor<'e, Database = crate::Driver>,
for<'q> <crate::Driver as HasArguments<'q>>::Arguments:
Expand Down Expand Up @@ -125,68 +125,68 @@ where
res
}

async fn reload<'e, E>(&mut self, executor: E) -> Result<()>
async fn read_all<'e, E>(executor: E) -> Result<Vec<Self>>
where
E: Executor<'e, Database = crate::Driver>,
for<'q> <crate::Driver as HasArguments<'q>>::Arguments:
IntoArguments<'q, crate::Driver> + Send,
{
let query = crate::runtime::sql::select_by::<T>(T::PRIMARY_KEY.as_col());

hooks::execute(HookStage::PreBind, &query, HookInput::Row(self)).await?;

let mut sql = sqlx::query_as(query.sql());

for c in query.bindings().columns() {
sql = self.bind(c, sql).unwrap();
}
let query = crate::runtime::sql::select_all::<T>();

hooks::execute(HookStage::PreBind, &query, HookInput::None).await?;
hooks::execute(HookStage::PreExec, &query, HookInput::None).await?;

let res = sql
let res = sqlx::query_as(query.sql())
.persistent(false)
.fetch_one(executor)
.fetch_all(executor)
.await
.map_err(QueryError::from)
.map_err(Error::Query);

hooks::execute(
hooks::HookStage::PostExec,
&query,
QueryResult::One(&res).into(),
QueryResult::Many(&res).into(),
)
.await?;

*self = res?;

Ok(())
res
}

async fn find_all<'e, E>(executor: E) -> Result<Vec<Self>>
async fn reload<'e, E>(&mut self, executor: E) -> Result<()>
where
E: Executor<'e, Database = crate::Driver>,
for<'q> <crate::Driver as HasArguments<'q>>::Arguments:
IntoArguments<'q, crate::Driver> + Send,
{
let query = crate::runtime::sql::select_all::<T>();
let query = crate::runtime::sql::select_by::<T>(T::PRIMARY_KEY.as_col());

hooks::execute(HookStage::PreBind, &query, HookInput::Row(self)).await?;

let mut sql = sqlx::query_as(query.sql());

for c in query.bindings().columns() {
sql = self.bind(c, sql).unwrap();
}

hooks::execute(HookStage::PreBind, &query, HookInput::None).await?;
hooks::execute(HookStage::PreExec, &query, HookInput::None).await?;

let res = sqlx::query_as(query.sql())
let res = sql
.persistent(false)
.fetch_all(executor)
.fetch_one(executor)
.await
.map_err(QueryError::from)
.map_err(Error::Query);

hooks::execute(
hooks::HookStage::PostExec,
&query,
QueryResult::Many(&res).into(),
QueryResult::One(&res).into(),
)
.await?;

res
*self = res?;

Ok(())
}
}
19 changes: 11 additions & 8 deletions atmosphere-core/src/schema/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ use sqlx::{database::HasArguments, Database, Executor, IntoArguments};

/// Update rows in a database.
///
/// Provides functionality for updating data in tables within a SQL database. This trait defines asynchronous methods
/// for modifying existing rows in the database, either through direct updates or upserts (update or insert if not exists).
/// It ensures that hooks are executed at various stages, enabling custom logic to be integrated into the update process.
/// Provides functionality for updating data in tables within a SQL database. This trait defines
/// asynchronous methods for modifying existing rows in the database, either through direct updates
/// or upserts (update or insert if not exists). It ensures that hooks are executed at various
/// stages, enabling custom logic to be integrated into the update process.
#[async_trait]
pub trait Update: Table + Bind + Hooks + Send + Sync + Unpin + 'static {
/// Updates an existing row in the database. This method constructs an update query, binds the
Expand All @@ -27,10 +28,9 @@ pub trait Update: Table + Bind + Hooks + Send + Sync + Unpin + 'static {
for<'q> <crate::Driver as HasArguments<'q>>::Arguments:
IntoArguments<'q, crate::Driver> + Send;

/// Similar to `update`, but uses an upsert approach. It either updates an existing row or
/// inserts a new one if it does not exist, depending on the primary key's presence and
/// uniqueness.
async fn save<'e, E>(
/// Similar to `update`, but either updates an existing row or inserts a new one if it does not
/// exist, depending on the primary key's presence and uniqueness.
async fn upsert<'e, E>(
&mut self,
executor: E,
) -> Result<<crate::Driver as Database>::QueryResult>
Expand Down Expand Up @@ -83,7 +83,10 @@ where
res
}

async fn save<'e, E>(&mut self, executor: E) -> Result<<crate::Driver as Database>::QueryResult>
async fn upsert<'e, E>(
&mut self,
executor: E,
) -> Result<<crate::Driver as Database>::QueryResult>
where
E: Executor<'e, Database = crate::Driver>,
for<'q> <crate::Driver as HasArguments<'q>>::Arguments:
Expand Down
49 changes: 20 additions & 29 deletions atmosphere-core/src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,18 @@ where
E: Entity + Clone + Debug + Eq + Send,
{
assert!(
E::find(pool, instance.pk()).await.is_err(),
"instance was found (find) before it was created"
E::read(pool, instance.pk()).await.is_err(),
"instance was found (read) before it was created"
);

assert!(
E::find_optional(pool, instance.pk())
.await
.unwrap()
.is_none(),
"instance was found (find_optional) before it was created"
E::find(pool, instance.pk()).await.unwrap().is_none(),
"instance was found (find) before it was created"
);

instance.create(pool).await.expect("insertion did not work");

let retrieved = E::find(pool, instance.pk())
let retrieved = E::read(pool, instance.pk())
.await
.expect("instance not found after insertion");

Expand All @@ -47,32 +44,29 @@ where
E: Entity + Clone + Debug + Eq + Send,
{
assert!(
E::find(pool, instance.pk()).await.is_err(),
"instance was found (find) after deletion"
E::read(pool, instance.pk()).await.is_err(),
"instance was found (read) after deletion"
);

assert!(
E::find_optional(pool, instance.pk())
.await
.unwrap()
.is_none(),
"instance was found (find_optional) after deletion"
E::find(pool, instance.pk()).await.unwrap().is_none(),
"instance was found (find) after deletion"
);

assert!(
E::find_all(pool).await.unwrap().is_empty(),
E::read_all(pool).await.unwrap().is_empty(),
"there was an instance found in the database before creating"
);

instance.create(pool).await.expect("insertion did not work");

let retrieved = E::find(pool, instance.pk())
let retrieved = E::read(pool, instance.pk())
.await
.expect("instance not found after insertion");

assert_eq!(instance, retrieved);

assert_eq!(E::find_all(pool).await.unwrap(), vec![instance.clone()]);
assert_eq!(E::read_all(pool).await.unwrap(), vec![instance.clone()]);
}

/// Tests updating of an entity in the database.
Expand All @@ -83,7 +77,7 @@ pub async fn update<E>(pool: &crate::Pool, mut instance: E, updates: Vec<E>)
where
E: Entity + Clone + Debug + Eq + Send,
{
instance.save(pool).await.expect("insertion did not work");
instance.upsert(pool).await.expect("insertion did not work");

for mut update in updates {
update
Expand All @@ -98,16 +92,16 @@ where

assert_eq!(instance, update);

let retrieved = E::find(pool, instance.pk())
let retrieved = E::read(pool, instance.pk())
.await
.expect("instance not found after update");

assert_eq!(instance, retrieved);

let retrieved = E::find_optional(pool, instance.pk())
let retrieved = E::find(pool, instance.pk())
.await
.unwrap()
.expect("instance not found (find_optional) after update");
.expect("instance not found (find) after update");

assert_eq!(instance, retrieved);
}
Expand All @@ -131,16 +125,13 @@ where
.expect_err("instance could be reloaded from db after deletion");

assert!(
E::find(pool, instance.pk()).await.is_err(),
"instance was found (find) after deletion"
E::read(pool, instance.pk()).await.is_err(),
"instance was found (read) after deletion"
);

assert!(
E::find_optional(pool, instance.pk())
.await
.unwrap()
.is_none(),
"instance was found (find_optional) after deletion"
E::find(pool, instance.pk()).await.unwrap().is_none(),
"instance was found (find) after deletion"
);

instance.create(pool).await.expect("insertion did not work");
Expand Down
5 changes: 4 additions & 1 deletion atmosphere-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ proc-macro = true
atmosphere-core.workspace = true
sqlx.workspace = true
proc-macro2 = { version = "1.0.36", default-features = false }
syn = { version = "2.0.39", default-features = false, features = ["parsing", "proc-macro"] }
syn = { version = "2.0.39", default-features = false, features = [
"parsing",
"proc-macro",
] }
quote = { version = "1.0.14", default-features = false }
lazy_static = "1.4.0"

Expand Down
Loading

0 comments on commit 03e2a0e

Please sign in to comment.