1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use crate::{Driver, Error, Executor, Result};
use std::{
borrow::Cow,
future::{self, Future},
};
use url::Url;
/// A live database handle capable of executing queries and spawning transactions.
///
/// Extends [`Executor`] with connection and transaction management.
///
/// # Lifecycle
/// - `connect` creates (or fetches) an underlying connection. It may eagerly
/// establish network I/O; always await it.
/// - `begin` starts a transaction scope. Commit/rollback MUST be awaited to
/// guarantee resource release.
pub trait Connection: Executor {
/// Validates and normalizes the connection URL, handling special cases like in-memory databases.
fn sanitize_url(_driver: &Self::Driver, mut url: Cow<'static, str>) -> Result<Url>
where
Self: Sized,
{
let mut in_memory = false;
if let Some((scheme, host)) = url.split_once("://")
&& host.starts_with(":memory:")
{
url = format!("{scheme}://localhost{}", &host[8..]).into();
in_memory = true;
}
let mut result = Url::parse(&url)?;
if in_memory {
result.query_pairs_mut().append_pair("mode", "memory");
}
let names = <Self::Driver as Driver>::NAME;
'prefix: {
for name in names {
let prefix = format!("{}://", name);
if url.starts_with(&prefix) {
break 'prefix prefix;
}
}
let error = Error::msg(format!(
"Connection URL must start with: {}",
names.join(", ")
));
log::error!("{:#}", error);
return Err(error);
};
Ok(result)
}
/// Establishes a connection (or pool) to the database specified by the URL.
///
/// Implementations may perform I/O or validation during `connect`.
/// Callers should treat this as a potentially expensive operation.
fn connect(
driver: &Self::Driver,
url: Cow<'static, str>,
) -> impl Future<Output = Result<<Self::Driver as Driver>::Connection>> + Send
where
Self: Sized;
/// Starts a new transaction on this connection.
///
/// Must await `commit` or `rollback` to finalize the scope and release resources.
fn begin(
&mut self,
) -> impl Future<Output = Result<<Self::Driver as Driver>::Transaction<'_>>> + Send;
/// Closes the connection and releases any session resources.
fn disconnect(self) -> impl Future<Output = Result<()>> + Send
where
Self: Sized,
{
future::ready(Ok(()))
}
}
impl<S: Connection> Connection for &mut S {
fn connect(
driver: &Self::Driver,
url: Cow<'static, str>,
) -> impl Future<Output = Result<<Self::Driver as Driver>::Connection>> + Send
where
Self: Sized,
{
S::connect(driver, url)
}
fn begin(
&mut self,
) -> impl Future<Output = Result<<Self::Driver as Driver>::Transaction<'_>>> + Send {
(**self).begin()
}
fn disconnect(self) -> impl Future<Output = Result<()>> + Send
where
Self: Sized,
{
future::ready(Err(Error::msg(
"Cannot disconnect using `&mut Connection`, use the actual object.",
)))
}
}