Skip to main content

tank_core/
connection.rs

1use crate::{Driver, Error, Executor, Result, truncate_long};
2use anyhow::Context;
3use std::{
4    borrow::Cow,
5    future::{self, Future},
6};
7use url::Url;
8
9/// A live database handle capable of executing queries and spawning transactions.
10///
11/// Extends [`Executor`] with connection and transaction management.
12///
13/// # Lifecycle
14/// - `connect` creates (or fetches) an underlying connection. It may eagerly
15///   establish network I/O; always await it.
16/// - `begin` starts a transaction scope. Commit/rollback MUST be awaited to
17///   guarantee resource release.
18pub trait Connection: Executor {
19    fn sanitize_url(_driver: &Self::Driver, mut url: Cow<'static, str>) -> Result<Url>
20    where
21        Self: Sized,
22    {
23        let mut in_memory = false;
24        if let Some((scheme, host)) = url.split_once("://")
25            && host.starts_with(":memory:")
26        {
27            url = format!("{scheme}://localhost{}", &host[8..]).into();
28            in_memory = true;
29        }
30        let context = || format!("While trying to connect to `{}`", truncate_long!(url));
31        let mut result = Url::parse(&url).with_context(context)?;
32        if in_memory {
33            result.query_pairs_mut().append_pair("mode", "memory");
34        }
35        let names = <Self::Driver as Driver>::NAME;
36        'prefix: {
37            for name in names {
38                let prefix = format!("{}://", name);
39                if url.starts_with(&prefix) {
40                    break 'prefix prefix;
41                }
42            }
43            let error = Error::msg(format!(
44                "Connection URL must start with: {}",
45                names.join(", ")
46            ))
47            .context(context());
48            log::error!("{:#}", error);
49            return Err(error);
50        };
51        Ok(result)
52    }
53
54    /// Create a connection (or pool) to the given URL.
55    ///
56    /// Implementations may perform I/O or validation during `connect`.
57    /// Callers should treat this as a potentially expensive operation.
58    fn connect(
59        driver: &Self::Driver,
60        url: Cow<'static, str>,
61    ) -> impl Future<Output = Result<<Self::Driver as Driver>::Connection>>
62    where
63        Self: Sized;
64
65    /// Begin a transaction scope tied to this connection.
66    ///
67    /// Must await `commit` or `rollback` to finalize the scope and release resources.
68    fn begin(&mut self) -> impl Future<Output = Result<<Self::Driver as Driver>::Transaction<'_>>>;
69
70    /// Disconnect and release the underlying session(s).
71    fn disconnect(self) -> impl Future<Output = Result<()>>
72    where
73        Self: Sized,
74    {
75        future::ready(Ok(()))
76    }
77}