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};