sqlutil 0.1.7

A utility library for building SQL queries
Documentation
use crate::{build_select_sql, make_column, make_table, make_where};

pub fn make_fetch(tables: &[&str], columns: Option<&[&str]>, wheres: Option<&[&str]>) -> String {
    build_select_sql(
        &make_table(tables),
        columns.map(make_column).as_deref(),
        wheres.map(make_where).as_deref(),
        None,
        None,
        Some(1),
    )
}

#[cfg(test)]
mod tests {
    use super::*;

    // ── Always outputs LIMIT 1 ────────────────────────────────────────────────

    #[test]
    fn test_fetch_always_limit_1() {
        let sql = make_fetch(&["users"], None, None);
        assert!(sql.ends_with("LIMIT 1"), "expected LIMIT 1, got: {sql}");
    }

    #[test]
    fn test_fetch_no_offset() {
        // OFFSET must never appear — make_fetch passes offset = None
        let sql = make_fetch(&["users"], None, None);
        assert!(!sql.contains("OFFSET"), "unexpected OFFSET in: {sql}");
    }

    #[test]
    fn test_fetch_no_order_by() {
        // ORDER BY must never appear — make_fetch passes order = None
        let sql = make_fetch(&["users"], None, None);
        assert!(!sql.contains("ORDER BY"), "unexpected ORDER BY in: {sql}");
    }

    // ── Columns ───────────────────────────────────────────────────────────────

    #[test]
    fn test_fetch_default_columns() {
        // None columns → SELECT *
        let sql = make_fetch(&["users"], None, None);
        assert_eq!(sql, r#"SELECT * FROM "users" LIMIT 1"#);
    }

    #[test]
    fn test_fetch_single_column() {
        let sql = make_fetch(&["users"], Some(&["id"]), None);
        assert_eq!(sql, r#"SELECT id FROM "users" LIMIT 1"#);
    }

    #[test]
    fn test_fetch_multiple_columns() {
        let sql = make_fetch(&["users"], Some(&["id", "name", "email"]), None);
        assert_eq!(sql, r#"SELECT id , name , email FROM "users" LIMIT 1"#);
    }

    #[test]
    fn test_fetch_explicit_wildcard_column() {
        let sql = make_fetch(&["users"], Some(&["*"]), None);
        assert_eq!(sql, r#"SELECT * FROM "users" LIMIT 1"#);
    }

    // ── Tables ────────────────────────────────────────────────────────────────

    #[test]
    fn test_fetch_single_table() {
        let sql = make_fetch(&["orders"], None, None);
        assert_eq!(sql, r#"SELECT * FROM "orders" LIMIT 1"#);
    }

    #[test]
    fn test_fetch_multiple_tables() {
        // make_table joins with " , "
        let sql = make_fetch(&["users", "profiles"], None, None);
        assert_eq!(sql, r#"SELECT * FROM "users , profiles" LIMIT 1"#);
    }

    #[test]
    fn test_fetch_table_name_with_spaces() {
        let sql = make_fetch(&["my table"], None, None);
        assert_eq!(sql, r#"SELECT * FROM "my table" LIMIT 1"#);
    }

    #[test]
    fn test_fetch_table_name_with_double_quote_escaped() {
        let sql = make_fetch(&[r#"weird"tbl"#], None, None);
        assert_eq!(sql, r#"SELECT * FROM "weird""tbl" LIMIT 1"#);
    }

    // ── WHERE clause ──────────────────────────────────────────────────────────

    #[test]
    fn test_fetch_no_where_none() {
        let sql = make_fetch(&["users"], None, None);
        assert!(!sql.contains("WHERE"), "unexpected WHERE in: {sql}");
    }

    #[test]
    fn test_fetch_no_where_empty_slice() {
        // Some(&[]) → make_where returns "" → build_select_sql skips empty WHERE
        let sql = make_fetch(&["users"], None, Some(&[]));
        assert!(!sql.contains("WHERE"), "unexpected WHERE in: {sql}");
        assert_eq!(sql, r#"SELECT * FROM "users" LIMIT 1"#);
    }

    #[test]
    fn test_fetch_single_where() {
        let sql = make_fetch(&["users"], None, Some(&["id = 1"]));
        assert_eq!(sql, r#"SELECT * FROM "users" WHERE id = 1 LIMIT 1"#);
    }

    #[test]
    fn test_fetch_two_where_conditions() {
        let sql = make_fetch(&["users"], None, Some(&["active = 1", "age > 18"]));
        assert_eq!(
            sql,
            r#"SELECT * FROM "users" WHERE active = 1 AND age > 18 LIMIT 1"#
        );
    }

    #[test]
    fn test_fetch_three_where_conditions() {
        let sql = make_fetch(
            &["users"],
            None,
            Some(&["active = 1", "age > 18", "role = 'admin'"]),
        );
        assert_eq!(
            sql,
            r#"SELECT * FROM "users" WHERE active = 1 AND age > 18 AND role = 'admin' LIMIT 1"#
        );
    }

    #[test]
    fn test_fetch_where_whitespace_only_is_skipped() {
        let sql = make_fetch(&["users"], None, Some(&["   "]));
        assert_eq!(sql, r#"SELECT * FROM "users" LIMIT 1"#);
    }

    #[test]
    fn test_fetch_where_is_null() {
        let sql = make_fetch(&["events"], None, Some(&["deleted_at IS NULL"]));
        assert_eq!(
            sql,
            r#"SELECT * FROM "events" WHERE deleted_at IS NULL LIMIT 1"#
        );
    }

    #[test]
    fn test_fetch_where_in_clause() {
        let sql = make_fetch(&["products"], None, Some(&["id IN (1, 2, 3)"]));
        assert_eq!(
            sql,
            r#"SELECT * FROM "products" WHERE id IN (1, 2, 3) LIMIT 1"#
        );
    }

    // ── Combined columns + WHERE ──────────────────────────────────────────────

    #[test]
    fn test_fetch_columns_and_where() {
        let sql = make_fetch(&["users"], Some(&["id", "name"]), Some(&["active = 1"]));
        assert_eq!(
            sql,
            r#"SELECT id , name FROM "users" WHERE active = 1 LIMIT 1"#
        );
    }

    #[test]
    fn test_fetch_columns_and_multiple_where() {
        let sql = make_fetch(
            &["sessions"],
            Some(&["id", "token", "user_id"]),
            Some(&["expired = 0", "user_id = 42"]),
        );
        assert_eq!(
            sql,
            r#"SELECT id , token , user_id FROM "sessions" WHERE expired = 0 AND user_id = 42 LIMIT 1"#
        );
    }

    // ── Return type ───────────────────────────────────────────────────────────

    #[test]
    fn test_fetch_returns_non_empty_string() {
        let result: String = make_fetch(&["t"], None, None);
        assert!(!result.is_empty());
    }

    #[test]
    fn test_fetch_starts_with_select() {
        let sql = make_fetch(&["anything"], None, None);
        assert!(sql.starts_with("SELECT"));
    }
}