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    /// Validates and normalizes the connection URL, handling special cases like in-memory databases.
20    fn sanitize_url(_driver: &Self::Driver, mut url: Cow<'static, str>) -> Result<Url>
21    where
22        Self: Sized,
23    {
24        let mut in_memory = false;
25        if let Some((scheme, host)) = url.split_once("://")
26            && host.starts_with(":memory:")
27        {
28            url = format!("{scheme}://localhost{}", &host[8..]).into();
29            in_memory = true;
30        }
31        let context = || format!("While trying to connect to `{}`", truncate_long!(url));
32        let mut result = Url::parse(&url).with_context(context)?;
33        if in_memory {
34            result.query_pairs_mut().append_pair("mode", "memory");
35        }
36        let names = <Self::Driver as Driver>::NAME;
37        'prefix: {
38            for name in names {
39                let prefix = format!("{}://", name);
40                if url.starts_with(&prefix) {
41                    break 'prefix prefix;
42                }
43            }
44            let error = Error::msg(format!(
45                "Connection URL must start with: {}",
46                names.join(", ")
47            ))
48            .context(context());
49            log::error!("{:#}", error);
50            return Err(error);
51        };
52        Ok(result)
53    }
54
55    /// Establishes a connection (or pool) to the database specified by the URL.
56    ///
57    /// Implementations may perform I/O or validation during `connect`.
58    /// Callers should treat this as a potentially expensive operation.
59    fn connect(
60        driver: &Self::Driver,
61        url: Cow<'static, str>,
62    ) -> impl Future<Output = Result<<Self::Driver as Driver>::Connection>>
63    where
64        Self: Sized;
65
66    /// Starts a new transaction on this connection.
67    ///
68    /// Must await `commit` or `rollback` to finalize the scope and release resources.
69    fn begin(&mut self) -> impl Future<Output = Result<<Self::Driver as Driver>::Transaction<'_>>>;
70
71    /// Closes the connection and releases any session resources.
72    fn disconnect(self) -> impl Future<Output = Result<()>>
73    where
74        Self: Sized,
75    {
76        future::ready(Ok(()))
77    }
78}