testkit_postgres/
lib.rs

1// Common traits used across feature implementations
2mod common_traits;
3pub use common_traits::*;
4
5#[cfg(feature = "with-tokio-postgres")]
6pub mod tokio_postgres;
7
8#[cfg(feature = "with-sqlx")]
9pub mod sqlx_postgres;
10
11// Error types for the library
12mod error;
13pub use error::*;
14
15/// Re-export the traits from testkit-core
16pub use testkit_core::{
17    DatabaseBackend, DatabaseConfig, DatabaseName, DatabasePool, TestContext,
18    TestDatabaseConnection, TestDatabaseInstance,
19};
20
21// Export feature-specific implementations
22#[cfg(feature = "with-tokio-postgres")]
23pub use tokio_postgres::*;
24
25#[cfg(feature = "with-sqlx")]
26pub use sqlx_postgres::*;
27
28// Convenience re-exports
29pub use testkit_core::with_connection;
30pub use testkit_core::with_connection_string;
31pub use testkit_core::with_database;
32#[cfg(feature = "with-tokio-postgres")]
33pub use testkit_core::{with_boxed_database, with_boxed_database_config};
34
35// Re-export the boxed_async macro for easily creating boxed async blocks
36pub use testkit_core::boxed_async;
37
38/// Execute a function with a direct database connection using SQLx
39/// This is the most efficient way to perform a one-off database operation
40/// The connection is automatically closed when the operation completes
41#[cfg(feature = "with-sqlx")]
42pub async fn with_sqlx_connection<F, R, E>(
43    connection_string: impl Into<String>,
44    operation: F,
45) -> Result<R, PostgresError>
46where
47    F: FnOnce(&mut sqlx_postgres::SqlxConnection) -> futures::future::BoxFuture<'_, Result<R, E>>,
48    E: std::error::Error + Send + Sync + 'static,
49{
50    sqlx_postgres::SqlxConnection::with_connection(connection_string, operation).await
51}
52
53/// Execute a function with a direct database connection using tokio-postgres
54/// This is the most efficient way to perform a one-off database operation
55/// The connection is automatically closed when the operation completes
56#[cfg(feature = "with-tokio-postgres")]
57pub async fn with_postgres_connection<F, R, E>(
58    connection_string: impl Into<String>,
59    operation: F,
60) -> Result<R, PostgresError>
61where
62    F: FnOnce(&tokio_postgres::PostgresConnection) -> futures::future::BoxFuture<'_, Result<R, E>>,
63    E: std::error::Error + Send + Sync + 'static,
64{
65    tokio_postgres::PostgresConnection::with_connection(connection_string, operation).await
66}
67
68/// Example of how to use the boxed database API with PostgreSQL
69///
70/// This example shows how to use the boxed database API to work with closures that
71/// capture local variables. Use the `boxed_async!` macro to avoid manually boxing the async blocks.
72///
73/// ```no_run,ignore
74/// use testkit_core::{with_boxed_database, boxed_async};
75/// // Import from the sqlx_postgres module directly
76/// use testkit_postgres::{PostgresConnection, postgres_backend};
77///
78/// async fn example() -> Result<(), Box<dyn std::error::Error>> {
79///     // Create a backend
80///     let backend = postgres_backend().await?;
81///     
82///     // Some local variables that would cause lifetime issues with regular closures
83///     let table_name = String::from("users");
84///     let table_name_for_tx = table_name.clone(); // Clone for use in transaction
85///     
86///     // Use the boxed database API with the boxed_async! macro
87///     let ctx = with_boxed_database(backend)
88///         .setup(move |conn: &mut PostgresConnection| boxed_async!(async move {
89///             // Create a table using the captured variable
90///             let query = format!(
91///                 "CREATE TABLE {} (id SERIAL PRIMARY KEY, name TEXT NOT NULL)",
92///                 table_name
93///             );
94///             conn.client().execute(&query, &[]).await?;
95///             Ok(())
96///         }))
97///         .with_transaction(move |conn: &mut PostgresConnection| boxed_async!(async move {
98///             // Insert data using the cloned variable
99///             let query = format!("INSERT INTO {} (name) VALUES ($1)", table_name_for_tx);
100///             conn.client().execute(&query, &[&"John Doe"]).await?;
101///             Ok(())
102///         }))
103///         .execute()
104///         .await?;
105///     
106///     Ok(())
107/// }
108/// ```
109#[allow(dead_code)]
110async fn boxed_example() -> Result<(), Box<dyn std::error::Error>> {
111    // This is just a dummy implementation to make the doctest compile
112    // The actual example is in the doc comment above
113    Ok(())
114}