1#[cfg(feature = "console")]
25use std::sync::Arc;
26
27#[cfg(feature = "console")]
28use sqlmodel_console::{ConsoleAware, SqlModelConsole};
29
30#[cfg(feature = "console")]
31use crate::global_console::global_console;
32
33use sqlmodel_core::Connection;
34
35#[derive(Debug)]
41pub struct ConnectionSession<C: Connection> {
42 connection: C,
44 #[cfg(feature = "console")]
46 console: Option<Arc<SqlModelConsole>>,
47}
48
49impl<C: Connection> ConnectionSession<C> {
50 pub fn new(connection: C) -> Self {
52 Self {
53 connection,
54 #[cfg(feature = "console")]
55 console: None,
56 }
57 }
58
59 #[must_use]
61 pub fn builder() -> ConnectionSessionBuilder<C> {
62 ConnectionSessionBuilder::new()
63 }
64
65 #[must_use]
67 pub fn connection(&self) -> &C {
68 &self.connection
69 }
70
71 pub fn connection_mut(&mut self) -> &mut C {
73 &mut self.connection
74 }
75
76 pub fn into_connection(self) -> C {
78 self.connection
79 }
80}
81
82#[cfg(feature = "console")]
83impl<C: Connection> ConsoleAware for ConnectionSession<C> {
84 fn set_console(&mut self, console: Option<Arc<SqlModelConsole>>) {
85 self.console = console;
86 }
87
88 fn console(&self) -> Option<&Arc<SqlModelConsole>> {
89 self.console.as_ref()
90 }
91
92 fn has_console(&self) -> bool {
93 self.console.is_some()
94 }
95}
96
97#[derive(Debug, Default)]
107pub struct ConnectionSessionBuilder<C: Connection> {
108 #[cfg(feature = "console")]
109 console: Option<Arc<SqlModelConsole>>,
110 #[cfg(not(feature = "console"))]
111 _marker: std::marker::PhantomData<C>,
112 #[cfg(feature = "console")]
113 _marker: std::marker::PhantomData<C>,
114}
115
116impl<C: Connection> ConnectionSessionBuilder<C> {
117 #[must_use]
119 pub fn new() -> Self {
120 Self {
121 #[cfg(feature = "console")]
122 console: None,
123 _marker: std::marker::PhantomData,
124 }
125 }
126
127 #[cfg(feature = "console")]
132 #[must_use]
133 pub fn with_console(mut self, console: SqlModelConsole) -> Self {
134 self.console = Some(Arc::new(console));
135 self
136 }
137
138 #[cfg(feature = "console")]
143 #[must_use]
144 pub fn with_shared_console(mut self, console: Arc<SqlModelConsole>) -> Self {
145 self.console = Some(console);
146 self
147 }
148
149 #[cfg(feature = "console")]
155 #[must_use]
156 pub fn with_auto_console(mut self) -> Self {
157 self.console = Some(Arc::new(SqlModelConsole::new()));
158 self
159 }
160
161 #[allow(unused_mut)] pub fn build_with(self, connection: C) -> ConnectionSession<C> {
169 let mut session = ConnectionSession::new(connection);
170
171 #[cfg(feature = "console")]
172 {
173 session.console = self.console.or_else(global_console);
175 }
176
177 session
178 }
179}
180
181#[cfg(feature = "console")]
186pub trait ConnectionBuilderExt {
187 type Connection: Connection + ConsoleAware;
189
190 fn with_console(self, console: SqlModelConsole) -> Self;
192
193 fn with_shared_console(self, console: Arc<SqlModelConsole>) -> Self;
195
196 fn with_auto_console(self) -> Self;
198}
199
200#[cfg(test)]
201#[allow(clippy::manual_async_fn)] mod tests {
203 use super::*;
204
205 #[derive(Debug)]
207 struct MockConnection;
208
209 impl sqlmodel_core::Connection for MockConnection {
210 type Tx<'conn>
211 = MockTransaction
212 where
213 Self: 'conn;
214
215 fn query(
216 &self,
217 _cx: &asupersync::Cx,
218 _sql: &str,
219 _params: &[sqlmodel_core::Value],
220 ) -> impl std::future::Future<
221 Output = asupersync::Outcome<Vec<sqlmodel_core::Row>, sqlmodel_core::Error>,
222 > + Send {
223 async { asupersync::Outcome::Ok(vec![]) }
224 }
225
226 fn query_one(
227 &self,
228 _cx: &asupersync::Cx,
229 _sql: &str,
230 _params: &[sqlmodel_core::Value],
231 ) -> impl std::future::Future<
232 Output = asupersync::Outcome<Option<sqlmodel_core::Row>, sqlmodel_core::Error>,
233 > + Send {
234 async { asupersync::Outcome::Ok(None) }
235 }
236
237 fn execute(
238 &self,
239 _cx: &asupersync::Cx,
240 _sql: &str,
241 _params: &[sqlmodel_core::Value],
242 ) -> impl std::future::Future<Output = asupersync::Outcome<u64, sqlmodel_core::Error>> + Send
243 {
244 async { asupersync::Outcome::Ok(0) }
245 }
246
247 fn insert(
248 &self,
249 _cx: &asupersync::Cx,
250 _sql: &str,
251 _params: &[sqlmodel_core::Value],
252 ) -> impl std::future::Future<Output = asupersync::Outcome<i64, sqlmodel_core::Error>> + Send
253 {
254 async { asupersync::Outcome::Ok(0) }
255 }
256
257 fn batch(
258 &self,
259 _cx: &asupersync::Cx,
260 _statements: &[(String, Vec<sqlmodel_core::Value>)],
261 ) -> impl std::future::Future<Output = asupersync::Outcome<Vec<u64>, sqlmodel_core::Error>> + Send
262 {
263 async { asupersync::Outcome::Ok(vec![]) }
264 }
265
266 fn begin(
267 &self,
268 _cx: &asupersync::Cx,
269 ) -> impl std::future::Future<
270 Output = asupersync::Outcome<Self::Tx<'_>, sqlmodel_core::Error>,
271 > + Send {
272 async { asupersync::Outcome::Ok(MockTransaction) }
273 }
274
275 fn begin_with(
276 &self,
277 _cx: &asupersync::Cx,
278 _isolation: sqlmodel_core::connection::IsolationLevel,
279 ) -> impl std::future::Future<
280 Output = asupersync::Outcome<Self::Tx<'_>, sqlmodel_core::Error>,
281 > + Send {
282 async { asupersync::Outcome::Ok(MockTransaction) }
283 }
284
285 fn prepare(
286 &self,
287 _cx: &asupersync::Cx,
288 _sql: &str,
289 ) -> impl std::future::Future<
290 Output = asupersync::Outcome<
291 sqlmodel_core::connection::PreparedStatement,
292 sqlmodel_core::Error,
293 >,
294 > + Send {
295 async {
296 asupersync::Outcome::Ok(sqlmodel_core::connection::PreparedStatement::new(
297 0,
298 String::new(),
299 0,
300 ))
301 }
302 }
303
304 fn query_prepared(
305 &self,
306 _cx: &asupersync::Cx,
307 _stmt: &sqlmodel_core::connection::PreparedStatement,
308 _params: &[sqlmodel_core::Value],
309 ) -> impl std::future::Future<
310 Output = asupersync::Outcome<Vec<sqlmodel_core::Row>, sqlmodel_core::Error>,
311 > + Send {
312 async { asupersync::Outcome::Ok(vec![]) }
313 }
314
315 fn execute_prepared(
316 &self,
317 _cx: &asupersync::Cx,
318 _stmt: &sqlmodel_core::connection::PreparedStatement,
319 _params: &[sqlmodel_core::Value],
320 ) -> impl std::future::Future<Output = asupersync::Outcome<u64, sqlmodel_core::Error>> + Send
321 {
322 async { asupersync::Outcome::Ok(0) }
323 }
324
325 fn ping(
326 &self,
327 _cx: &asupersync::Cx,
328 ) -> impl std::future::Future<Output = asupersync::Outcome<(), sqlmodel_core::Error>> + Send
329 {
330 async { asupersync::Outcome::Ok(()) }
331 }
332
333 fn close(
334 self,
335 _cx: &asupersync::Cx,
336 ) -> impl std::future::Future<Output = sqlmodel_core::error::Result<()>> + Send {
337 async { Ok(()) }
338 }
339 }
340
341 struct MockTransaction;
342
343 impl sqlmodel_core::connection::TransactionOps for MockTransaction {
344 fn query(
345 &self,
346 _cx: &asupersync::Cx,
347 _sql: &str,
348 _params: &[sqlmodel_core::Value],
349 ) -> impl std::future::Future<
350 Output = asupersync::Outcome<Vec<sqlmodel_core::Row>, sqlmodel_core::Error>,
351 > + Send {
352 async { asupersync::Outcome::Ok(vec![]) }
353 }
354
355 fn query_one(
356 &self,
357 _cx: &asupersync::Cx,
358 _sql: &str,
359 _params: &[sqlmodel_core::Value],
360 ) -> impl std::future::Future<
361 Output = asupersync::Outcome<Option<sqlmodel_core::Row>, sqlmodel_core::Error>,
362 > + Send {
363 async { asupersync::Outcome::Ok(None) }
364 }
365
366 fn execute(
367 &self,
368 _cx: &asupersync::Cx,
369 _sql: &str,
370 _params: &[sqlmodel_core::Value],
371 ) -> impl std::future::Future<Output = asupersync::Outcome<u64, sqlmodel_core::Error>> + Send
372 {
373 async { asupersync::Outcome::Ok(0) }
374 }
375
376 fn savepoint(
377 &self,
378 _cx: &asupersync::Cx,
379 _name: &str,
380 ) -> impl std::future::Future<Output = asupersync::Outcome<(), sqlmodel_core::Error>> + Send
381 {
382 async { asupersync::Outcome::Ok(()) }
383 }
384
385 fn rollback_to(
386 &self,
387 _cx: &asupersync::Cx,
388 _name: &str,
389 ) -> impl std::future::Future<Output = asupersync::Outcome<(), sqlmodel_core::Error>> + Send
390 {
391 async { asupersync::Outcome::Ok(()) }
392 }
393
394 fn release(
395 &self,
396 _cx: &asupersync::Cx,
397 _name: &str,
398 ) -> impl std::future::Future<Output = asupersync::Outcome<(), sqlmodel_core::Error>> + Send
399 {
400 async { asupersync::Outcome::Ok(()) }
401 }
402
403 fn commit(
404 self,
405 _cx: &asupersync::Cx,
406 ) -> impl std::future::Future<Output = asupersync::Outcome<(), sqlmodel_core::Error>> + Send
407 {
408 async { asupersync::Outcome::Ok(()) }
409 }
410
411 fn rollback(
412 self,
413 _cx: &asupersync::Cx,
414 ) -> impl std::future::Future<Output = asupersync::Outcome<(), sqlmodel_core::Error>> + Send
415 {
416 async { asupersync::Outcome::Ok(()) }
417 }
418 }
419
420 #[test]
421 fn test_connection_session_builder_basic() {
422 let conn = MockConnection;
423 let session = ConnectionSession::builder().build_with(conn);
424 assert!(std::ptr::eq(session.connection(), session.connection()));
425 }
426
427 #[test]
428 fn test_connection_session_new() {
429 let conn = MockConnection;
430 let session = ConnectionSession::new(conn);
431 let _ = session.connection();
432 }
433
434 #[test]
435 fn test_connection_session_connection_access() {
436 let conn = MockConnection;
437 let mut session = ConnectionSession::new(conn);
438 let _ = session.connection();
439 let _ = session.connection_mut();
440 }
441
442 #[test]
443 fn test_connection_session_into_connection() {
444 let conn = MockConnection;
445 let session = ConnectionSession::new(conn);
446 let _recovered: MockConnection = session.into_connection();
447 }
448
449 #[cfg(feature = "console")]
450 #[test]
451 fn test_connection_session_builder_with_console() {
452 let console = SqlModelConsole::new();
453 let conn = MockConnection;
454 let session = ConnectionSession::builder()
455 .with_console(console)
456 .build_with(conn);
457 assert!(session.has_console());
458 }
459
460 #[cfg(feature = "console")]
461 #[test]
462 fn test_connection_session_builder_with_shared_console() {
463 let console = Arc::new(SqlModelConsole::new());
464 let conn1 = MockConnection;
465 let conn2 = MockConnection;
466
467 let session1 = ConnectionSession::builder()
468 .with_shared_console(console.clone())
469 .build_with(conn1);
470 let session2 = ConnectionSession::builder()
471 .with_shared_console(console)
472 .build_with(conn2);
473
474 assert!(session1.has_console());
475 assert!(session2.has_console());
476 }
477
478 #[cfg(feature = "console")]
479 #[test]
480 fn test_connection_session_builder_with_auto_console() {
481 let conn = MockConnection;
482 let session = ConnectionSession::builder()
483 .with_auto_console()
484 .build_with(conn);
485 assert!(session.has_console());
486 }
487
488 #[cfg(feature = "console")]
489 #[test]
490 fn test_connection_session_console_aware() {
491 let conn = MockConnection;
492 let mut session = ConnectionSession::new(conn);
493
494 assert!(!session.has_console());
495 assert!(session.console().is_none());
496
497 let console = Arc::new(SqlModelConsole::new());
498 session.set_console(Some(console));
499 assert!(session.has_console());
500 assert!(session.console().is_some());
501
502 session.set_console(None);
503 assert!(!session.has_console());
504 }
505
506 #[test]
507 fn test_builder_chain_fluent_api() {
508 let conn = MockConnection;
509 let builder = ConnectionSession::<MockConnection>::builder();
510 let _session = builder.build_with(conn);
511 }
512}