Skip to main content

ferro_orm/
lib.rs

1//! # ferro-orm
2//!
3//! Atomic conditional updates and ORM primitives for the Ferro framework.
4//!
5//! `GuardedUpdate<E>` compiles to a single `UPDATE … WHERE …` SQL statement,
6//! replacing the hand-rolled `read → check → write` pattern wherever a column's
7//! value is conditionally mutated. The database is the authority on contention;
8//! the call site is race-free by construction.
9//!
10//! ## Example
11//!
12//! ```rust,ignore
13//! use ferro_orm::{GuardedUpdate, ColumnTrait};
14//! use sea_orm::sea_query::Expr;
15//!
16//! GuardedUpdate::new(inventory_units::Entity)
17//!     .filter(inventory_units::Column::Id.eq(unit_id))
18//!     .filter(inventory_units::Column::Quantity.gte(needed))
19//!     .set_expr(
20//!         inventory_units::Column::Quantity,
21//!         Expr::col(inventory_units::Column::Quantity).sub(needed),
22//!     )
23//!     .exec_one(&txn)
24//!     .await?;
25//! // — exactly one row matched and was decremented atomically,
26//! //   OR Err(NoRowsAffected) signalling capacity exhausted.
27//! ```
28//!
29//! ## Atomicity guarantee (and its limit)
30//!
31//! `GuardedUpdate` guarantees atomicity *per statement*, not per builder.
32//! A caller building `.set_expr(qty - 1)` and reading the resulting `qty`
33//! in a separate query without a transaction re-introduces a race. The crate's
34//! job is to make the conditional `UPDATE` race-free; bracketing it in a
35//! transaction is the caller's responsibility.
36
37mod error;
38mod guarded;
39
40pub use error::GuardedError;
41pub use guarded::GuardedUpdate;
42
43// Targeted re-exports — consumers calling the builder need these.
44// Do NOT add a wildcard re-export of `sea_orm` (D-03).
45pub use sea_orm::sea_query::{Expr, IntoCondition, SimpleExpr, Value};
46pub use sea_orm::{ColumnTrait, ConnectionTrait, DbErr, EntityTrait};