turso_orm/lib.rs
1//! ORM: an async object-relational mapper dedicated to the [Turso] database.
2//!
3//! The public API follows the entity, active model and query builder shape
4//! familiar from the Rust ORM ecosystem, but this is a from-scratch,
5//! single-engine stack: there is no backend abstraction and no SQL dialect
6//! switch. Targeting one engine lets the crate lean on SQLite semantics
7//! directly — `INTEGER PRIMARY KEY` row ids,
8//! `RETURNING *`, `pragma_table_info` — instead of papering over differences.
9//!
10//! The crate sits on two siblings it re-exports rather than wraps:
11//!
12//! - [`turso_orm_driver`] owns connections, pooling, transactions and row
13//! decoding; it is re-exported as [`Database`], [`ConnectionTrait`],
14//! [`Transaction`] and friends.
15//! - [`turso_sql`] owns the SQL builders and the [`Value`] type; it is
16//! re-exported as [`sql`] together with the pieces entity code needs
17//! ([`Expr`], [`Condition`], [`Func`], [`Order`], [`Statement`]).
18//!
19//! What this crate adds is the entity layer ([`entity`]) — the traits a
20//! `Model` struct, its `Column` and `PrimaryKey` enums and its `ActiveModel`
21//! implement — and the typed query builders ([`query`]) that turn them into
22//! statements. The derive macros that generate those impls live in
23//! `turso_orm_macros` and are re-exported behind the `macros` feature.
24//!
25//! # Design decisions
26//!
27//! - Generated `Column` enums deliberately do not derive `PartialEq`, so that
28//! `Column::X.eq(v)` resolves to the [`entity::ColumnTrait`] condition
29//! builder instead of the `PartialEq` method.
30//! - Every [`entity::ColumnTrait`] builder emits a qualified `table.column`
31//! reference, so conditions written against one entity never clash with a
32//! joined table that has a column of the same name.
33//! - [`entity::ModelTrait::set`] and [`entity::ActiveModelTrait::set`] return
34//! a [`Result`] instead of panicking on a value of the wrong type.
35//! - A relation is plain data ([`entity::RelationDef`]): the same value
36//! renders the join, drives the loaders and emits the foreign key. A
37//! many-to-many relation is two of them ([`entity::Related::via`]), a
38//! [`entity::Linked`] chain any number, and a table joined twice is
39//! aliased by [`query::Select`] rather than by the caller. Rust allows one
40//! `Related<Target>` impl per entity, so a second relation to the same
41//! table is used through its definition.
42//! - The `__private` module exists for generated code only; nothing in it is
43//! part of the public API.
44//!
45//! # Example
46//!
47//! ```ignore
48//! use turso_orm::prelude::*;
49//!
50//! #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
51//! #[turso(table_name = "user")]
52//! pub struct Model {
53//! #[turso(primary_key)]
54//! pub id: i32,
55//! #[turso(unique)]
56//! pub email: String,
57//! pub name: Option<String>,
58//! }
59//!
60//! #[derive(Copy, Clone, Debug, DeriveRelation)]
61//! pub enum Relation {}
62//!
63//! impl ActiveModelBehavior for ActiveModel {}
64//!
65//! # async fn run() -> Result<(), DbErr> {
66//! let db = Database::connect(ConnectOptions::in_memory()).await?;
67//! db.execute(Schema::new().create_table_from_entity(Entity).to_statement()).await?;
68//! let user = ActiveModel { email: Set("a@b.c".into()), ..Default::default() }.insert(&db).await?;
69//! let found = Entity::find_by_id(user.id).one(&db).await?;
70//! # Ok(())
71//! # }
72//! ```
73//!
74//! [Turso]: https://github.com/tursodatabase/turso
75
76#![cfg_attr(docsrs, feature(doc_cfg))]
77
78pub mod entity;
79mod error;
80pub mod query;
81pub mod types;
82
83pub use entity::Schema;
84pub use error::{DbErr, Result};
85
86/// Items the derive macros expand to; not part of the public API.
87///
88/// Generated code refers to these through `::turso_orm::__private::...` so
89/// that the helpers stay out of the documented surface and can change
90/// without a semver bump.
91#[doc(hidden)]
92pub mod __private {
93 pub use crate::entity::active_model::decode_field;
94 pub use crate::entity::iden::Static;
95 pub use crate::entity::model::get_field;
96 pub use async_trait::async_trait;
97 pub use turso_orm_driver::{Error as DriverError, FromValue, Result as DriverResult, Row};
98 pub use turso_sql::{Ident, IntoIden};
99}
100pub use turso_orm_driver::{
101 ConnectOptions, ConnectionTrait, Database, ExecResult, Row, StreamTrait, Transaction,
102 TransactionMode, TransactionTrait,
103};
104pub use turso_sql::{
105 self as sql, Build, Condition, Expr, Func, IntoCondition, JoinType, Order, Statement, Value,
106};
107
108/// Alias of [`Database`] for code that prefers the longer name.
109pub type DatabaseConnection = Database;
110
111#[cfg(feature = "macros")]
112#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
113pub use turso_orm_macros::{
114 DeriveActiveEnum, DeriveEntityModel, DeriveIden, DeriveIntoActiveModel, DerivePartialModel,
115 DeriveRelation, FromQueryResult,
116};
117
118/// Everything an entity module needs, meant to be glob-imported.
119///
120/// The prelude gathers the entity traits, the query builders, the connection
121/// types and the SQL building blocks, plus feature-gated aliases for the
122/// column types that come from an external crate (`chrono`, `uuid`,
123/// `serde_json`, `rust_decimal`).
124pub mod prelude {
125 pub use crate::entity::NotSet;
126 pub use crate::entity::{
127 ActiveEnum, ActiveModelBehavior, ActiveModelTrait, ActiveValue, ColumnTrait, EntityTrait,
128 FromQueryResult, IntoActiveModel, IntoActiveValue, Linked, LoaderTrait, ModelTrait,
129 PartialModelTrait, PrimaryKeyTrait, Related, RelationDef, RelationTrait, Schema, Set,
130 TryIntoModel, Unchanged,
131 };
132 pub use crate::query::{Cursor, Paginator, Select, SelectTwo, SelectTwoMany};
133 pub use crate::types::TursoType;
134 pub use crate::{
135 Build, Condition, ConnectOptions, ConnectionTrait, Database, DatabaseConnection, DbErr,
136 Expr, Func, Order, Statement, StreamTrait, Transaction, TransactionMode, TransactionTrait,
137 Value,
138 };
139 // The derive shares its name with the trait of the same purpose; Rust keeps
140 // macros in their own namespace, so both can be glob-imported together.
141 #[cfg(feature = "macros")]
142 pub use crate::{
143 DeriveActiveEnum, DeriveEntityModel, DeriveIden, DeriveIntoActiveModel, DerivePartialModel,
144 DeriveRelation, FromQueryResult,
145 };
146 pub use turso_sql::prelude::{ColumnType, ForeignKeyAction, JoinType};
147
148 /// A naive timestamp without time zone.
149 #[cfg(feature = "with-chrono")]
150 pub type DateTime = chrono::NaiveDateTime;
151 /// A timestamp in UTC.
152 #[cfg(feature = "with-chrono")]
153 pub type DateTimeUtc = chrono::DateTime<chrono::Utc>;
154 /// A timestamp with a fixed offset.
155 #[cfg(feature = "with-chrono")]
156 pub type DateTimeWithTimeZone = chrono::DateTime<chrono::FixedOffset>;
157 /// A calendar date.
158 #[cfg(feature = "with-chrono")]
159 pub type Date = chrono::NaiveDate;
160 /// A time of day.
161 #[cfg(feature = "with-chrono")]
162 pub type Time = chrono::NaiveTime;
163 /// A JSON document.
164 #[cfg(feature = "with-json")]
165 pub type Json = serde_json::Value;
166 /// A UUID.
167 #[cfg(feature = "with-uuid")]
168 pub type Uuid = uuid::Uuid;
169 /// An exact decimal number.
170 #[cfg(feature = "with-rust_decimal")]
171 pub type Decimal = rust_decimal::Decimal;
172}