easy-sql 0.101.1

Macro-first SQL toolkit with compile-time checked queries, optional migrations on top of sqlx.
Documentation
//! Prototype output implementation for query! macro

use anyhow::Context;
use easy_macros::always_context;

use crate::{
    Connection, Driver, Insert, Output, Table, macro_support::never_any, traits::DriverArguments,
};

use crate::macro_support;

use super::TestDriver;
#[derive(Debug, Table)]
#[sql(no_version)]
struct ExampleTable {
    #[sql(primary_key)]
    id: i64,
    field0: String,
    field1: String,
    field2: i32,
    field3: i64,
    field4: i16,
}
#[allow(dead_code)]
#[derive(Debug, Output)]
#[sql(table = ExampleTable)]
struct ExampleOutput {
    field1: String,
    field2: i32,
    field3: i64,
}
#[always_context]
#[no_context]
async fn _test_select() -> anyhow::Result<ExampleOutput> {
    let mut fake_conn = never_any::<Connection<TestDriver>>();

    let random_id = 42;

    // query!(&mut fake_conn, SELECT ExampleOutput FROM ExampleTable WHERE id = {random_id} AND field2 > 17);

    // query! macro output
    let result = {
        // Security checks are added in the macro
        // Imports
        use anyhow::Context;
        use {crate::traits::ToConvert, sqlx::Arguments};

        let mut args = macro_support::args_for_driver(&&mut fake_conn);
        let mut query = "SELECT ".to_string();
        let current_arg_n = 0;
        let mut _easy_sql_d = macro_support::driver_identifier_delimiter(&&mut fake_conn);
        let mut __easy_sql_parameter_placeholder =
            macro_support::driver_parameter_placeholder(&&mut fake_conn);

        // Build query
        macro_support::query_add_selected::<ExampleTable, ExampleOutput, _>(
            &mut query,
            &&mut fake_conn,
        );
        query.push_str(" FROM ");
        query.push_str(<ExampleTable as Table<TestDriver>>::table_name());
        query.push_str(&format!(
            " WHERE {_easy_sql_d}id{_easy_sql_d} = {} AND {_easy_sql_d}field2{_easy_sql_d} > {}",
            __easy_sql_parameter_placeholder(current_arg_n),
            __easy_sql_parameter_placeholder(current_arg_n + 1)
        ));
        args.add(&random_id).map_err(anyhow::Error::from_boxed)?;

        let mut builder = sqlx::QueryBuilder::with_arguments(query, args);
        let built_query = builder.build();

        async fn execute<'a, T, O: Output<T, D>, D: Driver>(
            exec: &mut impl crate::traits::EasyExecutor<D>,
            query: sqlx::query::Query<'a, crate::traits::InternalDriver<D>, DriverArguments<'a, D>>,
        ) -> anyhow::Result<O> {
            let raw_data = O::DataToConvert::get(exec.executor(), query).await?;

            let result = O::convert(raw_data)?;

            Ok(result)
        }

        execute::<ExampleTable, ExampleOutput, _>(&mut &mut fake_conn, built_query)
            .await
            .with_context(|| "Generated by macro")

        // Generate debug info in the macro from the input, with what are the parameters and their values
    }
    .context("")?;

    Ok(result)
}

#[always_context]
#[no_context]
async fn _test_insert() -> anyhow::Result<()> {
    let mut fake_conn = never_any::<Connection<TestDriver>>();

    let new_entry = ExampleTable {
        id: 1,
        field0: "test".to_string(),
        field1: "example".to_string(),
        field2: 123,
        field3: 456,
        field4: 7,
    };

    // query!(&mut fake_conn, INSERT INTO ExampleTable VALUES {new_entry});

    // query! macro output
    {
        async {
            //TODO Security checks
            // Imports

            async fn __easy_sql_perform<'a, T: Insert<'a, ExampleTable, TestDriver>>(
                exec: &mut impl crate::EasyExecutor<TestDriver>,
                to_insert: T,
            ) -> anyhow::Result<crate::traits::DriverQueryResult<TestDriver>> {
                let mut args = DriverArguments::<TestDriver>::default();
                let mut query = "INSERT INTO ".to_string();
                let mut current_arg_n = 0;
                let mut _easy_sql_d = TestDriver::identifier_delimiter();

                // Build query
                query.push_str(<ExampleTable as Table<TestDriver>>::table_name());
                query.push_str(" (");

                let columns = <ExampleTable as Insert<ExampleTable, TestDriver>>::insert_columns();
                for (i, col) in columns.iter().enumerate() {
                    if i > 0 {
                        query.push_str(", ");
                    }
                    query.push_str(&format!("{_easy_sql_d}{col}{_easy_sql_d}"));
                }

                query.push_str(") VALUES");

                let (new_args, count) = to_insert
                    .insert_values(args)
                    .context("Failed to get insert values")?;
                args = new_args;

                for _ in 0..count {
                    query.push_str(" (");

                    for i in 0..columns.len() {
                        query.push_str(&TestDriver::parameter_placeholder(current_arg_n + i));
                        query.push(',');
                    }
                    current_arg_n += columns.len();
                    query.pop(); //Remove last comma

                    query.push_str("),");
                }
                query.pop(); //Remove last comma

                //Build and execute query
                let mut builder = sqlx::QueryBuilder::with_arguments(query, args);

                let built_query = builder.build();

                built_query
                    .execute(exec.executor())
                    .await
                    .context("Failed to execute insert query")
                //TODO Generate debug info in the macro from the input, with what are the parameters and their values
            }

            __easy_sql_perform(&mut &mut fake_conn, new_entry)
                .await
                .with_context(|| "Generated by macro")
        }
    }
    .await
    .context("")?;

    Ok(())
}

#[always_context]
#[no_context]
async fn _test_update() -> anyhow::Result<()> {
    let mut fake_conn = never_any::<Connection<TestDriver>>();

    let data_update = ExampleTable {
        id: 1,
        field0: "updated_test".to_string(),
        field1: "updated_example".to_string(),
        field2: 456,
        field3: 789,
        field4: 10,
    };

    // query!(&mut fake_conn, UPDATE ExampleTable SET {data_update} WHERE id = {data_update.id});

    // query! macro output
    {
        //Add Security checks here
        // Imports
        use sqlx::Arguments;

        let mut args = DriverArguments::<TestDriver>::default();
        let mut query = "UPDATE ".to_string();
        let mut current_arg_n = 0;
        let mut _easy_sql_d = TestDriver::identifier_delimiter();

        // Build query
        query.push_str(<ExampleTable as Table<TestDriver>>::table_name());
        query.push_str(" SET ");

        args = crate::macro_support::query_update_data_selected_driver::<
            ExampleTable,
            TestDriver,
            _,
        >(&data_update, args, &mut query, &mut current_arg_n)?;

        query.push_str(&format!(
            " WHERE {_easy_sql_d}id{_easy_sql_d} = {}",
            TestDriver::parameter_placeholder(current_arg_n)
        ));
        args.add(&data_update.id)
            .map_err(anyhow::Error::from_boxed)?;

        async fn execute<'a>(
            exec: impl sqlx::Executor<'a, Database = crate::traits::InternalDriver<TestDriver>>,
            query: String,
            args: DriverArguments<'a, TestDriver>,
        ) -> Result<crate::traits::DriverQueryResult<TestDriver>, sqlx::Error> {
            let mut builder = sqlx::QueryBuilder::with_arguments(query, args);

            let built_query = builder.build();

            built_query.execute(exec).await
        }

        execute(&mut *fake_conn, query, args)
        // Generate debug info in the macro from the input, with what are the parameters and their values
    }
    .await
    .context("")?;

    Ok(())
}

#[always_context]
#[no_context]
async fn _test_delete() -> anyhow::Result<()> {
    let mut fake_conn = never_any::<Connection<TestDriver>>();

    let delete_id = 1;

    // query!(&mut fake_conn, DELETE FROM ExampleTable WHERE id = {delete_id});

    // query! macro output
    {
        async {
            //TODO Security checks
            // Imports
            use futures::FutureExt;
            use sqlx::Arguments;
            let mut args = DriverArguments::<TestDriver>::default();
            let mut query = "DELETE FROM ".to_string();
            let current_arg_n = 0;
            let mut _easy_sql_d = TestDriver::identifier_delimiter();

            // Build query
            query.push_str(<ExampleTable as Table<TestDriver>>::table_name());
            query.push_str(&format!(
                " WHERE {_easy_sql_d}id{_easy_sql_d} = {}",
                TestDriver::parameter_placeholder(current_arg_n)
            ));
            args.add(&delete_id).map_err(anyhow::Error::from_boxed)?;

            async fn execute<'a>(
                exec: &mut impl crate::EasyExecutor<TestDriver>,
                query: String,
                args: DriverArguments<'a, TestDriver>,
            ) -> Result<crate::traits::DriverQueryResult<TestDriver>, sqlx::Error> {
                let mut builder = sqlx::QueryBuilder::with_arguments(query, args);

                let built_query = builder.build();

                built_query.execute(exec.executor()).await
            }

            execute(&mut &mut fake_conn, query, args)
                .map(|r| r.with_context(|| "Generated by macro"))
                .await
            //TODO Generate debug info in the macro from the input, with what are the parameters and their values
        }
    }
    .await
    .context("")?;

    Ok(())
}