1use std::future::IntoFuture;
2use std::marker::PhantomData;
3use std::pin::Pin;
4use std::sync::Arc;
5
6use crate::{
7 config::Config,
8 error::Error,
9 manager::Manager,
10 strategy::{RoundRobin, Strategy},
11 topology::view::Topology,
12};
13
14#[derive(Clone)]
16pub struct Client {
17 manager: Arc<Manager>,
18}
19
20impl Client {
21 pub async fn new(config: Config) -> Result<Self, Error> {
27 let manager = Arc::new(Manager::new(config, RoundRobin::new()).await?);
28 Ok(Self { manager })
29 }
30
31 pub async fn new_with_strategy<S>(config: Config, strategy: S) -> Result<Self, Error>
37 where
38 S: Strategy,
39 {
40 let manager = Arc::new(Manager::new(config, strategy).await?);
41 Ok(Self { manager })
42 }
43
44 pub fn query<'a>(
47 &'a self,
48 sql: &'a str,
49 params: &'a [&'a (dyn pg::types::ToSql + Sync)],
50 ) -> Query<'a, Vec<pg::Row>> {
51 Query::new(&self.manager, sql, params)
52 }
53
54 pub fn execute<'a>(
57 &'a self,
58 sql: &'a str,
59 params: &'a [&'a (dyn pg::types::ToSql + Sync)],
60 ) -> Query<'a, u64> {
61 Query::new(&self.manager, sql, params)
62 }
63
64 pub async fn topology(&self) -> Result<Topology, Error> {
71 self.manager.topology().await
72 }
73
74 pub async fn ping(&self) -> Result<(), Error> {
80 self.manager
81 .map(|pool| async move {
82 let client = pool.get().await?;
83 client.simple_query("SELECT 1").await?;
84 Ok(())
85 })
86 .await?;
87 Ok(())
88 }
89
90 pub async fn close(&self) -> Result<(), Error> {
96 self.manager.close().await
97 }
98}
99
100pub struct Query<'a, T> {
104 manager: &'a Manager,
105 sql: &'a str,
106 params: &'a [&'a (dyn pg::types::ToSql + Sync)],
107 on_master: bool,
108 bucket_id: Option<u64>,
109 instance: Option<&'a str>,
110 marker: PhantomData<T>,
111}
112
113impl<'a, T> Query<'a, T> {
114 fn new(
116 manager: &'a Arc<Manager>,
117 sql: &'a str,
118 params: &'a [&'a (dyn pg::types::ToSql + Sync)],
119 ) -> Self {
120 Self {
121 manager,
122 sql,
123 params,
124 on_master: true,
125 bucket_id: None,
126 instance: None,
127 marker: PhantomData,
128 }
129 }
130
131 #[must_use]
133 pub fn on_master(self, on_master: bool) -> RoutedQuery<'a, T> {
134 RoutedQuery(self).on_master(on_master)
135 }
136
137 #[must_use]
139 pub fn bucket_id(self, bucket_id: u64) -> RoutedQuery<'a, T> {
140 RoutedQuery(self).bucket_id(bucket_id)
141 }
142
143 #[must_use]
145 pub fn instance(mut self, name: &'a str) -> PinnedQuery<'a, T> {
146 self.instance = Some(name);
147 PinnedQuery(self)
148 }
149
150 async fn pool(&self) -> Result<Arc<pgpool::Pool>, Error> {
152 match self.instance {
153 Some(name) => self.manager.route_to(name).await,
154 None => {
155 self.manager
156 .route(self.sql, self.params, self.on_master, self.bucket_id)
157 .await
158 }
159 }
160 }
161}
162
163pub struct RoutedQuery<'a, T>(Query<'a, T>);
186
187impl<T> RoutedQuery<'_, T> {
188 #[must_use]
190 pub fn on_master(mut self, on_master: bool) -> Self {
191 self.0.on_master = on_master;
192 self
193 }
194
195 #[must_use]
197 pub fn bucket_id(mut self, bucket_id: u64) -> Self {
198 self.0.bucket_id = Some(bucket_id);
199 self
200 }
201}
202
203impl<'a, T> IntoFuture for RoutedQuery<'a, T>
204where
205 Query<'a, T>: IntoFuture,
206{
207 type Output = <Query<'a, T> as IntoFuture>::Output;
208
209 type IntoFuture = <Query<'a, T> as IntoFuture>::IntoFuture;
210
211 fn into_future(self) -> Self::IntoFuture {
212 self.0.into_future()
213 }
214}
215
216pub struct PinnedQuery<'a, T>(Query<'a, T>);
231
232impl<'a, T> IntoFuture for PinnedQuery<'a, T>
233where
234 Query<'a, T>: IntoFuture,
235{
236 type Output = <Query<'a, T> as IntoFuture>::Output;
237
238 type IntoFuture = <Query<'a, T> as IntoFuture>::IntoFuture;
239
240 fn into_future(self) -> Self::IntoFuture {
241 self.0.into_future()
242 }
243}
244
245impl<'a> IntoFuture for Query<'a, Vec<pg::Row>> {
246 type Output = Result<Vec<pg::Row>, Error>;
247
248 type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>;
249
250 fn into_future(self) -> Self::IntoFuture {
251 Box::pin(async move {
252 let pool = self.pool().await?;
253 let client = pool.get().await?;
254 match client.query(self.sql, self.params).await {
255 Ok(rows) => Ok(rows),
256 Err(e) => {
257 self.manager.on_error(self.sql, &e).await;
258 Err(e.into())
259 }
260 }
261 })
262 }
263}
264
265impl<'a> IntoFuture for Query<'a, u64> {
266 type Output = Result<u64, Error>;
267
268 type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>;
269
270 fn into_future(self) -> Self::IntoFuture {
271 Box::pin(async move {
272 let pool = self.pool().await?;
273 let client = pool.get().await?;
274 match client.execute(self.sql, self.params).await {
275 Ok(n) => Ok(n),
276 Err(e) => {
277 self.manager.on_error(self.sql, &e).await;
278 Err(e.into())
279 }
280 }
281 })
282 }
283}