drizzle 0.1.12

A type-safe SQL query builder for Rust
Documentation
#![cfg(any(feature = "rusqlite", feature = "turso", feature = "libsql"))]
#![allow(clippy::approx_constant)]

use drizzle::sqlite::prelude::*;

// Test struct with various data types for FromRow
#[derive(SQLiteFromRow, Debug, PartialEq)]
struct AllDataTypes {
    id: i32,
    name: String,
    age: i64,
    score: f64,
    active: bool,
    data: Vec<u8>,
}

// Test struct with different integer sizes
#[derive(SQLiteFromRow, Debug, PartialEq)]
struct IntegerTypes {
    id: i32,
    big_num: i64,
    small_num: i16,
    tiny_num: i8,
}

// Test struct with float types
#[derive(SQLiteFromRow, Debug, PartialEq)]
struct FloatTypes {
    id: i32,
    precise: f64,
    compact: f32,
}

// Test table for comprehensive type testing
#[SQLiteTable]
struct TypeTest {
    #[column(PRIMARY)]
    id: i32,
    name: String,
    age: i64,
    score: f64,
    active: bool,
    data: Vec<u8>,
}

// Table for integer size testing
#[SQLiteTable]
struct IntegerTest {
    #[column(PRIMARY)]
    id: i32,
    big_num: i64,
    small_num: i16,
    tiny_num: i8,
}

// Table for float testing
#[SQLiteTable]
struct FloatTest {
    #[column(PRIMARY)]
    id: i32,
    precise: f64,
    compact: f32,
}

#[derive(SQLiteSchema)]
pub struct TypeTestSchema {
    type_test: TypeTest,
}

#[drizzle::test]
fn test_fromrow_with_all_data_types(db: &mut TestDb<TypeTestSchema>) {
    let TypeTestSchema { type_test } = schema;

    // Insert test data with all data types
    let test_data = InsertTypeTest::new("test_user", 25, 98.5, true, [1, 2, 3, 4, 5]);
    db.insert(type_test).values([test_data]).execute();

    // Test FromRow with all data types
    let result: AllDataTypes = db.select(()).from(type_test).get();

    let expected = AllDataTypes {
        id: 1,
        name: "test_user".to_string(),
        age: 25,
        score: 98.5,
        active: true,
        data: vec![1, 2, 3, 4, 5],
    };

    assert_eq!(result, expected);
}

#[derive(SQLiteSchema)]
struct IntegerSchema {
    integer_test: IntegerTest,
}

#[drizzle::test]
fn test_fromrow_with_integer_sizes(db: &mut TestDb<IntegerSchema>) {
    let IntegerSchema { integer_test } = schema;

    // Insert test data with different integer sizes
    let test_data = InsertIntegerTest::new(9223372036854775806i64, 32000i16, 100i8);
    db.insert(integer_test).values([test_data]).execute();

    // Test FromRow with different integer types
    let result: IntegerTypes = db.select(()).from(integer_test).get();

    let expected = IntegerTypes {
        id: 1,
        big_num: 9223372036854775806i64,
        small_num: 32000i16,
        tiny_num: 100i8,
    };

    assert_eq!(result, expected);
}

#[derive(SQLiteSchema)]
struct FloatSchema {
    float_test: FloatTest,
}
#[drizzle::test]
fn test_fromrow_with_float_types(db: &mut TestDb<FloatSchema>) {
    let FloatSchema { float_test } = schema;

    // Insert test data with different float types
    let test_data = InsertFloatTest::new(3.141592653589793, 2.718f32);
    db.insert(float_test).values([test_data]).execute();

    // Test FromRow with different float types
    let result: FloatTypes = db.select(()).from(float_test).get();

    let expected = FloatTypes {
        id: 1,
        precise: 3.141592653589793,
        compact: 2.718f32,
    };

    assert_eq!(result, expected);
}

#[drizzle::test]
fn test_fromrow_type_conversion_edge_cases(db: &mut TestDb<TypeTestSchema>) {
    let TypeTestSchema { type_test } = schema;

    // Insert test data with edge case values
    let test_data = InsertTypeTest::new("edge_case", 0, 0.0, false, []);
    db.insert(type_test).values([test_data]).execute();

    // Test FromRow with edge case values
    let result: AllDataTypes = db.select(()).from(type_test).get();

    let expected = AllDataTypes {
        id: 1,
        name: "edge_case".to_string(),
        age: 0,
        score: 0.0,
        active: false,
        data: vec![],
    };

    assert_eq!(result, expected);
}

// Test FromRow derive macro with partial selection
#[derive(SQLiteFromRow, Debug, Default)]
#[from(TypeTest)]
struct DerivedPartialSimple {
    name: String,
}

// Test FromRow with column mapping
#[derive(SQLiteFromRow, Debug, Default)]
#[from(TypeTest)]
struct DerivedSimpleWithColumns {
    #[column(TypeTest::id)]
    table_id: i32,
    #[column(TypeTest::name)]
    table_name: String,
}

#[drizzle::test]
fn test_fromrow_derive_with_partial_selection(db: &mut TestDb<TypeTestSchema>) {
    let TypeTestSchema { type_test } = schema;

    let test_data = InsertTypeTest::new("derive_test", 25, 98.5, true, [1, 2, 3]);
    db.insert(type_test).values([test_data]).execute();

    // Test the derived implementation with partial selection
    let result: DerivedPartialSimple = db
        .select(DerivedPartialSimple::Select)
        .from(type_test)
        .get();
    assert_eq!(result.name, "derive_test");
}

#[drizzle::test]
fn test_fromrow_with_column_mapping(db: &mut TestDb<TypeTestSchema>) {
    let TypeTestSchema { type_test } = schema;

    let test_data = InsertTypeTest::new("column_test", 25, 98.5, true, [1, 2, 3]).with_id(42);
    db.insert(type_test).values([test_data]).execute();

    // Test the column-mapped FromRow implementation
    let result: DerivedSimpleWithColumns = db
        .select(DerivedSimpleWithColumns::Select)
        .from(type_test)
        .get();

    assert_eq!(result.table_id, 42);
    assert_eq!(result.table_name, "column_test");
}

#[drizzle::test]
fn test_insert_returning_select_target_infers_row(db: &mut TestDb<TypeTestSchema>) {
    let TypeTestSchema { type_test } = schema;

    let stmt = db
        .insert(type_test)
        .values([InsertTypeTest::new(
            "returning_test",
            30,
            88.0,
            true,
            [1, 2, 3],
        )])
        .returning(DerivedPartialSimple::Select);

    let result: DerivedPartialSimple = stmt.get();
    assert_eq!(result.name, "returning_test");
}

#[derive(SQLiteFromRow, Debug, PartialEq)]
struct NamedNameId {
    id: i32,
    name: String,
}

#[derive(SQLiteFromRow, Debug, PartialEq)]
struct TupleNameId(String, i32);

#[drizzle::test]
fn test_fromrow_named_struct_maps_by_name(db: &mut TestDb<TypeTestSchema>) {
    // Turso row API does not expose column names, so named decoding is index-based there.
    if __driver_name == "turso" {
        return Ok(());
    }

    let TypeTestSchema { type_test } = schema;

    let row = InsertTypeTest::new("order_test", 25, 98.5, true, [1, 2, 3]).with_id(42);
    db.insert(type_test).values([row]).execute();

    // Strict typed decode follows selected column order.
    let stmt = db.select((type_test.id, type_test.name)).from(type_test);
    let result: NamedNameId = stmt.get();

    assert_eq!(result.id, 42);
    assert_eq!(result.name, "order_test");
}

#[drizzle::test]
fn test_fromrow_tuple_struct_maps_by_index(db: &mut TestDb<TypeTestSchema>) {
    let TypeTestSchema { type_test } = schema;

    let row = InsertTypeTest::new("order_test", 25, 98.5, true, [1, 2, 3]).with_id(42);
    db.insert(type_test).values([row]).execute();

    // Tuple structs decode positionally, following SELECT order.
    let stmt = db.select((type_test.name, type_test.id)).from(type_test);
    let result: TupleNameId = stmt.get();

    assert_eq!(result.0, "order_test");
    assert_eq!(result.1, 42);
}