Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Compile-Time Type Safety

New in v0.14.20 — Full Diesel-like type checking for QAIL queries

QAIL now supports compile-time type validation through generated schema files, similar to Diesel but with AST-native architecture.

Quick Start

1. Generate Schema

qail types schema.qail -o src/schema.rs

2. Use Type-Safe Builders

#![allow(unused)]
fn main() {
use crate::schema::users;

let query = Qail::get(users::TABLE)
    .typed_column(users::id())
    .typed_column(users::email())
    .typed_eq(users::active(), true)  // Compile-time: active must be bool
    .typed_gt(users::age(), 18);       // Compile-time: age must be numeric
}

Schema Generation

Input: schema.qail

table users {
    id          uuid primary_key
    email       text not_null unique
    name        text
    active      boolean default(true)
    age         integer
    created_at  timestamptz default(now())
}

Output: schema.rs

#![allow(unused)]
fn main() {
pub mod users {
    use qail_core::typed::{TypedColumn, Table};
    
    pub const TABLE: &str = "users";
    
    pub fn id() -> TypedColumn<uuid::Uuid> {
        TypedColumn::new("id")
    }
    
    pub fn email() -> TypedColumn<String> {
        TypedColumn::new("email")
    }
    
    pub fn active() -> TypedColumn<bool> {
        TypedColumn::new("active")
    }
    
    pub fn age() -> TypedColumn<i32> {
        TypedColumn::new("age")
    }
}
}

Type-Safe Methods

MethodDescriptionExample
typed_eq(col, val)Type-safe equalitytyped_eq(users::active(), true)
typed_ne(col, val)Type-safe not-equaltyped_ne(users::status(), "banned")
typed_gt(col, val)Type-safe greater-thantyped_gt(users::age(), 18)
typed_lt(col, val)Type-safe less-thantyped_lt(users::balance(), 0.0)
typed_gte(col, val)Greater-than or equaltyped_gte(users::score(), 100)
typed_lte(col, val)Less-than or equaltyped_lte(users::priority(), 5)
typed_column(col)Add typed columntyped_column(users::email())

SQL to Rust Type Mapping

SQL TypeRust Type
uuiduuid::Uuid
text, varcharString
integer, int4i32
bigint, int8i64
smallint, int2i16
boolean, boolbool
real, float4f32
double precision, float8f64
numeric, decimalf64
timestamptz, timestampchrono::DateTime<Utc>
datechrono::NaiveDate
jsonb, jsonserde_json::Value
byteaVec<u8>

Reserved Keywords

Rust reserved keywords are automatically escaped:

Column NameGenerated Function
typefn r#type()
fnfn r#fn()
structfn r#struct()

Compile-Time Errors

Type mismatches are caught at compile time:

#![allow(unused)]
fn main() {
// ✅ Compiles - active is bool
query.typed_eq(users::active(), true);

// ❌ Compile error - age is i32, not string
query.typed_eq(users::age(), "eighteen");
// error[E0277]: the trait bound `&str: ColumnValue<i32>` is not satisfied
}

Integration with Existing Code

Type-safe methods can be mixed with dynamic methods:

#![allow(unused)]
fn main() {
let query = Qail::get(users::TABLE)
    .typed_eq(users::active(), true)  // Type-safe
    .filter("created_at", Operator::Gte, "2024-01-01")  // Dynamic
    .typed_column(users::email());
}