sqlx_cloudflare_do/
connection.rs1use std::fmt::{self, Debug, Formatter};
2use std::future::{self, Future};
3use std::str::FromStr;
4use std::time::Duration;
5
6use log::LevelFilter;
7use sqlx_core::connection::{ConnectOptions, Connection};
8use sqlx_core::error::Error;
9use sqlx_core::transaction::Transaction;
10use sqlx_core::Url;
11
12use crate::Do;
13
14pub struct DoConnection {
24 pub(crate) storage: worker::Storage,
28 pub(crate) sql: worker::SqlStorage,
30}
31
32unsafe impl Send for DoConnection {}
40unsafe impl Sync for DoConnection {}
42
43impl DoConnection {
44 #[must_use]
46 pub fn new(storage: worker::Storage) -> Self {
47 let sql = storage.sql();
48 Self { storage, sql }
49 }
50
51 #[must_use]
54 pub fn storage(&self) -> &worker::Storage {
55 &self.storage
56 }
57
58 #[must_use]
60 pub fn sql(&self) -> &worker::SqlStorage {
61 &self.sql
62 }
63
64 #[must_use]
67 pub fn into_storage(self) -> worker::Storage {
68 self.storage
69 }
70}
71
72impl From<worker::Storage> for DoConnection {
73 fn from(storage: worker::Storage) -> Self {
74 Self::new(storage)
75 }
76}
77
78impl Debug for DoConnection {
79 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
80 f.debug_struct("DoConnection").finish_non_exhaustive()
81 }
82}
83
84impl Connection for DoConnection {
85 type Database = Do;
86
87 type Options = DoConnectOptions;
88
89 fn close(self) -> impl Future<Output = Result<(), Error>> + Send + 'static {
91 future::ready(Ok(()))
92 }
93
94 fn close_hard(self) -> impl Future<Output = Result<(), Error>> + Send + 'static {
95 future::ready(Ok(()))
96 }
97
98 fn ping(&mut self) -> impl Future<Output = Result<(), Error>> + Send + '_ {
100 future::ready(Ok(()))
101 }
102
103 fn begin(&mut self) -> impl Future<Output = Result<Transaction<'_, Do>, Error>> + Send + '_ {
105 Transaction::begin(self, None)
106 }
107
108 fn shrink_buffers(&mut self) {}
109
110 fn flush(&mut self) -> impl Future<Output = Result<(), Error>> + Send + '_ {
111 future::ready(Ok(()))
112 }
113
114 fn should_flush(&self) -> bool {
115 false
116 }
117}
118
119#[derive(Debug, Clone, PartialEq, Eq)]
129#[non_exhaustive]
130pub struct DoConnectOptions;
131
132impl FromStr for DoConnectOptions {
133 type Err = Error;
134
135 fn from_str(s: &str) -> Result<Self, Error> {
136 let url = Url::parse(s).map_err(|error| Error::Configuration(error.into()))?;
137 Self::from_url(&url)
138 }
139}
140
141impl ConnectOptions for DoConnectOptions {
142 type Connection = DoConnection;
143
144 fn from_url(url: &Url) -> Result<Self, Error> {
145 if url.scheme() == "do" {
146 Ok(Self)
147 } else {
148 Err(Error::Configuration(
149 format!("expected a `do://` URL, got scheme `{}`", url.scheme()).into(),
150 ))
151 }
152 }
153
154 fn to_url_lossy(&self) -> Url {
155 Url::parse("do://").expect("a constant URL")
156 }
157
158 fn connect(&self) -> impl Future<Output = Result<DoConnection, Error>> + Send + '_ {
159 future::ready(Err(Error::Configuration(
160 "a Durable Object connection cannot be opened from a URL; build it from the object's \
161 storage with `DoConnection::new(state.storage())`"
162 .into(),
163 )))
164 }
165
166 fn log_statements(self, _level: LevelFilter) -> Self {
168 self
169 }
170
171 fn log_slow_statements(self, _level: LevelFilter, _duration: Duration) -> Self {
172 self
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use sqlx_core::connection::ConnectOptions;
179
180 use super::DoConnectOptions;
181
182 #[test]
183 fn parses_a_do_url() {
184 let options: DoConnectOptions = "do://".parse().unwrap();
185 assert_eq!(options.to_url_lossy().as_str(), "do://");
186 }
187
188 #[test]
189 fn rejects_other_schemes() {
190 assert!("sqlite://db".parse::<DoConnectOptions>().is_err());
191 assert!("d1://DB".parse::<DoConnectOptions>().is_err());
192 }
193}