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)]
126pub struct DoConnectOptions {
127 _private: (),
128}
129
130impl FromStr for DoConnectOptions {
131 type Err = Error;
132
133 fn from_str(s: &str) -> Result<Self, Error> {
134 let url = Url::parse(s).map_err(|error| Error::Configuration(error.into()))?;
135 Self::from_url(&url)
136 }
137}
138
139impl ConnectOptions for DoConnectOptions {
140 type Connection = DoConnection;
141
142 fn from_url(url: &Url) -> Result<Self, Error> {
143 if url.scheme() == "do" {
144 Ok(Self { _private: () })
145 } else {
146 Err(Error::Configuration(
147 format!("expected a `do://` URL, got scheme `{}`", url.scheme()).into(),
148 ))
149 }
150 }
151
152 fn to_url_lossy(&self) -> Url {
153 Url::parse("do://").expect("a constant URL")
154 }
155
156 fn connect(&self) -> impl Future<Output = Result<DoConnection, Error>> + Send + '_ {
157 future::ready(Err(Error::Configuration(
158 "a Durable Object connection cannot be opened from a URL; build it from the object's \
159 storage with `DoConnection::new(state.storage())`"
160 .into(),
161 )))
162 }
163
164 fn log_statements(self, _level: LevelFilter) -> Self {
166 self
167 }
168
169 fn log_slow_statements(self, _level: LevelFilter, _duration: Duration) -> Self {
170 self
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use sqlx_core::connection::ConnectOptions;
177
178 use super::DoConnectOptions;
179
180 #[test]
181 fn parses_a_do_url() {
182 let options: DoConnectOptions = "do://".parse().unwrap();
183 assert_eq!(options.to_url_lossy().as_str(), "do://");
184 }
185
186 #[test]
187 fn rejects_other_schemes() {
188 assert!("sqlite://db".parse::<DoConnectOptions>().is_err());
189 assert!("d1://DB".parse::<DoConnectOptions>().is_err());
190 }
191}