🦀 Database Overview in TuskLang for Rust

Rust Documentation

Database Overview in TuskLang for Rust

TuskLang provides a powerful and type-safe database abstraction layer for Rust applications, leveraging Rust's memory safety, zero-cost abstractions, and async/await capabilities to create a revolutionary database experience.

🚀 Why Rust + TuskLang Database Integration?

Rust's ownership system, zero-cost abstractions, and compile-time guarantees make it the perfect language for database operations. TuskLang's Rust integration provides:

- Memory Safety: No null pointer dereferences or data races - Type Safety: Compile-time validation of database schemas - Performance: Zero-cost abstractions with native performance - Async/Await: Non-blocking database operations - Error Handling: Comprehensive error types with Result<T, E>

Database Connection Configuration

// Database configuration in config.tsk
config: {
    database: {
        default: "postgres"
        connections: {
            postgres: {
                driver: "postgres"
                host: @env("DB_HOST", "localhost")
                port: @env("DB_PORT", 5432)
                database: @env("DB_DATABASE", "myapp")
                username: @env("DB_USERNAME", "postgres")
                password: @env("DB_PASSWORD", "")
                ssl_mode: "prefer"
                max_connections: 10
                min_connections: 2
                connect_timeout: 30
                idle_timeout: 300
            }
            
            mysql: {
                driver: "mysql"
                host: @env("MYSQL_HOST", "localhost")
                port: @env("MYSQL_PORT", 3306)
                database: @env("MYSQL_DATABASE", "myapp")
                username: @env("MYSQL_USERNAME", "root")
                password: @env("MYSQL_PASSWORD", "")
                charset: "utf8mb4"
                collation: "utf8mb4_unicode_ci"
                max_connections: 10
            }
            
            sqlite: {
                driver: "sqlite"
                database: @env("SQLITE_DATABASE", "database/database.sqlite")
                foreign_key_constraints: true
                journal_mode: "WAL"
                synchronous: "NORMAL"
            }
        }
    }
}

Basic Database Operations with Rust

use tusk_db::{Database, QueryBuilder, Result};
use serde::{Deserialize, Serialize};

// Direct database queries with type safety #[derive(Debug, Serialize, Deserialize)] struct User { id: i32, name: String, email: String, active: bool, }

// Async database operations async fn get_users() -> Result<Vec<User>> { let users = @db.query::<User>( "SELECT * FROM users WHERE active = ?", &[&true] ).await?; Ok(users) }

// Insert data with type validation async fn create_user(name: &str, email: &str) -> Result<i32> { let user_id = @db.insert( "INSERT INTO users (name, email, active) VALUES (?, ?, ?)", &[&name, &email, &true] ).await?; Ok(user_id) }

// Update data with error handling async fn update_user_last_login(user_id: i32) -> Result<u64> { let affected = @db.update( "UPDATE users SET last_login = ? WHERE id = ?", &[&chrono::Utc::now(), &user_id] ).await?; Ok(affected) }

// Delete data with transaction safety async fn delete_expired_sessions() -> Result<u64> { let deleted = @db.delete( "DELETE FROM sessions WHERE expired_at < ?", &[&chrono::Utc::now()] ).await?; Ok(deleted) }

Advanced Transaction Handling

use tusk_db::{Transaction, DatabaseError};

// Complex transaction with rollback on error async fn process_order(user_id: i32, cart_items: Vec<CartItem>) -> Result<Order> { let mut tx = @db.begin_transaction().await?; // Use try-catch pattern for automatic rollback let result = async { // Create order let order_id = tx.insert( "INSERT INTO orders (user_id, total, status) VALUES (?, ?, ?)", &[&user_id, &calculate_total(&cart_items), &"pending"] ).await?; // Add order items for item in &cart_items { tx.insert( "INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (?, ?, ?, ?)", &[&order_id, &item.product_id, &item.quantity, &item.price] ).await?; // Update inventory atomically tx.update( "UPDATE inventory SET stock = stock - ? WHERE product_id = ? AND stock >= ?", &[&item.quantity, &item.product_id, &item.quantity] ).await?; } // Update order status tx.update( "UPDATE orders SET status = ? WHERE id = ?", &[&"completed", &order_id] ).await?; // Fetch the complete order let order = tx.query_one::<Order>( "SELECT * FROM orders WHERE id = ?", &[&order_id] ).await?; Ok(order) }.await; match result { Ok(order) => { tx.commit().await?; Ok(order) } Err(e) => { tx.rollback().await?; Err(e) } } }

Type-Safe Query Builder

use tusk_db::{QueryBuilder, WhereClause, JoinType};

// Fluent query builder with compile-time type checking async fn get_active_users() -> Result<Vec<User>> { let users = @db.table::<User>("users") .where_eq("active", true) .where_gt("created_at", chrono::Utc::now() - chrono::Duration::days(30)) .order_by("name", "ASC") .limit(10) .get() .await?; Ok(users) }

// Complex queries with joins and aggregates #[derive(Debug, Serialize, Deserialize)] struct PostWithAuthor { id: i32, title: String, content: String, author_name: String, comment_count: i64, }

async fn get_featured_posts() -> Result<Vec<PostWithAuthor>> { let posts = @db.table::<PostWithAuthor>("posts") .select(&["posts.*", "users.name as author_name", "COUNT(comments.id) as comment_count"]) .join("users", "posts.user_id", "=", "users.id") .left_join("comments", "posts.id", "=", "comments.post_id") .where_eq("posts.published", true) .where_in("posts.status", &["active", "featured"]) .where_between("posts.created_at", &[start_date, end_date]) .group_by("posts.id, users.name") .having("COUNT(comments.id)", ">", 5) .order_by("posts.created_at", "DESC") .paginate(20) .get() .await?; Ok(posts) }

// Aggregates with type safety async fn get_user_statistics() -> Result<UserStats> { let stats = @db.table("users") .select(&[ "COUNT(*) as total_users", "AVG(age) as avg_age", "MAX(last_login) as last_activity", "SUM(CASE WHEN active = true THEN 1 ELSE 0 END) as active_users" ]) .get_one::<UserStats>() .await?; Ok(stats) }

Rust-Specific Model System

use tusk_db::{Model, ModelBuilder, Relationship};
use serde::{Deserialize, Serialize};
use async_trait::async_trait;

// Define a model with Rust traits #[derive(Debug, Serialize, Deserialize, Clone)] struct User { pub id: Option<i32>, pub name: String, pub email: String, pub password_hash: String, pub email_verified_at: Option<chrono::DateTime<Utc>>, pub is_active: bool, pub created_at: Option<chrono::DateTime<Utc>>, pub updated_at: Option<chrono::DateTime<Utc>>, }

#[async_trait] impl Model for User { fn table_name() -> &'static str { "users" } fn fillable_fields() -> &'static [&'static str] { &["name", "email", "password_hash", "is_active"] } fn hidden_fields() -> &'static [&'static str] { &["password_hash"] } // Relationships with type safety async fn posts(&self) -> Result<Vec<Post>> { @has_many::<Post>(self.id.unwrap(), "user_id").await } async fn profile(&self) -> Result<Option<UserProfile>> { @has_one::<UserProfile>(self.id.unwrap(), "user_id").await } async fn roles(&self) -> Result<Vec<Role>> { @belongs_to_many::<Role>(self.id.unwrap(), "user_roles", "user_id", "role_id").await } // Scopes for query building fn scope_active(query: QueryBuilder) -> QueryBuilder { query.where_eq("is_active", true) } fn scope_recent(query: QueryBuilder) -> QueryBuilder { query.where_gt("created_at", chrono::Utc::now() - chrono::Duration::days(30)) } }

// Using models with Rust patterns async fn user_operations() -> Result<()> { // Find user with error handling let user = match @User::find(1).await { Ok(user) => user, Err(DatabaseError::NotFound) => { eprintln!("User not found"); return Ok(()); } Err(e) => return Err(e.into()), }; // Query with scopes let active_users = @User::query() .scope_active() .scope_recent() .order_by("name") .get() .await?; // Create with validation let new_user = @User::create(User { id: None, name: "Jane Doe".to_string(), email: "jane@example.com".to_string(), password_hash: hash_password("secret")?, email_verified_at: None, is_active: true, created_at: None, updated_at: None, }).await?; // Update with optimistic locking let updated_user = @User::update(new_user.id.unwrap(), |user| { user.is_active = false; user }).await?; Ok(()) }

Database Migrations with Rust

use tusk_db::{Migration, Schema, ColumnType, IndexType};

// Type-safe migration system #[derive(Debug)] struct CreateUsersTable;

#[async_trait] impl Migration for CreateUsersTable { fn name() -> &'static str { "create_users_table" } async fn up(schema: &Schema) -> Result<()> { schema.create_table("users", |table| { table.id("id"); table.string("name", 255).not_null(); table.string("email", 255).unique().not_null(); table.timestamp("email_verified_at").nullable(); table.string("password_hash", 255).not_null(); table.boolean("is_active").default(true); table.timestamp("created_at").default_current(); table.timestamp("updated_at").default_current(); // Indexes for performance table.index(&["email"], IndexType::BTree); table.index(&["created_at"], IndexType::BTree); table.index(&["is_active", "created_at"], IndexType::BTree); }).await?; Ok(()) } async fn down(schema: &Schema) -> Result<()> { schema.drop_table_if_exists("users").await?; Ok(()) } }

// Migration runner with Rust async patterns async fn run_migrations() -> Result<()> { let migrations = vec![ Box::new(CreateUsersTable), Box::new(CreatePostsTable), Box::new(CreateCommentsTable), ]; for migration in migrations { @migrate::run(migration).await?; } Ok(()) }

Connection Pooling and Performance

use tusk_db::{ConnectionPool, PoolConfig};
use tokio::sync::Semaphore;

// Connection pooling with Rust async patterns async fn setup_database_pool() -> Result<ConnectionPool> { let config = PoolConfig { max_connections: 20, min_connections: 5, connect_timeout: std::time::Duration::from_secs(30), idle_timeout: std::time::Duration::from_secs(300), max_lifetime: std::time::Duration::from_secs(3600), }; let pool = @db::create_pool(config).await?; Ok(pool) }

// Concurrent database operations async fn process_users_concurrently(user_ids: Vec<i32>) -> Result<Vec<User>> { let semaphore = Semaphore::new(10); // Limit concurrent connections let futures: Vec<_> = user_ids.into_iter().map(|id| { let sem = semaphore.clone(); async move { let _permit = sem.acquire().await.unwrap(); @User::find(id).await } }).collect(); let results = futures::future::join_all(futures).await; // Handle results with error aggregation let mut users = Vec::new(); let mut errors = Vec::new(); for result in results { match result { Ok(user) => users.push(user), Err(e) => errors.push(e), } } if !errors.is_empty() { return Err(DatabaseError::BatchError(errors).into()); } Ok(users) }

Error Handling and Recovery

use tusk_db::{DatabaseError, ConnectionError, QueryError};
use thiserror::Error;

#[derive(Error, Debug)] pub enum AppError { #[error("Database connection failed: {0}")] Connection(#[from] ConnectionError), #[error("Query execution failed: {0}")] Query(#[from] QueryError), #[error("User not found: {id}")] UserNotFound { id: i32 }, #[error("Validation failed: {field}")] Validation { field: String }, }

// Comprehensive error handling async fn robust_user_operation(user_id: i32) -> Result<User, AppError> { // Retry logic with exponential backoff let mut attempts = 0; let max_attempts = 3; loop { match @User::find(user_id).await { Ok(user) => return Ok(user), Err(DatabaseError::NotFound) => { return Err(AppError::UserNotFound { id: user_id }); } Err(DatabaseError::Connection(e)) if attempts < max_attempts => { attempts += 1; let delay = std::time::Duration::from_secs(2_u64.pow(attempts)); tokio::time::sleep(delay).await; continue; } Err(e) => return Err(e.into()), } } }

Testing Database Operations

use tusk_db::test_utils::{TestDatabase, TestTransaction};
use tokio_test::assert_ok;

// Database testing with Rust async testing #[tokio::test] async fn test_user_creation() -> Result<()> { let test_db = @TestDatabase::new().await?; let mut tx = test_db.begin_transaction().await?; // Test user creation let user = @User::create(User { id: None, name: "Test User".to_string(), email: "test@example.com".to_string(), password_hash: "hashed_password".to_string(), email_verified_at: None, is_active: true, created_at: None, updated_at: None, }).await?; assert!(user.id.is_some()); assert_eq!(user.name, "Test User"); assert_eq!(user.email, "test@example.com"); // Verify in database let found_user = @User::find(user.id.unwrap()).await?; assert_eq!(found_user.name, "Test User"); tx.rollback().await?; Ok(()) }

// Integration tests with real database #[tokio::test] #[ignore] // Only run in integration test environment async fn test_database_integration() -> Result<()> { let pool = setup_database_pool().await?; // Test complex operations let result = process_order(1, vec![ CartItem { product_id: 1, quantity: 2, price: 29.99 }, CartItem { product_id: 2, quantity: 1, price: 49.99 }, ]).await?; assert!(result.id.is_some()); assert_eq!(result.status, "completed"); Ok(()) }

Performance Monitoring and Metrics

use tusk_db::metrics::{QueryMetrics, PerformanceMonitor};
use std::time::Instant;

// Performance monitoring with Rust metrics async fn monitored_query() -> Result<Vec<User>> { let start = Instant::now(); let users = @db.table::<User>("users") .where_eq("active", true) .get() .await?; let duration = start.elapsed(); // Record metrics @metrics::record_query_duration("get_active_users", duration).await; @metrics::record_query_count("get_active_users", 1).await; if duration > std::time::Duration::from_millis(100) { @log::warn!("Slow query detected", { query: "get_active_users", duration: duration.as_millis(), count: users.len() }); } Ok(users) }

Best Practices for Rust Database Integration

1. Use Strong Types: Leverage Rust's type system for compile-time safety 2. Handle Errors Properly: Use Result<T, E> and proper error types 3. Async/Await: Use non-blocking database operations 4. Connection Pooling: Manage database connections efficiently 5. Transactions: Use transactions for related operations 6. Prepared Statements: Let the ORM handle query preparation 7. Indexing: Add proper database indexes for performance 8. Monitoring: Track query performance and errors 9. Testing: Use test databases and transactions for testing 10. Security: Use parameterized queries (automatic with ORM)

Related Topics

- query-builder-rust - Fluent query interface for Rust - orm-models-rust - Model definition and usage in Rust - migrations-rust - Database schema versioning for Rust - relationships-rust - Model relationships in Rust - database-transactions-rust - Transaction handling in Rust

---

Ready to revolutionize your Rust database operations with TuskLang's type-safe, async-first approach?