prax-orm 0.6.5

A next-generation, type-safe ORM for Rust inspired by Prisma
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
//! # Prax - A Next-Generation ORM for Rust
//!
//! Prax is a type-safe, async-first Object-Relational Mapper (ORM) for Rust,
//! inspired by Prisma. It provides a delightful developer experience with
//! compile-time guarantees and a powerful schema definition language.
//!
//! ## Features
//!
//! - **Schema Definition Language**: Define your data models in a clear, readable `.prax` file
//! - **Type-Safe Queries**: Compile-time checked queries with a fluent builder API
//! - **Async-First**: Built on Tokio for high-performance async database operations
//! - **Multi-Database Support**: PostgreSQL, MySQL, SQLite, and MongoDB
//! - **Automatic Migrations**: Generate and apply database migrations from your schema
//! - **Relations**: Define and query relationships between models with ease
//! - **Transactions**: Full transaction support with savepoints and isolation levels
//! - **Middleware System**: Extensible query interception for logging, metrics, and more
//! - **Multi-Tenant Support**: Built-in support for multi-tenant applications
//!
//! ## Quick Start
//!
//! ### 1. Define Your Schema
//!
//! Create a `prax/schema.prax` file in your project:
//!
//! ```text
//! // Models define your database tables
//! model User {
//!     id        Int      @id @auto
//!     email     String   @unique
//!     name      String?
//!     posts     Post[]
//!     createdAt DateTime @default(now())
//! }
//!
//! model Post {
//!     id        Int      @id @auto
//!     title     String
//!     content   String?  @db.Text
//!     published Boolean  @default(false)
//!     authorId  Int
//!     author    User     @relation(fields: [authorId], references: [id])
//! }
//! ```
//!
//! ### 2. Configure Your Database
//!
//! Create a `prax.toml` configuration file in your project root:
//!
//! ```toml
//! [database]
//! provider = "postgresql"
//! url = "${DATABASE_URL}"
//!
//! [database.pool]
//! max_connections = 10
//!
//! [generator.client]
//! output = "./src/generated"
//! ```
//!
//! ### 3. Use in Your Application
//!
//! ```rust,ignore
//! use prax::prelude::*;
//!
//! #[tokio::main]
//! async fn main() -> Result<(), prax::SchemaError> {
//!     // Initialize the client (in real usage, generated from schema)
//!     let client = PraxClient::new("postgresql://localhost/mydb").await?;
//!
//!     // Create a new user
//!     let user = client
//!         .user()
//!         .create(CreateData::new()
//!             .set("email", "alice@example.com")
//!             .set("name", "Alice"))
//!         .exec()
//!         .await?;
//!
//!     // Find users with filtering
//!     let users = client
//!         .user()
//!         .find_many()
//!         .r#where(user::email::contains("@example.com"))
//!         .order_by(user::createdAt::desc())
//!         .take(10)
//!         .exec()
//!         .await?;
//!
//!     // Update a user
//!     let updated = client
//!         .user()
//!         .update()
//!         .r#where(user::id::equals(1))
//!         .data(UpdateData::new().set("name", "Alice Smith"))
//!         .exec()
//!         .await?;
//!
//!     // Delete a user
//!     client
//!         .user()
//!         .delete()
//!         .r#where(user::id::equals(1))
//!         .exec()
//!         .await?;
//!
//!     Ok(())
//! }
//! ```
//!
//! ## Schema Language
//!
//! The Prax schema language provides a powerful way to define your data models:
//!
//! ### Models
//!
//! Models represent database tables:
//!
//! ```text
//! model User {
//!     id    Int    @id @auto     // Primary key with auto-increment
//!     email String @unique       // Unique constraint
//!     name  String?              // Optional (nullable) field
//! }
//! ```
//!
//! ### Field Types
//!
//! | Type       | Description                    | Database Mapping  |
//! |------------|--------------------------------|-------------------|
//! | `Int`      | Integer                        | INTEGER/INT       |
//! | `BigInt`   | 64-bit integer                 | BIGINT            |
//! | `Float`    | Floating point                 | FLOAT/DOUBLE      |
//! | `Decimal`  | Exact decimal                  | DECIMAL/NUMERIC   |
//! | `String`   | Text                           | VARCHAR/TEXT      |
//! | `Boolean`  | True/false                     | BOOLEAN           |
//! | `DateTime` | Date and time                  | TIMESTAMP         |
//! | `Date`     | Date only                      | DATE              |
//! | `Time`     | Time only                      | TIME              |
//! | `Json`     | JSON data                      | JSON/JSONB        |
//! | `Bytes`    | Binary data                    | BYTEA/BLOB        |
//! | `Uuid`     | UUID                           | UUID              |
//!
//! ### Field Attributes
//!
//! | Attribute              | Description                              |
//! |------------------------|------------------------------------------|
//! | `@id`                  | Primary key                              |
//! | `@auto`                | Auto-increment/generate                  |
//! | `@unique`              | Unique constraint                        |
//! | `@default(value)`      | Default value                            |
//! | `@map("name")`         | Custom column name                       |
//! | `@db.Type`             | Database-specific type                   |
//! | `@index`               | Create index on field                    |
//! | `@updated_at`          | Auto-update timestamp                    |
//! | `@relation(...)`       | Define relation                          |
//!
//! ### Relations
//!
//! Define relationships between models:
//!
//! ```text
//! model User {
//!     id    Int    @id @auto
//!     posts Post[]                    // One-to-many
//! }
//!
//! model Post {
//!     id       Int  @id @auto
//!     authorId Int
//!     author   User @relation(fields: [authorId], references: [id])
//! }
//! ```
//!
//! ### Enums
//!
//! Define enumerated types:
//!
//! ```text
//! enum Role {
//!     User
//!     Admin
//!     Moderator
//! }
//!
//! model User {
//!     id   Int  @id @auto
//!     role Role @default(User)
//! }
//! ```
//!
//! ## Query API
//!
//! ### Finding Records
//!
//! ```rust,ignore
//! // Find many with filters
//! let users = client
//!     .user()
//!     .find_many()
//!     .r#where(user::email::contains("@example.com"))
//!     .r#where(user::createdAt::gt(some_date))
//!     .order_by(user::name::asc())
//!     .skip(10)
//!     .take(20)
//!     .exec()
//!     .await?;
//!
//! // Find unique record
//! let user = client
//!     .user()
//!     .find_unique()
//!     .r#where(user::id::equals(1))
//!     .exec()
//!     .await?;
//!
//! // Find first matching
//! let user = client
//!     .user()
//!     .find_first()
//!     .r#where(user::email::ends_with("@company.com"))
//!     .exec()
//!     .await?;
//! ```
//!
//! ### Filter Operations
//!
//! ```rust,ignore
//! // Equality
//! user::email::equals("test@example.com")
//!
//! // Comparison
//! user::age::gt(18)
//! user::age::gte(18)
//! user::age::lt(65)
//! user::age::lte(65)
//!
//! // String operations
//! user::name::contains("john")
//! user::name::starts_with("Dr.")
//! user::name::ends_with("Smith")
//!
//! // List operations
//! user::status::in_list(vec!["active", "pending"])
//! user::status::not_in(vec!["banned"])
//!
//! // Null checks
//! user::deleted_at::is_null()
//! user::verified_at::is_not_null()
//!
//! // Combining filters
//! Filter::and(vec![
//!     user::active::equals(true),
//!     user::email::contains("@company.com"),
//! ])
//!
//! Filter::or(vec![
//!     user::role::equals(Role::Admin),
//!     user::role::equals(Role::Moderator),
//! ])
//! ```
//!
//! ### Creating Records
//!
//! ```rust,ignore
//! // Single create
//! let user = client
//!     .user()
//!     .create(CreateData::new()
//!         .set("email", "new@example.com")
//!         .set("name", "New User"))
//!     .exec()
//!     .await?;
//!
//! // Create many
//! let count = client
//!     .user()
//!     .create_many(vec![
//!         CreateData::new().set("email", "user1@example.com"),
//!         CreateData::new().set("email", "user2@example.com"),
//!     ])
//!     .exec()
//!     .await?;
//!
//! // Create with nested relation
//! let user = client
//!     .user()
//!     .create(CreateData::new()
//!         .set("email", "author@example.com")
//!         .connect("posts", post::id::equals(1)))
//!     .exec()
//!     .await?;
//! ```
//!
//! ### Updating Records
//!
//! ```rust,ignore
//! // Update single
//! let user = client
//!     .user()
//!     .update()
//!     .r#where(user::id::equals(1))
//!     .data(UpdateData::new()
//!         .set("name", "Updated Name")
//!         .increment("login_count", 1))
//!     .exec()
//!     .await?;
//!
//! // Update many
//! let count = client
//!     .user()
//!     .update_many()
//!     .r#where(user::active::equals(false))
//!     .data(UpdateData::new().set("active", true))
//!     .exec()
//!     .await?;
//!
//! // Upsert (create or update)
//! let user = client
//!     .user()
//!     .upsert()
//!     .r#where(user::email::equals("test@example.com"))
//!     .create(CreateData::new().set("email", "test@example.com"))
//!     .update(UpdateData::new().set("login_count", 1))
//!     .exec()
//!     .await?;
//! ```
//!
//! ### Deleting Records
//!
//! ```rust,ignore
//! // Delete single
//! client
//!     .user()
//!     .delete()
//!     .r#where(user::id::equals(1))
//!     .exec()
//!     .await?;
//!
//! // Delete many
//! let count = client
//!     .user()
//!     .delete_many()
//!     .r#where(user::active::equals(false))
//!     .exec()
//!     .await?;
//! ```
//!
//! ### Including Relations
//!
//! ```rust,ignore
//! // Include related records
//! let user = client
//!     .user()
//!     .find_unique()
//!     .r#where(user::id::equals(1))
//!     .include(user::posts::include())
//!     .exec()
//!     .await?;
//!
//! // Nested includes
//! let post = client
//!     .post()
//!     .find_unique()
//!     .r#where(post::id::equals(1))
//!     .include(post::author::include(
//!         user::posts::include()
//!     ))
//!     .exec()
//!     .await?;
//!
//! // Select specific fields
//! let user = client
//!     .user()
//!     .find_unique()
//!     .r#where(user::id::equals(1))
//!     .select(user::select!(id, email, name))
//!     .exec()
//!     .await?;
//! ```
//!
//! ## Transactions
//!
//! ```rust,ignore
//! // Basic transaction
//! let result = client.transaction(|tx| async move {
//!     let user = tx.user()
//!         .create(CreateData::new().set("email", "tx@example.com"))
//!         .exec()
//!         .await?;
//!
//!     tx.post()
//!         .create(CreateData::new()
//!             .set("title", "My First Post")
//!             .set("authorId", user.id))
//!         .exec()
//!         .await?;
//!
//!     Ok(user)
//! }).await?;
//!
//! // Transaction with options
//! let result = client
//!     .transaction_with_config(TransactionConfig::new()
//!         .isolation(IsolationLevel::Serializable)
//!         .timeout(Duration::from_secs(30)))
//!     .run(|tx| async move {
//!         // ... operations
//!         Ok(())
//!     })
//!     .await?;
//! ```
//!
//! ## Raw SQL
//!
//! When you need to execute raw SQL:
//!
//! ```rust,ignore
//! use prax::raw_query;
//!
//! // Type-safe raw query
//! let users: Vec<User> = client
//!     .query_raw(raw_query!(
//!         "SELECT * FROM users WHERE email = {}",
//!         "test@example.com"
//!     ))
//!     .await?;
//!
//! // Execute raw SQL
//! let affected = client
//!     .execute_raw(raw_query!(
//!         "UPDATE users SET verified = true WHERE id = {}",
//!         user_id
//!     ))
//!     .await?;
//! ```
//!
//! ## Aggregations
//!
//! ```rust,ignore
//! // Count
//! let count = client
//!     .user()
//!     .count()
//!     .r#where(user::active::equals(true))
//!     .exec()
//!     .await?;
//!
//! // Aggregate functions
//! let stats = client
//!     .post()
//!     .aggregate()
//!     .avg(post::views)
//!     .sum(post::views)
//!     .min(post::created_at)
//!     .max(post::created_at)
//!     .exec()
//!     .await?;
//!
//! // Group by
//! let by_status = client
//!     .post()
//!     .group_by(post::status)
//!     .count()
//!     .having(count::gt(10))
//!     .exec()
//!     .await?;
//! ```
//!
//! ## Multi-Tenant Applications
//!
//! Prax provides built-in support for multi-tenant applications:
//!
//! ```rust,ignore
//! use prax::tenant::{TenantConfig, TenantMiddleware, IsolationStrategy};
//!
//! // Configure tenant isolation
//! let config = TenantConfig::builder()
//!     .strategy(IsolationStrategy::RowLevel {
//!         tenant_column: "tenant_id".into(),
//!     })
//!     .build();
//!
//! // Add tenant middleware
//! let client = client.with_middleware(TenantMiddleware::new(config));
//!
//! // Set tenant context for requests
//! let client = client.with_tenant("tenant-123");
//!
//! // All queries are automatically scoped to this tenant
//! let users = client.user().find_many().exec().await?;
//! ```
//!
//! ## Middleware
//!
//! Extend Prax with custom middleware:
//!
//! ```rust,ignore
//! use prax::middleware::{LoggingMiddleware, MetricsMiddleware, MiddlewareBuilder};
//!
//! let client = client.with_middleware(
//!     MiddlewareBuilder::new()
//!         .add(LoggingMiddleware::new())
//!         .add(MetricsMiddleware::new())
//!         .build()
//! );
//! ```
//!
//! ## CLI Commands
//!
//! Prax provides a CLI for common operations:
//!
//! ```bash
//! # Initialize a new project
//! prax init
//!
//! # Generate client code from schema
//! prax generate
//!
//! # Create a new migration
//! prax migrate create "add_users_table"
//!
//! # Apply pending migrations
//! prax migrate deploy
//!
//! # Reset database
//! prax migrate reset
//!
//! # Validate schema
//! prax validate
//!
//! # Format schema files
//! prax format
//! ```
//!
//! ## Crate Features
//!
//! Enable features in your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! prax = { version = "0.1", features = ["postgres", "mysql", "sqlite"] }
//! ```
//!
//! | Feature    | Description                                    |
//! |------------|------------------------------------------------|
//! | `postgres` | PostgreSQL support via `tokio-postgres`        |
//! | `mysql`    | MySQL support via `mysql_async`                |
//! | `sqlite`   | SQLite support via `tokio-rusqlite`            |
//! | `mongodb`  | MongoDB support                                |
//! | `sqlx`     | Alternative backend with compile-time checks   |
//! | `tracing`  | Integration with the `tracing` crate           |
//! | `serde`    | Serialization support for schema types         |
//!
//! ## Error Handling
//!
//! Prax provides detailed error types with actionable messages:
//!
//! ```rust,ignore
//! use prax::error::{QueryError, ErrorCode};
//!
//! match client.user().find_unique().r#where(user::id::equals(1)).exec().await {
//!     Ok(user) => println!("Found: {:?}", user),
//!     Err(e) => {
//!         eprintln!("Error: {}", e);
//!         eprintln!("Code: {:?}", e.code());
//!         if let Some(suggestion) = e.suggestion() {
//!             eprintln!("Suggestion: {}", suggestion);
//!         }
//!     }
//! }
//! ```
//!
//! ## Performance
//!
//! Prax is designed for high performance:
//!
//! - **Connection Pooling**: Built-in connection pool with configurable limits
//! - **Prepared Statement Caching**: Automatic caching of prepared statements
//! - **Batch Operations**: Efficient bulk create, update, and delete
//! - **Lazy Loading**: Relations are loaded only when requested
//!
//! ## Further Reading
//!
//! - [Schema Documentation](schema/index.html)
//! - [Query API](query/index.html)
//! - [Configuration](config/index.html)
//! - [Migration Guide](migrate/index.html)
//! - [Examples](https://github.com/pegasusheavy/prax-orm/tree/main/examples)

#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]

/// Schema parsing and AST types.
///
/// This module provides everything needed to work with Prax schema files:
///
/// - [`schema::parse_schema`] - Parse a schema string
/// - [`schema::parse_schema_file`] - Parse a schema from a file
/// - [`schema::validate_schema`] - Parse and validate a schema
/// - [`schema::PraxConfig`] - Configuration from `prax.toml`
/// - [`schema::Schema`] - The parsed schema representation
///
/// ## Example
///
/// ```rust,ignore
/// use prax::schema::{parse_schema, validate_schema, PraxConfig};
///
/// // Parse a schema
/// let schema = parse_schema(r#"
///     model User {
///         id    Int    @id @auto
///         email String @unique
///     }
/// "#)?;
///
/// // Access schema information
/// println!("Models: {:?}", schema.model_names().collect::<Vec<_>>());
///
/// // Load configuration
/// let config = PraxConfig::from_file("prax.toml")?;
/// println!("Database: {}", config.database.provider);
/// ```
pub mod schema {
    pub use prax_schema::*;
}

// Re-export proc macros
pub use prax_codegen::Model;
pub use prax_codegen::prax_schema;

/// Prelude module for convenient imports.
///
/// Import everything you need with a single line:
///
/// ```rust,ignore
/// use prax::prelude::*;
/// ```
///
/// This includes:
/// - Schema parsing functions
/// - Configuration types
/// - Common traits and types
pub mod prelude {
    pub use crate::schema::{PraxConfig, Schema, parse_schema, parse_schema_file};
    pub use crate::{Model, prax_schema};
}

// Re-export key types at the crate root
pub use schema::{Schema, SchemaError};

/// Error types for the Prax ORM.
///
/// The main error type is [`SchemaError`] which covers all schema-related
/// errors including parsing, validation, and file I/O.
pub mod error {
    pub use prax_schema::SchemaError;
}