libsql_orm/lib.rs
1//! # turso-orm
2//!
3//! A powerful, async-first ORM for [Turso Database](https://github.com/tursodatabase) with first-class support for **Cloudflare Workers** and WebAssembly environments.
4//!
5//! ## ✨ Features
6//!
7//! - 🚀 **Cloudflare Workers Ready** - Built specifically for edge computing environments
8//! - 🔄 **Async/Await Support** - Fully async API with excellent performance
9//! - 🎯 **Type-Safe** - Leverages Rust's type system for compile-time safety
10//! - 📊 **Rich Query Builder** - Fluent API for complex queries
11//! - 🔍 **Advanced Filtering** - Search, pagination, sorting, and aggregations
12//! - 🛠️ **Migration System** - Database schema management and versioning
13//! - 🎨 **Derive Macros** - Automatic model generation with `#[derive(Model)]`
14//! - 📦 **Bulk Operations** - Efficient batch inserts, updates, and deletes
15//! - 🌐 **WASM Compatible** - Optimized for WebAssembly targets
16//! - 🔄 **Type Conversion** - Automatic conversion between SQLite and Rust types
17//! - 🔄 **Upsert Operations** - Smart create_or_update and upsert methods
18//! - 📝 **Built-in Logging** - Comprehensive logging for debugging and monitoring
19//!
20//! ## 🚀 Quick Start
21//!
22//! ```rust
23//! use libsql_orm::{Model, Database};
24//! use serde::{Deserialize, Serialize};
25//! use chrono::{DateTime, Utc};
26//!
27//! #[derive(Model, Debug, Clone, Serialize, Deserialize)]
28//! struct User {
29//! pub id: Option<i64>,
30//! pub name: String,
31//! pub email: String,
32//! pub age: Option<i32>,
33//! pub is_active: bool,
34//! pub created_at: DateTime<Utc>,
35//! }
36//!
37//! async fn example() -> Result<(), Box<dyn std::error::Error>> {
38//! // Connect to database
39//! let db = Database::new_connect("turso://your-db.turso.io", "your-auth-token").await?;
40//!
41//! // Create a user
42//! let user = User {
43//! id: None,
44//! name: "Alice".to_string(),
45//! email: "alice@example.com".to_string(),
46//! age: Some(30),
47//! is_active: true,
48//! created_at: Utc::now(),
49//! };
50//!
51//! // Save to database
52//! let saved_user = user.create(&db).await?;
53//! println!("Created user with ID: [MASKED]");
54//!
55//! // Find users
56//! let users = User::find_all(&db).await?;
57//! println!("Found {} users", users.len());
58//!
59//! // Use smart upsert operations
60//! let user_with_id = User {
61//! id: Some(123), // Will update if exists, create if not
62//! name: "Updated Name".to_string(),
63//! email: "updated@example.com".to_string(),
64//! age: Some(31),
65//! is_active: true,
66//! created_at: Utc::now(),
67//! };
68//! let smart_saved = user_with_id.create_or_update(&db).await?;
69//!
70//! // Upsert by unique constraint
71//! let unique_user = User {
72//! id: None,
73//! name: "Unique User".to_string(),
74//! email: "unique@example.com".to_string(), // Check by email
75//! age: Some(25),
76//! is_active: true,
77//! created_at: Utc::now(),
78//! };
79//! let upserted = unique_user.upsert(&["email"], &db).await?;
80//!
81//! Ok(())
82//! }
83//! ```
84//!
85//! ## 🔄 Upsert Operations
86//!
87//! Smart create-or-update operations for efficient data management:
88//!
89//! ```no_run
90//! use libsql_orm::{Model, Database, Result};
91//! # #[derive(libsql_orm::Model, Clone, serde::Serialize, serde::Deserialize)]
92//! # struct User { id: Option<i64>, name: String, email: String, username: Option<String> }
93//! # async fn example(db: &Database) -> Result<()> {
94//!
95//! // Method 1: create_or_update (based on primary key)
96//! let user = User { id: Some(123), name: "John".to_string(), email: "john@example.com".to_string(), username: None };
97//! let saved = user.create_or_update(db).await?; // Updates if ID exists, creates if not
98//!
99//! // Method 2: upsert (based on unique constraints)
100//! let user = User { id: None, email: "john@example.com".to_string(), name: "John".to_string(), username: None };
101//! let saved = user.upsert(&["email"], db).await?; // Updates if email exists, creates if not
102//!
103//! // Multiple unique constraints
104//! let user = User { id: None, email: "john@example.com".to_string(), name: "John".to_string(), username: Some("john123".to_string()) };
105//! let saved = user.upsert(&["email", "username"], db).await?;
106//! # Ok(())
107//! # }
108//! ```
109//!
110//! ## 📝 Built-in Logging
111//!
112//! Comprehensive logging for debugging and monitoring:
113//!
114//! ```no_run
115//! # use libsql_orm::{Model, Database, Result};
116//! # #[derive(libsql_orm::Model, Clone, serde::Serialize, serde::Deserialize)]
117//! # struct User { id: Option<i64>, name: String, email: String }
118//! # impl User { fn new(name: &str, email: &str) -> Self {
119//! # Self { id: None, name: name.to_string(), email: email.to_string() }
120//! # }}
121//! # async fn example(db: &Database) -> Result<()> {
122//! // All database operations are automatically logged
123//! // Logs appear in browser console (WASM) or standard logging (native)
124//!
125//! let user = User::new("John", "john@example.com");
126//!
127//! // Logs: [INFO] users: Creating record in table: users
128//! // Logs: [DEBUG] users: SQL: INSERT INTO users (...) VALUES (...)
129//! let saved = user.create(db).await?;
130//!
131//! // Logs: [DEBUG] users: Finding record by ID: 123
132//! let found = User::find_by_id(123, db).await?;
133//!
134//! // Logs: [INFO] users: Updating record with ID: 123
135//! let updated = found.unwrap().update(db).await?;
136//! # Ok(())
137//! # }
138//! ```
139//!
140//! ## 📚 Advanced Usage
141//!
142//! ### Custom Table Names and Boolean Type Safety
143//!
144//! ```rust
145//! use libsql_orm::{Model, orm_column, deserialize_bool};
146//! use serde::{Serialize, Deserialize};
147//!
148//! #[derive(Model, Debug, Clone, Serialize, Deserialize)]
149//! #[table_name("user_accounts")] // Custom table name
150//! struct User {
151//! #[orm_column(type = "INTEGER PRIMARY KEY AUTOINCREMENT")]
152//! pub id: Option<i64>,
153//!
154//! #[orm_column(not_null, unique)]
155//! pub email: String,
156//!
157//! pub is_active: bool, // ✅ Automatic SQLite integer ↔ Rust bool conversion
158//! pub is_verified: bool, // ✅ Type-safe boolean operations
159//!
160//! // For edge cases, use custom deserializer
161//! #[serde(deserialize_with = "deserialize_bool")]
162//! pub has_premium: bool, // ✅ Manual boolean conversion
163//!
164//! #[orm_column(type = "TEXT DEFAULT 'active'")]
165//! pub status: String,
166//! }
167//!
168//! # use libsql_orm::{FilterOperator, Filter, Database, Result};
169//! # async fn example(db: &Database) -> Result<()> {
170//! // Boolean filtering works seamlessly
171//! let active_users = User::find_where(
172//! FilterOperator::Single(Filter::eq("is_active", true)),
173//! db
174//! ).await?;
175//! # Ok(())
176//! # }
177//! ```
178//!
179//! ### Query Builder
180//!
181//! ```rust
182//! use libsql_orm::{QueryBuilder, FilterOperator, Filter, Sort, SortOrder, Value, Result};
183//!
184//! # fn example() -> Result<()> {
185//! // Complex query with filtering and pagination
186//! let query = QueryBuilder::new("users")
187//! .select(vec!["id", "name", "email"])
188//! .r#where(FilterOperator::Single(Filter::ge("age", 18i64)))
189//! .order_by(Sort::new("created_at", SortOrder::Desc))
190//! .limit(10)
191//! .offset(20);
192//!
193//! let (sql, params) = query.build()?;
194//! # Ok(())
195//! # }
196//! ```
197//!
198//! ### Cloudflare Workers Integration
199//!
200//! ```ignore
201//! use worker::*;
202//! use libsql_orm::{Model, Database};
203//!
204//! # #[derive(libsql_orm::Model, Clone, serde::Serialize, serde::Deserialize)]
205//! # struct User { id: Option<i64>, name: String }
206//!
207//! #[event(fetch)]
208//! async fn fetch(req: Request, env: Env, _ctx: Context) -> Result<Response> {
209//! let database_url = env.var("TURSO_DATABASE_URL")?.to_string();
210//! let auth_token = env.var("TURSO_AUTH_TOKEN")?.to_string();
211//!
212//! let db = Database::new_connect(&database_url, &auth_token).await
213//! .map_err(|e| format!("Database connection failed: {}", e))?;
214//!
215//! // Your application logic here
216//! let users = User::find_all(&db).await
217//! .map_err(|e| format!("Query failed: {}", e))?;
218//!
219//! Response::from_json(&users)
220//! }
221//! ```
222pub mod compat;
223pub mod database;
224pub mod error;
225pub mod filters;
226pub mod macros;
227pub mod migrations;
228pub mod model;
229pub mod pagination;
230pub mod query;
231pub mod types;
232
233#[cfg(test)]
234mod tests;
235
236pub use database::Database;
237pub use error::{Error, Result};
238pub use filters::{Filter, FilterOperator, SearchFilter, Sort};
239pub use migrations::{templates, Migration, MigrationBuilder, MigrationManager};
240pub use model::Model;
241pub use pagination::{CursorPaginatedResult, CursorPagination, PaginatedResult, Pagination};
242pub use query::{QueryBuilder, QueryResult};
243pub use types::*;
244
245// Export the boolean deserializer
246pub use types::deserialize_bool;
247
248// Re-export commonly used types
249pub use chrono;
250pub use serde::{Deserialize, Serialize};
251pub use uuid::Uuid;
252
253/// Re-export the Model macro for convenience
254pub use libsql_orm_macros::{generate_migration, orm_column, Model};