rqb 0.1.7

sqlx-first typed Postgres query builder.
Documentation
#![allow(dead_code)]

use chrono::{DateTime, Utc};
use rqb::prelude::*;
use sqlx::PgPool;
use sqlx::postgres::PgPoolOptions;
use uuid::Uuid;

pub async fn pool() -> PgPool {
    let url = std::env::var("RQB_TEST_DATABASE_URL")
        .expect("RQB_TEST_DATABASE_URL must be set for postgres integration tests");
    PgPoolOptions::new()
        .max_connections(8)
        .connect(&url)
        .await
        .expect("connect to Postgres integration database")
}

pub async fn assert_postgres_18(pool: &PgPool) {
    let version: String = rqb::raw("SHOW server_version_num")
        .fetch_one_scalar(pool)
        .await
        .expect("SHOW server_version_num");
    let version = version
        .parse::<u32>()
        .expect("server_version_num must be an integer");
    assert!(
        (180000..190000).contains(&version),
        "integration tests must run against Postgres 18, got server_version_num={version}"
    );
}

pub fn uuid(value: &str) -> Uuid {
    Uuid::parse_str(value).expect("valid fixture UUID")
}

pub fn unique_text(label: &str) -> String {
    format!("rqb-it-{label}-{}", Uuid::new_v4())
}

pub fn utc(value: &str) -> DateTime<Utc> {
    DateTime::parse_from_rfc3339(value)
        .expect("valid RFC3339 timestamp")
        .with_timezone(&Utc)
}

pub async fn insert_product(pool: &PgPool, id: Uuid, sku: &str, name: &str, price_cents: i64) {
    rqb::insert(products::table())
        .set_many((
            products::ID.set(id),
            products::SKU.set(sku.to_owned()),
            products::NAME.set(name.to_owned()),
            products::PRICE_CENTS.set(price_cents),
        ))
        .execute(pool)
        .await
        .expect("insert integration product");
}

pub async fn delete_product(pool: &PgPool, id: Uuid) {
    let _ = rqb::delete_from(products::table())
        .filter(products::ID.eq(id))
        .execute(pool)
        .await;
}

pub mod organizations {
    use super::*;

    pub static ID_META: Meta = Meta::col("id", "uuid").ops(OpSet::equality());
    pub static SLUG_META: Meta = Meta::col("slug", "text").ops(OpSet::text());
    pub static NAME_META: Meta = Meta::col("name", "text").ops(OpSet::text());

    pub const ID: Field<Uuid> = Field::new(&ID_META);
    pub const SLUG: Field<String> = Field::new(&SLUG_META);
    pub const NAME: Field<String> = Field::new(&NAME_META);

    pub static FIELDS: [&Meta; 3] = [&ID_META, &SLUG_META, &NAME_META];

    pub fn table() -> Source {
        rqb::table("public.organizations", &FIELDS)
    }
}

pub mod products {
    use super::*;

    pub static ID_META: Meta = Meta::col("id", "uuid")
        .ops(OpSet::equality())
        .json(JsonKind::Uuid);
    pub static SKU_META: Meta = Meta::col("sku", "text")
        .ops(OpSet::text())
        .json(JsonKind::Text);
    pub static NAME_META: Meta = Meta::col("name", "text")
        .ops(OpSet::text())
        .json(JsonKind::Text);
    pub static PRICE_CENTS_META: Meta = Meta::col("price_cents", "int8")
        .ops(OpSet::ordered())
        .json(JsonKind::BigInt);

    pub const ID: Field<Uuid> = Field::new(&ID_META);
    pub const SKU: Field<String> = Field::new(&SKU_META);
    pub const NAME: Field<String> = Field::new(&NAME_META);
    pub const PRICE_CENTS: Field<i64> = Field::new(&PRICE_CENTS_META);

    pub static FIELDS: [&Meta; 4] = [&ID_META, &SKU_META, &NAME_META, &PRICE_CENTS_META];

    pub fn table() -> Source {
        rqb::table("public.products", &FIELDS)
    }
}

pub mod order_items {
    use super::*;

    pub static ID_META: Meta = Meta::col("id", "uuid").ops(OpSet::equality());
    pub static ORDER_ID_META: Meta = Meta::col("order_id", "uuid").ops(OpSet::equality());
    pub static PRODUCT_ID_META: Meta = Meta::col("product_id", "uuid").ops(OpSet::equality());
    pub static QUANTITY_META: Meta = Meta::col("quantity", "int4").ops(OpSet::ordered());
    pub static UNIT_PRICE_CENTS_META: Meta =
        Meta::col("unit_price_cents", "int8").ops(OpSet::ordered());

    pub const ID: Field<Uuid> = Field::new(&ID_META);
    pub const ORDER_ID: Field<Uuid> = Field::new(&ORDER_ID_META);
    pub const PRODUCT_ID: Field<Uuid> = Field::new(&PRODUCT_ID_META);
    pub const QUANTITY: Field<i32> = Field::new(&QUANTITY_META);
    pub const UNIT_PRICE_CENTS: Field<i64> = Field::new(&UNIT_PRICE_CENTS_META);

    pub static FIELDS: [&Meta; 5] = [
        &ID_META,
        &ORDER_ID_META,
        &PRODUCT_ID_META,
        &QUANTITY_META,
        &UNIT_PRICE_CENTS_META,
    ];

    pub fn table() -> Source {
        rqb::table("public.order_items", &FIELDS)
    }
}

pub mod order_search {
    use super::*;

    pub static ID_META: Meta = Meta::col("id", "uuid")
        .ops(OpSet::equality())
        .json(JsonKind::Uuid);
    pub static EMAIL_META: Meta = Meta::col("email", "text")
        .ops(OpSet::ordered())
        .json(JsonKind::Text);
    pub static STATUS_META: Meta = Meta::col("status", "order_status").ops(OpSet::ordered());
    pub static CHANNEL_META: Meta = Meta::col("channel", "text")
        .ops(OpSet::ordered())
        .json(JsonKind::Text);
    pub static TOTAL_CENTS_META: Meta = Meta::col("total_cents", "int8")
        .ops(OpSet::ordered())
        .json(JsonKind::BigInt);
    pub static CREATED_AT_META: Meta = Meta::col("created_at", "timestamptz")
        .ops(OpSet::ordered())
        .json(JsonKind::Timestamptz);

    pub const ID: Field<Uuid> = Field::new(&ID_META);
    pub const EMAIL: Field<String> = Field::new(&EMAIL_META);
    pub const STATUS: Field<String> = Field::new(&STATUS_META);
    pub const CHANNEL: Field<String> = Field::new(&CHANNEL_META);
    pub const TOTAL_CENTS: Field<i64> = Field::new(&TOTAL_CENTS_META);
    pub const CREATED_AT: Field<DateTime<Utc>> = Field::new(&CREATED_AT_META);

    pub static FIELDS: [&Meta; 6] = [
        &ID_META,
        &EMAIL_META,
        &STATUS_META,
        &CHANNEL_META,
        &TOTAL_CENTS_META,
        &CREATED_AT_META,
    ];

    pub fn view() -> Source {
        rqb::view("public.order_search_view", &FIELDS)
    }
}