parsql_deadpool_postgres/crud_ops.rs
1use deadpool_postgres::{Pool, PoolError};
2use postgres::types::FromSqlOwned;
3//use postgres::types::FromSql;
4use tokio_postgres::{Error, Row, types::FromSql};
5use crate::traits::{SqlQuery, SqlParams, UpdateParams, FromRow};
6
7// Daha basit bir yaklaşım: PoolError'dan genel bir Error oluştur
8fn pool_err_to_io_err(e: PoolError) -> Error {
9 // Bu özel fonksiyon tokio_postgres'in sağladığı timeout hatasını döndürür
10 // Güzel bir çözüm değil, ama çalışır bir örnek için kullanılabilir
11 let err = Error::__private_api_timeout();
12
13 // Debug süreci için stderr'e hatayı yazdıralım
14 eprintln!("Pool bağlantı hatası: {}", e);
15
16 err
17}
18
19/// # insert
20///
21/// Deadpool bağlantı havuzunu kullanarak veritabanına yeni bir kayıt ekler.
22///
23/// ## Parametreler
24/// - `pool`: Deadpool bağlantı havuzu
25/// - `entity`: Eklenecek veri nesnesi (SqlQuery ve SqlParams trait'lerini uygulamalıdır)
26///
27/// ## Dönüş Değeri
28/// - `Result<i64, Error>`: Başarılı olursa, eklenen kayıt ID'sini döndürür; başarısız olursa, Error döndürür
29///
30/// ## Yapı Tanımı
31/// Bu fonksiyonla kullanılan yapılar aşağıdaki derive makrolarıyla işaretlenmelidir:
32///
33/// ```rust,no_run
34/// #[derive(Insertable, SqlParams)] // Gerekli makrolar
35/// #[table("tablo_adi")] // Ekleme yapılacak tablo adı
36/// pub struct VeriModeli {
37/// pub alan1: String,
38/// pub alan2: i32,
39/// // ...
40/// }
41/// ```
42///
43/// - `Insertable`: Otomatik olarak SQL INSERT ifadeleri oluşturur
44/// - `SqlParams`: Otomatik olarak SQL parametreleri oluşturur
45/// - `#[table("tablo_adi")]`: Ekleme yapılacak tablo adını belirtir
46///
47/// ## Kullanım Örneği
48/// ```rust,no_run
49/// use deadpool_postgres::{Config, Runtime, Pool};
50/// use tokio_postgres::{NoTls, Error};
51/// use parsql::tokio_postgres::pool_crud_ops::insert;
52///
53/// #[derive(Insertable, SqlParams)]
54/// #[table("users")]
55/// pub struct InsertUser {
56/// pub name: String,
57/// pub email: String,
58/// pub state: i16,
59/// }
60///
61/// #[tokio::main]
62/// async fn main() -> Result<(), Error> {
63/// let mut cfg = Config::new();
64/// cfg.host = Some("localhost".to_string());
65/// cfg.dbname = Some("test".to_string());
66///
67/// let pool = cfg.create_pool(Some(Runtime::Tokio1), NoTls).unwrap();
68///
69/// let insert_user = InsertUser {
70/// name: "John".to_string(),
71/// email: "john@example.com".to_string(),
72/// state: 1_i16,
73/// };
74///
75/// let insert_result = insert(&pool, insert_user).await?;
76/// println!("Insert result: {:?}", insert_result);
77/// Ok(())
78/// }
79/// ```
80// pub async fn insert<T: SqlQuery + SqlParams, P:for<'a> FromSql<'a> + Send + Sync>(
81// pool: &Pool,
82// entity: T,
83// ) -> Result<P, Error> {
84// let client = pool.get().await.map_err(pool_err_to_io_err)?;
85// let sql = T::query();
86
87// if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
88// println!("[PARSQL-TOKIO-POSTGRES-POOL] Execute SQL: {}", sql);
89// }
90
91// let params = entity.params();
92// let row = client.query_one(&sql, ¶ms).await?;
93// row.try_get::<_, P>(0)
94// }
95
96pub async fn insert<T, P>(
97 pool: &Pool,
98 entity: T,
99) -> Result<P, Error>
100where
101 T: SqlQuery + SqlParams,
102 P: FromSqlOwned + Send + Sync,
103{
104 let client = pool.get().await.map_err(pool_err_to_io_err)?;
105 let sql = T::query();
106
107 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
108 println!("[PARSQL-TOKIO-POSTGRES-POOL] Execute SQL: {}", sql);
109 }
110
111 let params = entity.params();
112 let row = client.query_one(&sql, ¶ms).await?;
113 row.try_get::<_, P>(0)
114}
115
116/// # update
117///
118/// Deadpool bağlantı havuzunu kullanarak veritabanındaki mevcut bir kaydı günceller.
119///
120/// ## Parametreler
121/// - `pool`: Deadpool bağlantı havuzu
122/// - `entity`: Güncelleme bilgilerini içeren veri nesnesi (SqlQuery ve UpdateParams trait'lerini uygulamalıdır)
123///
124/// ## Dönüş Değeri
125/// - `Result<bool, Error>`: Başarılı olursa, true döndürür; başarısız olursa, Error döndürür
126///
127/// ## Yapı Tanımı
128/// Bu fonksiyonla kullanılan yapılar aşağıdaki derive makrolarıyla işaretlenmelidir:
129///
130/// ```rust,no_run
131/// #[derive(Updateable, UpdateParams)] // Gerekli makrolar
132/// #[table("tablo_adi")] // Güncellenecek tablo adı
133/// #[update("alan1, alan2")] // Güncellenecek alanlar (isteğe bağlı)
134/// #[where_clause("id = $")] // Güncelleme koşulu
135/// pub struct VeriModeli {
136/// pub id: i32, // Koşulda kullanılan alanlar
137/// pub alan1: String, // Güncellenecek alanlar
138/// pub alan2: i32, // Güncellenecek alanlar
139/// // ...
140/// }
141/// ```
142///
143/// - `Updateable`: Otomatik olarak SQL UPDATE ifadeleri oluşturur
144/// - `UpdateParams`: Otomatik olarak güncelleme parametreleri oluşturur
145/// - `#[table("tablo_adi")]`: Güncellenecek tablo adını belirtir
146/// - `#[update("alan1, alan2")]`: Hangi alanların güncelleneceğini belirtir (belirtilmezse, tüm alanlar güncellenir)
147/// - `#[where_clause("id = $")]`: Güncelleme koşulunu belirtir (`$` parametre değeri ile değiştirilir)
148///
149/// ## Kullanım Örneği
150/// ```rust,no_run
151/// use deadpool_postgres::{Config, Runtime, Pool};
152/// use tokio_postgres::{NoTls, Error};
153/// use parsql::tokio_postgres::pool_crud_ops::update;
154///
155/// #[derive(Updateable, UpdateParams)]
156/// #[table("users")]
157/// #[update("name, email")]
158/// #[where_clause("id = $")]
159/// pub struct UpdateUser {
160/// pub id: i32,
161/// pub name: String,
162/// pub email: String,
163/// pub state: i16, // update özniteliğinde belirtilmediği için bu alan güncellenmez
164/// }
165///
166/// #[tokio::main]
167/// async fn main() -> Result<(), Error> {
168/// let mut cfg = Config::new();
169/// cfg.host = Some("localhost".to_string());
170/// cfg.dbname = Some("test".to_string());
171///
172/// let pool = cfg.create_pool(Some(Runtime::Tokio1), NoTls).unwrap();
173///
174/// let update_user = UpdateUser {
175/// id: 1,
176/// name: String::from("John"),
177/// email: String::from("john@example.com"),
178/// state: 2,
179/// };
180///
181/// let update_result = update(&pool, update_user).await?;
182/// println!("Update result: {:?}", update_result);
183/// Ok(())
184/// }
185/// ```
186pub async fn update<T: SqlQuery + UpdateParams>(
187 pool: &Pool,
188 entity: T,
189) -> Result<bool, Error> {
190 let client = pool.get().await.map_err(pool_err_to_io_err)?;
191 let sql = T::query();
192
193 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
194 println!("[PARSQL-TOKIO-POSTGRES-POOL] Execute SQL: {}", sql);
195 }
196
197 let params = entity.params();
198 match client.execute(&sql, ¶ms).await {
199 Ok(_) => Ok(true),
200 Err(e) => Err(e),
201 }
202}
203
204/// # delete
205///
206/// Deadpool bağlantı havuzunu kullanarak veritabanından bir kaydı siler.
207///
208/// ## Parametreler
209/// - `pool`: Deadpool bağlantı havuzu
210/// - `entity`: Silme bilgilerini içeren veri nesnesi (SqlQuery ve SqlParams trait'lerini uygulamalıdır)
211///
212/// ## Dönüş Değeri
213/// - `Result<u64, Error>`: Başarılı olursa, silinen kayıt sayısını döndürür; başarısız olursa, Error döndürür
214///
215/// ## Yapı Tanımı
216/// Bu fonksiyonla kullanılan yapılar aşağıdaki derive makrolarıyla işaretlenmelidir:
217///
218/// ```rust,no_run
219/// #[derive(Deletable, SqlParams)] // Gerekli makrolar
220/// #[table("tablo_adi")] // Silinecek tablo adı
221/// #[where_clause("id = $")] // Silme koşulu
222/// pub struct VeriModeli {
223/// pub id: i32, // Koşulda kullanılan alanlar
224/// // Diğer alanlar eklenebilir, ancak genellikle sadece koşul alanları gereklidir
225/// }
226/// ```
227///
228/// - `Deletable`: Otomatik olarak SQL DELETE ifadeleri oluşturur
229/// - `SqlParams`: Otomatik olarak SQL parametreleri oluşturur
230/// - `#[table("tablo_adi")]`: Silinecek tablo adını belirtir
231/// - `#[where_clause("id = $")]`: Silme koşulunu belirtir (`$` parametre değeri ile değiştirilir)
232///
233/// ## Kullanım Örneği
234/// ```rust,no_run
235/// use deadpool_postgres::{Config, Runtime, Pool};
236/// use tokio_postgres::{NoTls, Error};
237/// use parsql::tokio_postgres::pool_crud_ops::delete;
238///
239/// #[derive(Deletable, SqlParams)]
240/// #[table("users")]
241/// #[where_clause("id = $")]
242/// pub struct DeleteUser {
243/// pub id: i32,
244/// }
245///
246/// #[tokio::main]
247/// async fn main() -> Result<(), Error> {
248/// let mut cfg = Config::new();
249/// cfg.host = Some("localhost".to_string());
250/// cfg.dbname = Some("test".to_string());
251///
252/// let pool = cfg.create_pool(Some(Runtime::Tokio1), NoTls).unwrap();
253///
254/// let delete_user = DeleteUser { id: 6 };
255/// let delete_result = delete(&pool, delete_user).await?;
256///
257/// println!("Delete result: {:?}", delete_result);
258/// Ok(())
259/// }
260/// ```
261pub async fn delete<T: SqlQuery + SqlParams>(
262 pool: &Pool,
263 entity: T,
264) -> Result<u64, Error> {
265 let client = pool.get().await.map_err(pool_err_to_io_err)?;
266 let sql = T::query();
267
268 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
269 println!("[PARSQL-TOKIO-POSTGRES-POOL] Execute SQL: {}", sql);
270 }
271
272 let params = entity.params();
273 match client.execute(&sql, ¶ms).await {
274 Ok(rows_affected) => Ok(rows_affected),
275 Err(e) => Err(e),
276 }
277}
278
279/// # get
280///
281/// Deadpool bağlantı havuzunu kullanarak veritabanından bir kaydı alır.
282///
283/// ## Parametreler
284/// - `pool`: Deadpool bağlantı havuzu
285/// - `params`: Sorgu parametrelerini içeren veri nesnesi (SqlQuery, FromRow ve SqlParams trait'lerini uygulamalıdır)
286///
287/// ## Dönüş Değeri
288/// - `Result<T, Error>`: Başarılı olursa, alınan kaydı döndürür; başarısız olursa, Error döndürür
289///
290/// ## Yapı Tanımı
291/// Bu fonksiyonla kullanılan yapılar aşağıdaki derive makrolarıyla işaretlenmelidir:
292///
293/// ```rust,no_run
294/// #[derive(Queryable, SqlParams, FromRow)] // Gerekli makrolar
295/// #[table("tablo_adi")] // Sorgulanacak tablo adı
296/// #[where_clause("id = $")] // Sorgu koşulu
297/// pub struct VeriModeli {
298/// pub id: i32, // Koşulda kullanılan alanlar
299/// pub alan1: String, // Getirilen veri alanları
300/// pub alan2: i32, // Getirilen veri alanları
301/// // ...
302/// }
303/// ```
304///
305/// - `Queryable`: Otomatik olarak SQL SELECT ifadeleri oluşturur
306/// - `SqlParams`: Otomatik olarak SQL parametreleri oluşturur
307/// - `FromRow`: Veritabanı satırını yapıya dönüştürür
308/// - `#[table("tablo_adi")]`: Sorgulanacak tablo adını belirtir
309/// - `#[where_clause("id = $")]`: Sorgu koşulunu belirtir (`$` parametre değeri ile değiştirilir)
310///
311/// ## Kullanım Örneği
312/// ```rust,no_run
313/// use deadpool_postgres::{Config, Runtime, Pool};
314/// use tokio_postgres::{NoTls, Error};
315/// use parsql::tokio_postgres::pool_crud_ops::get;
316///
317/// #[derive(Queryable, SqlParams, FromRow)]
318/// #[table("users")]
319/// #[where_clause("id = $")]
320/// pub struct GetUser {
321/// pub id: i32,
322/// pub name: String,
323/// pub email: String,
324/// pub state: i16,
325/// }
326///
327/// impl GetUser {
328/// pub fn new(id: i32) -> Self {
329/// Self {
330/// id,
331/// name: String::new(),
332/// email: String::new(),
333/// state: 0,
334/// }
335/// }
336/// }
337///
338/// #[tokio::main]
339/// async fn main() -> Result<(), Error> {
340/// let mut cfg = Config::new();
341/// cfg.host = Some("localhost".to_string());
342/// cfg.dbname = Some("test".to_string());
343///
344/// let pool = cfg.create_pool(Some(Runtime::Tokio1), NoTls).unwrap();
345///
346/// let user_params = GetUser::new(1);
347/// let user = get(&pool, &user_params).await?;
348///
349/// println!("User: {:?}", user);
350/// Ok(())
351/// }
352/// ```
353pub async fn get<T: SqlQuery + FromRow + SqlParams>(
354 pool: &Pool,
355 params: &T,
356) -> Result<T, Error> {
357 let client = pool.get().await.map_err(pool_err_to_io_err)?;
358 let sql = T::query();
359
360 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
361 println!("[PARSQL-TOKIO-POSTGRES-POOL] Execute SQL: {}", sql);
362 }
363
364 let params = params.params();
365 let row = client.query_one(&sql, ¶ms).await?;
366 T::from_row(&row)
367}
368
369/// # get_all
370///
371/// Deadpool bağlantı havuzunu kullanarak veritabanından birden fazla kaydı alır.
372///
373/// ## Parametreler
374/// - `pool`: Deadpool bağlantı havuzu
375/// - `params`: Sorgu parametrelerini içeren veri nesnesi (SqlQuery, FromRow ve SqlParams trait'lerini uygulamalıdır)
376///
377/// ## Dönüş Değeri
378/// - `Result<Vec<T>, Error>`: Başarılı olursa, alınan kayıtları içeren bir vektör döndürür; başarısız olursa, Error döndürür
379///
380/// ## Yapı Tanımı
381/// Bu fonksiyonla kullanılan yapılar aşağıdaki derive makrolarıyla işaretlenmelidir:
382///
383/// ```rust,no_run
384/// #[derive(Queryable, SqlParams, FromRow)] // Gerekli makrolar
385/// #[table("tablo_adi")] // Sorgulanacak tablo adı
386/// #[where_clause("state = $")] // Sorgu koşulu
387/// pub struct VeriModeli {
388/// pub id: i32, // Alınacak alanlar
389/// pub alan1: String, // Alınacak alanlar
390/// pub alan2: i32, // Alınacak alanlar
391/// pub state: i16, // Koşulda kullanılan alanlar
392/// // ...
393/// }
394/// ```
395///
396/// - `Queryable`: Otomatik olarak SQL SELECT ifadeleri oluşturur
397/// - `SqlParams`: Otomatik olarak SQL parametreleri oluşturur
398/// - `FromRow`: Veritabanı satırını yapıya dönüştürür
399/// - `#[table("tablo_adi")]`: Sorgulanacak tablo adını belirtir
400/// - `#[where_clause("state = $")]`: Sorgu koşulunu belirtir (`$` parametre değeri ile değiştirilir)
401///
402/// ## Kullanım Örneği
403/// ```rust,no_run
404/// use deadpool_postgres::{Config, Runtime, Pool};
405/// use tokio_postgres::{NoTls, Error};
406/// use parsql::tokio_postgres::pool_crud_ops::get_all;
407///
408/// #[derive(Queryable, SqlParams, FromRow)]
409/// #[table("users")]
410/// #[where_clause("state = $")]
411/// pub struct ListUsers {
412/// pub id: i32,
413/// pub name: String,
414/// pub email: String,
415/// pub state: i16,
416/// }
417///
418/// impl ListUsers {
419/// pub fn new(state: i16) -> Self {
420/// Self {
421/// id: 0,
422/// name: String::new(),
423/// email: String::new(),
424/// state,
425/// }
426/// }
427/// }
428///
429/// #[tokio::main]
430/// async fn main() -> Result<(), Error> {
431/// let mut cfg = Config::new();
432/// cfg.host = Some("localhost".to_string());
433/// cfg.dbname = Some("test".to_string());
434///
435/// let pool = cfg.create_pool(Some(Runtime::Tokio1), NoTls).unwrap();
436///
437/// let user_params = ListUsers::new(1);
438/// let users = get_all(&pool, &user_params).await?;
439///
440/// println!("Users: {:?}", users);
441/// Ok(())
442/// }
443/// ```
444pub async fn get_all<T: SqlQuery + FromRow + SqlParams>(
445 pool: &Pool,
446 params: &T,
447) -> Result<Vec<T>, Error> {
448 let client = pool.get().await.map_err(pool_err_to_io_err)?;
449 let sql = T::query();
450
451 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
452 println!("[PARSQL-TOKIO-POSTGRES-POOL] Execute SQL: {}", sql);
453 }
454
455 let params = params.params();
456 let rows = client.query(&sql, ¶ms).await?;
457
458 let mut results = Vec::with_capacity(rows.len());
459 for row in rows {
460 results.push(T::from_row(&row)?);
461 }
462
463 Ok(results)
464}
465
466/// # select
467///
468/// Deadpool bağlantı havuzunu kullanarak özel bir model dönüştürücü fonksiyon ile veritabanından bir kayıt seçer.
469///
470/// ## Parametreler
471/// - `pool`: Deadpool bağlantı havuzu
472/// - `entity`: Sorgu parametrelerini içeren veri nesnesi (SqlQuery ve SqlParams trait'lerini uygulamalıdır)
473/// - `to_model`: Satırı modele dönüştüren fonksiyon
474///
475/// ## Dönüş Değeri
476/// - `Result<R, Error>`: Başarılı olursa, dönüştürülen modeli döndürür; başarısız olursa, Error döndürür
477///
478/// ## Kullanım Örneği
479/// ```rust,no_run
480/// use deadpool_postgres::{Config, Runtime, Pool};
481/// use tokio_postgres::{NoTls, Error, Row};
482/// use parsql::tokio_postgres::pool_crud_ops::select;
483///
484/// #[derive(Queryable, SqlParams)]
485/// #[table("users")]
486/// #[where_clause("id = $")]
487/// pub struct UserQuery {
488/// pub id: i32,
489/// }
490///
491/// pub struct UserModel {
492/// pub id: i32,
493/// pub name: String,
494/// pub email: String,
495/// pub is_active: bool,
496/// }
497///
498/// impl UserQuery {
499/// pub fn new(id: i32) -> Self {
500/// Self { id }
501/// }
502/// }
503///
504/// fn row_to_user(row: &Row) -> Result<UserModel, Error> {
505/// Ok(UserModel {
506/// id: row.try_get("id")?,
507/// name: row.try_get("name")?,
508/// email: row.try_get("email")?,
509/// is_active: row.try_get::<_, i16>("state")? == 1,
510/// })
511/// }
512///
513/// #[tokio::main]
514/// async fn main() -> Result<(), Error> {
515/// let mut cfg = Config::new();
516/// cfg.host = Some("localhost".to_string());
517/// cfg.dbname = Some("test".to_string());
518///
519/// let pool = cfg.create_pool(Some(Runtime::Tokio1), NoTls).unwrap();
520///
521/// let query = UserQuery::new(1);
522/// let user = select(&pool, query, row_to_user).await?;
523///
524/// println!("User: {:?}", user);
525/// Ok(())
526/// }
527/// ```
528pub async fn select<T: SqlQuery + SqlParams, R, F>(
529 pool: &Pool,
530 entity: T,
531 to_model: F,
532) -> Result<R, Error>
533where
534 F: Fn(&Row) -> Result<R, Error>,
535{
536 let client = pool.get().await.map_err(pool_err_to_io_err)?;
537 let sql = T::query();
538
539 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
540 println!("[PARSQL-TOKIO-POSTGRES-POOL] Execute SQL: {}", sql);
541 }
542
543 let params = entity.params();
544 let row = client.query_one(&sql, ¶ms).await?;
545 to_model(&row)
546}
547
548/// # select_all
549///
550/// Deadpool bağlantı havuzunu kullanarak özel bir model dönüştürücü fonksiyon ile veritabanından birden fazla kayıt seçer.
551///
552/// ## Parametreler
553/// - `pool`: Deadpool bağlantı havuzu
554/// - `entity`: Sorgu parametrelerini içeren veri nesnesi (SqlQuery ve SqlParams trait'lerini uygulamalıdır)
555/// - `to_model`: Satırı modele dönüştüren fonksiyon
556///
557/// ## Dönüş Değeri
558/// - `Result<Vec<R>, Error>`: Başarılı olursa, dönüştürülen modelleri içeren bir vektör döndürür; başarısız olursa, Error döndürür
559///
560/// ## Kullanım Örneği
561/// ```rust,no_run
562/// use deadpool_postgres::{Config, Runtime, Pool};
563/// use tokio_postgres::{NoTls, Error, Row};
564/// use parsql::tokio_postgres::pool_crud_ops::select_all;
565///
566/// #[derive(Queryable, SqlParams)]
567/// #[table("users")]
568/// #[where_clause("state = $")]
569/// pub struct UsersQuery {
570/// pub state: i16,
571/// }
572///
573/// pub struct UserModel {
574/// pub id: i32,
575/// pub name: String,
576/// pub email: String,
577/// pub is_active: bool,
578/// }
579///
580/// impl UsersQuery {
581/// pub fn new(state: i16) -> Self {
582/// Self { state }
583/// }
584/// }
585///
586/// fn row_to_user(row: &Row) -> UserModel {
587/// UserModel {
588/// id: row.get("id"),
589/// name: row.get("name"),
590/// email: row.get("email"),
591/// is_active: row.get::<_, i16>("state") == 1,
592/// }
593/// }
594///
595/// #[tokio::main]
596/// async fn main() -> Result<(), Error> {
597/// let mut cfg = Config::new();
598/// cfg.host = Some("localhost".to_string());
599/// cfg.dbname = Some("test".to_string());
600///
601/// let pool = cfg.create_pool(Some(Runtime::Tokio1), NoTls).unwrap();
602///
603/// let query = UsersQuery::new(1);
604/// let users = select_all(&pool, query, row_to_user).await?;
605///
606/// println!("Users: {:?}", users);
607/// Ok(())
608/// }
609/// ```
610pub async fn select_all<T: SqlQuery + SqlParams, R, F>(
611 pool: &Pool,
612 entity: T,
613 to_model: F,
614) -> Result<Vec<R>, Error>
615where
616 F: Fn(&Row) -> R,
617{
618 let client = pool.get().await.map_err(pool_err_to_io_err)?;
619 let sql = T::query();
620
621 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
622 println!("[PARSQL-TOKIO-POSTGRES-POOL] Execute SQL: {}", sql);
623 }
624
625 let params = entity.params();
626 let rows = client.query(&sql, ¶ms).await?;
627
628 let mut results = Vec::with_capacity(rows.len());
629 for row in rows {
630 results.push(to_model(&row));
631 }
632
633 Ok(results)
634}