Skip to main content

rainier_orm/
lib.rs

1//! # rainier_orm
2//!
3//! A dynamic, multi-dialect DBAL/ORM. You define a plain struct, derive
4//! [`Entity`] on it, and run generic CRUD across **SQLite / Cloudflare D1 /
5//! MySQL / Postgres** — no per-entity repositories written by hand.
6//!
7//! Three pieces compose it:
8//! 1. **`#[derive(Entity)]`** turns a struct into table + column metadata, a
9//!    generated row decoder ([`Entity::from_row`]), and value projections.
10//!    Column SQL types are deferred to each field type's [`SqlType`] impl and
11//!    decoding to its [`FromColumn`] impl, so adding a supported type is one
12//!    trait impl — not macro surgery.
13//! 2. **SQL generation via `sea-query`** (re-exported), which already renders
14//!    MySQL / Postgres / SQLite. Dialect translation is therefore free: the
15//!    same statement is built for whatever the [`Executor`] reports.
16//! 3. **A pluggable [`Executor`] trait** — one impl (`sea-orm`) covers MySQL,
17//!    Postgres, and SQLite; the Cloudflare path (D1 over HTTP, D1 native
18//!    binding in a Worker) implements the same trait. The core is wasm-safe
19//!    so the Worker can use it; native executors are feature-gated.
20//!
21//! Constraints (unique columns, indexes, foreign keys) are derived too; see
22//! [`schema`]. Relationships are *not* generated as navigation — a foreign key
23//! is a flat id field you traverse with [`repo::find_by`], because the related
24//! rows may live in a different backend (a cross-backend JOIN is impossible).
25//!
26//! ## The executors live elsewhere
27//!
28//! This crate defines the [`Executor`] port and **implements none of it**. Every
29//! driver — sea-orm for native MySQL / Postgres / SQLite, Cloudflare D1 over
30//! HTTP, libSQL / Turso — lives in
31//! [`rainier-drivers`](https://docs.rs/rainier-drivers), alongside Redis,
32//! Memcached and the AWS clients.
33//!
34//! That is not filing for its own sake. It is what keeps this crate
35//! **wasm-safe with no feature dance**: there are no optional dependencies here
36//! to accidentally enable, so the core compiles for `wasm32` unconditionally and
37//! a Worker takes the same `rainier-orm` a server does.
38//!
39//! Prefer a **native** driver wherever the runtime allows it: sea-orm is faster,
40//! supports real transactions, and pools locally. The HTTP executors exist for
41//! one reason — a `wasm32` runtime has no sockets, so a `fetch`-based transport
42//! is the *only* way to reach a database there.
43//!
44//! ```ignore
45//! use rainier_drivers::sql::SeaOrmExecutor;   // native: preferred
46//! use rainier_drivers::sql::D1Executor;       // wasm: HTTP transport
47//! ```
48//!
49//! ```ignore
50//! use rainier_orm::{Entity, repo};
51//!
52//! #[derive(Entity)]
53//! #[orm(table = "users")]
54//! struct User {
55//!     #[orm(pk, auto_increment)]
56//!     id: u64,
57//!     #[orm(unique)]
58//!     email: String,
59//!     timezone: Option<String>,
60//! }
61//!
62//! # async fn run(db: &impl rainier_orm::Executor) -> rainier_orm::Result<()> {
63//! let id = repo::insert(db, &User { id: 0, email: "a@b.c".into(), timezone: None }).await?;
64//! let u: Option<User> = repo::find_by_pk(db, id).await?;
65//! let by_email: Option<User> = repo::find_one_by(db, "email", "a@b.c").await?;
66//! # Ok(()) }
67//! ```
68//!
69//! Beyond CRUD: a fluent query builder ([`repo::query`] — predicates, joins,
70//! `first_or_create`, partial [`update`](query::Query::update) / atomic
71//! [`increment`](query::Query::increment)), [`repo::upsert`], a keyset
72//! [`Cursor`](repo::Cursor), derived constraints + DDL ([`schema`]), and column
73//! types past the primitives: [`Json<T>`](Json) (serde-as-text),
74//! [`Base64Bytes`] (uniform binary), and string-backed enums via
75//! [`impl_string_column!`].
76
77// Lets `#[derive(Entity)]` (which emits `::rainier_orm::…` paths) be used
78// *within* this crate — e.g. the migration-tracking entity in `migrate`.
79extern crate self as rainier_orm;
80
81pub use rainier_orm_macros::Entity;
82// The factory derive expands to a `rainier_database` path, so it is only
83// usable by something that depends on that crate — re-exported here so one
84// `use` reaches both derives.
85pub use rainier_orm_macros::Factory;
86
87// Re-export sea-query so the generated code (and callers building custom
88// queries) reference one version.
89pub use sea_query;
90
91pub mod active;
92pub mod blueprint;
93mod bytes;
94mod column;
95pub mod ddl;
96mod dialect;
97mod entity;
98mod executor;
99mod json;
100pub mod migrate;
101pub mod pool;
102pub mod query;
103pub mod repo;
104mod route;
105mod row;
106pub mod schema;
107pub mod shard;
108pub mod sharding;
109pub mod string_column;
110
111pub use active::Tracked;
112pub use blueprint::{Action, Blueprint, Column, ColumnKind, IndexDef, TableChanges};
113pub use bytes::Base64Bytes;
114pub use column::{ColumnSpec, ColumnType, ForeignKeySpec, IndexSpec, RefAction, SqlType};
115pub use json::Json;
116pub use route::{stable_hash, ShardCodec, ShardRoute};
117pub use shard::{
118    directory_db_name, shard_db_name, HashLocator, MapCatalog, ShardCatalog, ShardId, ShardLocator,
119    ShardedExecutor, SlotLocator,
120};
121// The shard-encoded id allocator and the connector's sharding config are the
122// most-used sharding-policy types and pair with `ShardCodec`; the rest of the
123// toolkit stays under `sharding::`.
124pub use dialect::Dialect;
125pub use entity::Entity as EntityTrait; // avoid clashing with the derive name
126pub use entity::Entity;
127pub use executor::{ExecOutcome, Executor};
128pub use pool::PoolConfig;
129pub use query::Query;
130pub use row::{FromColumn, Row, ToColumn};
131pub use sharding::{IdAllocator, ShardingSettings};
132pub use string_column::StringColumn;
133
134/// The crate error type. `anyhow` keeps the surface small and wasm-safe; a
135/// typed error is a later tightening.
136pub type Error = anyhow::Error;
137pub type Result<T> = core::result::Result<T, Error>;