sql-middleware 0.2.0

Lightweight async wrappers for tokio-postgres, rusqlite, libsql, turso, and tiberius.
Documentation
# SQL Middleware - A lightweight wrapper SQL backends

This crate provides a lightweight async wrapper for `SQLite`, `PostgreSQL`, `LibSQL`, `Turso` (experimental), and SQL Server (`tiberius`). The goal is a
similar, async-compatible API consistent across databases.

## Features

- Similar API regardless of backend (as much as possible)
- Asynchronous (where available)
- `deadpool` connection pooling (where available)
- Transaction support
- Not an ORM

## Feature Flags

Default features are `sqlite` and `postgres`. Enable others as needed:

```toml
# Only SQLite and LibSQL
sql-middleware = { version = "0", features = ["sqlite", "libsql"] }

# All backends
sql-middleware = { version = "0", features = ["sqlite", "postgres", "mssql", "libsql", "turso"] }
```

Additional flags:
- `libsql`: `LibSQL` (local has been tested, remote is present)
- `turso`: Turso (in-process, SQLite-compatible). Experimental; no remote support.
- `test-utils`: Test helpers for internal testing
- `mssql`: SQL Server via `tiberius` (untested, but present)
- `benchmarks`: Criterion helpers for benches

## Example

```rust,no_run
use sql_middleware::prelude::*;

async fn sqlite_example() -> Result<(), SqlMiddlewareDbError> {
    // Create a SQLite connection pool
    let config = ConfigAndPool::new_sqlite("my_database.db".to_string()).await?;
    
    // Get a connection from the pool
    let pool = config.pool.get().await?;
    let mut conn = MiddlewarePool::get_connection(&pool).await?;
    
    // Execute a query with parameters
    let result = conn.execute_select(
        "SELECT * FROM users WHERE id = ?",
        &[RowValues::Int(1)]
    ).await?;
    
    // Process the results
    for row in result.results {
        println!("User: {}", row.get("name").unwrap().as_text().unwrap());
    }
    
    Ok(())
}

async fn postgres_example() -> Result<(), SqlMiddlewareDbError> {
    // Create a PostgreSQL connection pool
    let mut pg_config = deadpool_postgres::Config::new();
    pg_config.host = Some("localhost".to_string());
    pg_config.port = Some(5432);
    pg_config.dbname = Some("mydatabase".to_string());
    pg_config.user = Some("user".to_string());
    pg_config.password = Some("password".to_string());
    
    let config = ConfigAndPool::new_postgres(pg_config).await?;
    
    // Get a connection and execute a query
    let pool = config.pool.get().await?;
    let mut conn = MiddlewarePool::get_connection(&pool).await?;
    
    let result = conn.execute_select(
        "SELECT * FROM users WHERE id = $1",
        &[RowValues::Int(1)]
    ).await?;
    
    Ok(())
}

async fn libsql_example() -> Result<(), SqlMiddlewareDbError> {
    use sql_middleware::prelude::*;

    // In-memory LibSQL (or use a file path like "./data.db")
    let config = ConfigAndPool::new_libsql(":memory:".to_string()).await?;

    // Remote LibSQL (Turso) example:
    // let config = ConfigAndPool::new_libsql_remote(
    //     "libsql://your-url".to_string(),
    //     "your_auth_token".to_string(),
    // ).await?;

    let pool = config.pool.get().await?;
    let mut conn = MiddlewarePool::get_connection(&pool).await?;

    let result = conn.execute_select(
        "SELECT 1 as one",
        &[]
    ).await?;

    assert_eq!(*result.results[0].get("one").unwrap().as_int().unwrap(), 1);
    Ok(())
}

async fn turso_example() -> Result<(), SqlMiddlewareDbError> {
    use sql_middleware::prelude::*;

    // In-memory Turso (or use a file path like "./data.db")
    let config = ConfigAndPool::new_turso(":memory:".to_string()).await?;

    let pool = config.pool.get().await?;
    let mut conn = MiddlewarePool::get_connection(&pool).await?;

    conn.execute_batch("CREATE TABLE t (id INTEGER, name TEXT);").await?;
    conn.execute_dml(
        "INSERT INTO t (id, name) VALUES (?, ?)",
        &[RowValues::Int(1), RowValues::Text("alice".into())],
    ).await?;

    let rs = conn
        .execute_select("SELECT name FROM t WHERE id = ?", &[RowValues::Int(1)])
        .await?;
    assert_eq!(rs.results[0].get("name").unwrap().as_text().unwrap(), "alice");
    Ok(())
}

async fn sqlserver_example() -> Result<(), SqlMiddlewareDbError> {
    // Create an SQL Server connection pool
    let config = ConfigAndPool::new_mssql(
        "localhost".to_string(),
        "mydatabase".to_string(),
        "sa".to_string(),
        "strong_password".to_string(),
        Some(1433),
        None,
    ).await?;
    
    // Get a connection and execute a query
    let pool = config.pool.get().await?;
    let mut conn = MiddlewarePool::get_connection(&pool).await?;
    
    let result = conn.execute_select(
        "SELECT * FROM users WHERE id = @p1",
        &[RowValues::Int(1)]
    ).await?;
    
    Ok(())
}

// For more in-depth examples (parameter conversion, batch queries, per-backend
// transactions, AsyncDatabaseExecutor usage), see the project README:
// https://github.com/derekfrye/sql-middleware/blob/main/docs/README.md
```