pg_embedded_setup_unpriv/cluster/delegation.rs
1//! Delegation methods that forward `TestCluster` calls to `TestClusterConnection`.
2//!
3//! This module provides ergonomic access to database lifecycle operations directly from
4//! `TestCluster`, eliminating the need for callers to explicitly call `.connection()`
5//! before invoking methods like `create_database` or `drop_database`.
6
7use super::TestCluster;
8use super::lifecycle::DatabaseName;
9use super::temporary_database::TemporaryDatabase;
10use crate::error::BootstrapResult;
11
12/// Generates delegation methods on `TestCluster` that forward to `TestClusterConnection`.
13///
14/// Each invocation generates a method that calls `self.connection().$method(...)`.
15macro_rules! delegate_to_connection {
16 // Single argument, unit return
17 (
18 $(#[$meta:meta])*
19 fn $name:ident(&self, $arg:ident: $arg_ty:ty) -> BootstrapResult<()>
20 ) => {
21 $(#[$meta])*
22 pub fn $name(&self, $arg: $arg_ty) -> BootstrapResult<()> {
23 self.connection().$name($arg)
24 }
25 };
26
27 // Two arguments, unit return
28 (
29 $(#[$meta:meta])*
30 fn $name:ident(&self, $arg1:ident: $arg1_ty:ty, $arg2:ident: $arg2_ty:ty) -> BootstrapResult<()>
31 ) => {
32 $(#[$meta])*
33 pub fn $name(&self, $arg1: $arg1_ty, $arg2: $arg2_ty) -> BootstrapResult<()> {
34 self.connection().$name($arg1, $arg2)
35 }
36 };
37
38 // Single argument, custom return type
39 (
40 $(#[$meta:meta])*
41 fn $name:ident(&self, $arg:ident: $arg_ty:ty) -> BootstrapResult<$ret:ty>
42 ) => {
43 $(#[$meta])*
44 pub fn $name(&self, $arg: $arg_ty) -> BootstrapResult<$ret> {
45 self.connection().$name($arg)
46 }
47 };
48
49 // Two arguments, custom return type
50 (
51 $(#[$meta:meta])*
52 fn $name:ident(&self, $arg1:ident: $arg1_ty:ty, $arg2:ident: $arg2_ty:ty) -> BootstrapResult<$ret:ty>
53 ) => {
54 $(#[$meta])*
55 pub fn $name(&self, $arg1: $arg1_ty, $arg2: $arg2_ty) -> BootstrapResult<$ret> {
56 self.connection().$name($arg1, $arg2)
57 }
58 };
59}
60
61impl TestCluster {
62 delegate_to_connection! {
63 /// Creates a new database with the given name.
64 ///
65 /// Delegates to [`crate::TestClusterConnection::create_database`].
66 ///
67 /// # Errors
68 ///
69 /// Returns an error if the database already exists or if the connection
70 /// fails.
71 ///
72 /// # Examples
73 ///
74 /// ```no_run
75 /// use pg_embedded_setup_unpriv::TestCluster;
76 ///
77 /// # fn main() -> pg_embedded_setup_unpriv::BootstrapResult<()> {
78 /// let cluster = TestCluster::new()?;
79 /// cluster.create_database("my_test_db")?;
80 /// # Ok(())
81 /// # }
82 /// ```
83 fn create_database(&self, name: impl Into<DatabaseName>) -> BootstrapResult<()>
84 }
85
86 delegate_to_connection! {
87 /// Creates a new database by cloning an existing template.
88 ///
89 /// Delegates to [`crate::TestClusterConnection::create_database_from_template`].
90 ///
91 /// # Errors
92 ///
93 /// Returns an error if the target database already exists, the template
94 /// does not exist, the template has active connections, or if the
95 /// connection fails.
96 ///
97 /// # Examples
98 ///
99 /// ```no_run
100 /// use pg_embedded_setup_unpriv::TestCluster;
101 ///
102 /// # fn main() -> pg_embedded_setup_unpriv::BootstrapResult<()> {
103 /// let cluster = TestCluster::new()?;
104 /// cluster.create_database("my_template")?;
105 /// // ... run migrations on my_template ...
106 /// cluster.create_database_from_template("test_db", "my_template")?;
107 /// # Ok(())
108 /// # }
109 /// ```
110 fn create_database_from_template(&self, name: impl Into<DatabaseName>, template: impl Into<DatabaseName>) -> BootstrapResult<()>
111 }
112
113 delegate_to_connection! {
114 /// Drops an existing database.
115 ///
116 /// Delegates to [`crate::TestClusterConnection::drop_database`].
117 ///
118 /// # Errors
119 ///
120 /// Returns an error if the database does not exist, has active connections,
121 /// or if the connection fails.
122 ///
123 /// # Examples
124 ///
125 /// ```no_run
126 /// use pg_embedded_setup_unpriv::TestCluster;
127 ///
128 /// # fn main() -> pg_embedded_setup_unpriv::BootstrapResult<()> {
129 /// let cluster = TestCluster::new()?;
130 /// cluster.create_database("temp_db")?;
131 /// cluster.drop_database("temp_db")?;
132 /// # Ok(())
133 /// # }
134 /// ```
135 fn drop_database(&self, name: impl Into<DatabaseName>) -> BootstrapResult<()>
136 }
137
138 delegate_to_connection! {
139 /// Checks whether a database with the given name exists.
140 ///
141 /// Delegates to [`crate::TestClusterConnection::database_exists`].
142 ///
143 /// # Errors
144 ///
145 /// Returns an error if the connection fails.
146 ///
147 /// # Examples
148 ///
149 /// ```no_run
150 /// use pg_embedded_setup_unpriv::TestCluster;
151 ///
152 /// # fn main() -> pg_embedded_setup_unpriv::BootstrapResult<()> {
153 /// let cluster = TestCluster::new()?;
154 /// assert!(cluster.database_exists("postgres")?);
155 /// assert!(!cluster.database_exists("nonexistent")?);
156 /// # Ok(())
157 /// # }
158 /// ```
159 fn database_exists(&self, name: impl Into<DatabaseName>) -> BootstrapResult<bool>
160 }
161
162 /// Ensures a template database exists, creating it if necessary.
163 ///
164 /// Delegates to [`crate::TestClusterConnection::ensure_template_exists`].
165 ///
166 /// # Errors
167 ///
168 /// Returns an error if database creation fails or if `setup_fn` returns
169 /// an error.
170 ///
171 /// # Examples
172 ///
173 /// ```no_run
174 /// use pg_embedded_setup_unpriv::TestCluster;
175 ///
176 /// # fn main() -> pg_embedded_setup_unpriv::BootstrapResult<()> {
177 /// let cluster = TestCluster::new()?;
178 ///
179 /// // Ensure template exists, running migrations if needed
180 /// cluster.ensure_template_exists("my_template", |db_name| {
181 /// // Run migrations on the newly created template database
182 /// Ok(())
183 /// })?;
184 ///
185 /// // Clone the template for each test
186 /// cluster.create_database_from_template("test_db_1", "my_template")?;
187 /// # Ok(())
188 /// # }
189 /// ```
190 pub fn ensure_template_exists<F>(
191 &self,
192 name: impl Into<DatabaseName>,
193 setup_fn: F,
194 ) -> BootstrapResult<()>
195 where
196 F: FnOnce(&str) -> BootstrapResult<()>,
197 {
198 self.connection().ensure_template_exists(name, setup_fn)
199 }
200
201 delegate_to_connection! {
202 /// Creates a temporary database that is dropped when the guard is dropped.
203 ///
204 /// Delegates to [`crate::TestClusterConnection::temporary_database`].
205 ///
206 /// # Errors
207 ///
208 /// Returns an error if the database already exists or if the connection
209 /// fails.
210 ///
211 /// # Examples
212 ///
213 /// ```no_run
214 /// use pg_embedded_setup_unpriv::TestCluster;
215 ///
216 /// # fn main() -> pg_embedded_setup_unpriv::BootstrapResult<()> {
217 /// let cluster = TestCluster::new()?;
218 /// let temp_db = cluster.temporary_database("my_temp_db")?;
219 ///
220 /// // Database is dropped automatically when temp_db goes out of scope
221 /// # Ok(())
222 /// # }
223 /// ```
224 fn temporary_database(&self, name: impl Into<DatabaseName>) -> BootstrapResult<TemporaryDatabase>
225 }
226
227 delegate_to_connection! {
228 /// Creates a temporary database from a template.
229 ///
230 /// Delegates to [`crate::TestClusterConnection::temporary_database_from_template`].
231 ///
232 /// # Errors
233 ///
234 /// Returns an error if the target database already exists, the template
235 /// does not exist, the template has active connections, or if the
236 /// connection fails.
237 ///
238 /// # Examples
239 ///
240 /// ```no_run
241 /// use pg_embedded_setup_unpriv::TestCluster;
242 ///
243 /// # fn main() -> pg_embedded_setup_unpriv::BootstrapResult<()> {
244 /// let cluster = TestCluster::new()?;
245 /// cluster.ensure_template_exists("migrated_template", |_| Ok(()))?;
246 ///
247 /// let temp_db = cluster.temporary_database_from_template("test_db", "migrated_template")?;
248 ///
249 /// // Database is dropped automatically when temp_db goes out of scope
250 /// # Ok(())
251 /// # }
252 /// ```
253 fn temporary_database_from_template(&self, name: impl Into<DatabaseName>, template: impl Into<DatabaseName>) -> BootstrapResult<TemporaryDatabase>
254 }
255}