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}