From 79bbed4fe0f63fe3788b1e00d4de68291cca048b Mon Sep 17 00:00:00 2001 From: cahe Date: Mon, 26 Oct 2020 23:19:27 -0300 Subject: [PATCH] Accept any postgres connection object kind --- src/app/robots.rs | 20 ++-- src/base/robots.rs | 27 ++++-- src/data/mod.rs | 2 +- src/data/postgres.rs | 162 ------------------------------- src/data/robots/mod.rs | 1 + src/data/robots/postgres.rs | 183 ++++++++++++++++++++++++++++++++++++ src/simple/mod.rs | 2 +- 7 files changed, 216 insertions(+), 181 deletions(-) delete mode 100644 src/data/postgres.rs create mode 100644 src/data/robots/mod.rs create mode 100644 src/data/robots/postgres.rs diff --git a/src/app/robots.rs b/src/app/robots.rs index e41cfe8..e730168 100644 --- a/src/app/robots.rs +++ b/src/app/robots.rs @@ -1,9 +1,11 @@ +use crate::base::robots::DefaultRepository; + use serde::{Deserialize, Serialize}; use crate::simple::database::PgSharedPool; use crate::base::robots; -use crate::data::postgres; +use crate::data::robots::postgres; use crate::simple::HandlingError; @@ -39,7 +41,7 @@ pub async fn add_robot(pool: PgSharedPool, r: GetRobot) -> Result Result Result { let mut tx = pool - .begin() + .acquire() .await .map(|c| Box::new(c)) .map_err(|e| HandlingError::DBError(e))?; - let mut repo = postgres::RobotsImpl::new(&mut tx); + let mut repo = postgres::new(&mut tx); Ok(repo.get_by_id(id).await?.into()) } @@ -72,13 +74,13 @@ pub async fn get_all( limit: i64, ) -> Result<(Vec, bool), HandlingError> { let mut tx = pool - .begin() + .acquire() .await .map(|c| Box::new(c)) .map_err(|e| HandlingError::DBError(e))?; - let mut repo = postgres::RobotsImpl::new(&mut tx); - let (items, next) = repo.get_all()?.paginate(offset, limit).await?; + let all = postgres::Collection::new(&mut tx); + let (items, next) = all.paginate(offset, limit).await?; let data = items.into_iter().map(|r| r.into()).collect(); @@ -96,7 +98,7 @@ pub async fn update_robot( .map(|c| Box::new(c)) .map_err(|e| HandlingError::DBError(e))?; - let mut repo = postgres::RobotsImpl::new(&mut tx); + let mut repo = postgres::new(&mut tx); let _: () = robots::update(&mut repo, (id, r).into()).await?; let robot = repo.get_by_id(id).await?; @@ -112,7 +114,7 @@ pub async fn delete_robot(pool: PgSharedPool, id: i64) -> Result<(), HandlingErr .map(|c| Box::new(c)) .map_err(|e| HandlingError::DBError(e))?; - let mut repo = postgres::RobotsImpl::new(&mut tx); + let mut repo = postgres::new(&mut tx); let _: () = robots::delete(&mut repo, id).await?; tx.commit().await.map_err(|e| HandlingError::DBError(e))?; diff --git a/src/base/robots.rs b/src/base/robots.rs index 02a8831..652e806 100644 --- a/src/base/robots.rs +++ b/src/base/robots.rs @@ -1,24 +1,35 @@ -use crate::simple::{HandlingError, TraitFuture}; +use crate::simple::HandlingError; +use crate::simple::TraitFuture; pub struct Robot { pub id: i64, pub name: String, } -pub trait DefaultRepository { - fn add(&mut self, r: Robot) -> TraitFuture>; - fn update(&mut self, r: Robot) -> TraitFuture>; - fn delete(&mut self, id: i64) -> TraitFuture>; +pub trait DefaultRepository<'f> { + fn add(&'f mut self, r: Robot) -> TraitFuture<'f, Result>; + fn get_by_id(&'f mut self, id: i64) -> TraitFuture<'f, Result>; + fn update(&'f mut self, r: Robot) -> TraitFuture>; + fn delete(&'f mut self, id: i64) -> TraitFuture>; } -pub async fn add(repo: &mut impl DefaultRepository, r: Robot) -> Result { +pub async fn add<'a>( + repo: &'a mut impl DefaultRepository<'a>, + r: Robot, +) -> Result { repo.add(r).await } -pub async fn update(repo: &mut impl DefaultRepository, r: Robot) -> Result<(), HandlingError> { +pub async fn update<'a>( + repo: &'a mut impl DefaultRepository<'a>, + r: Robot, +) -> Result<(), HandlingError> { repo.update(r).await } -pub async fn delete(repo: &mut impl DefaultRepository, id: i64) -> Result<(), HandlingError> { +pub async fn delete<'a>( + repo: &'a mut impl DefaultRepository<'a>, + id: i64, +) -> Result<(), HandlingError> { repo.delete(id).await } diff --git a/src/data/mod.rs b/src/data/mod.rs index 26e9103..ee09d7f 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -1 +1 @@ -pub mod postgres; +pub mod robots; diff --git a/src/data/postgres.rs b/src/data/postgres.rs deleted file mode 100644 index e605681..0000000 --- a/src/data/postgres.rs +++ /dev/null @@ -1,162 +0,0 @@ -use crate::simple::TraitFuture; -use std::boxed::Box; - -use crate::base::robots; -use crate::simple::HandlingError; - -use sqlx::Done; -use sqlx::FromRow; - -pub struct Collection<'a> { - tx: &'a mut Box>, - q: sqlx::query::Query<'a, sqlx::Postgres, sqlx::postgres::PgArguments>, - q_id: Option, -} - -impl Collection<'_> { - #[allow(dead_code)] - pub fn by_id(self, id: i64) -> Self { - Self { - tx: self.tx, - q: self.q, - q_id: Some(id), - } - } - - pub async fn fetch(self, offset: i64, limit: i64) -> Result, HandlingError> { - let result = self - .q - .bind(self.q_id.is_some()) - .bind(self.q_id.unwrap_or_default()) - .bind(offset) - .bind(limit) - .fetch_all(self.tx.as_mut()) - .await; - match result { - Ok(v) => v - .into_iter() - .map(|d| { - let (id, name) = <(i64, String)>::from_row(&d)?; - Ok(robots::Robot { id: id, name: name }) - }) - .collect(), - Err(e) => Err(HandlingError::DBError(e)), - } - } - - pub async fn paginate( - self, - offset: i64, - limit: i64, - ) -> Result<(Vec, bool), HandlingError> { - let mut items = self.fetch(offset, limit + 1).await?; - if items.len() > (limit as usize) { - let _ = items.pop(); - Ok((items, true)) - } else { - Ok((items, false)) - } - } -} - -pub struct RobotsImpl<'a> { - pub tx: &'a mut Box>, -} - -impl<'a> RobotsImpl<'_> { - pub fn new<'b>(tx: &'b mut Box>) -> RobotsImpl<'b> { - RobotsImpl { tx: tx } - } - - async fn add(&mut self, r: robots::Robot) -> Result { - let result = sqlx::query("INSERT INTO robots (name) VALUES ($1::TEXT) RETURNING id") - .bind(r.name) - .fetch_one(self.tx.as_mut()) - .await; - match result { - Ok(v) => { - let (id,) = <(i64,)>::from_row(&v)?; - Ok(id) - } - Err(sqlx::Error::RowNotFound) => Err(HandlingError::NotFound), - Err(e) => Err(HandlingError::DBError(e)), - } - } - - pub async fn get_by_id(&mut self, id: i64) -> Result { - let result = sqlx::query("SELECT * FROM robots WHERE id = $1::INTEGER") - .bind(id) - .fetch_one(self.tx.as_mut()) - .await; - match result { - Ok(v) => { - let (id, name) = <(i64, String)>::from_row(&v)?; - Ok(robots::Robot { id: id, name: name }) - } - Err(sqlx::Error::RowNotFound) => Err(HandlingError::NotFound), - Err(e) => Err(HandlingError::DBError(e)), - } - } - - pub fn get_all(&'a mut self) -> Result, HandlingError> { - let all = sqlx::query::( - "SELECT id, name - FROM robots - WHERE - (CASE WHEN $1::BOOLEAN THEN (id = $2::INTEGER) ELSE TRUE END) - OFFSET $3 - LIMIT $4", - ); - Ok(Collection { - tx: self.tx, - q: all, - q_id: None, - }) - } - - async fn update(&mut self, r: robots::Robot) -> Result<(), HandlingError> { - let result = sqlx::query("UPDATE robots SET name = $1::TEXT WHERE id = $2::INTEGER") - .bind(r.name) - .bind(r.id) - .execute(self.tx.as_mut()) - .await; - match result { - Ok(v) => match v.rows_affected() { - 1 => Ok(()), - 0 => Err(HandlingError::NotFound), - _ => Err(HandlingError::InternalError), - }, - Err(sqlx::Error::RowNotFound) => Err(HandlingError::NotFound), - Err(e) => Err(HandlingError::DBError(e)), - } - } - - async fn delete(&mut self, id: i64) -> Result<(), HandlingError> { - let result = sqlx::query("DELETE FROM robots WHERE id = $1::INTEGER") - .bind(id) - .execute(self.tx.as_mut()) - .await; - match result { - Ok(v) => match v.rows_affected() { - 1 => Ok(()), - 0 => Err(HandlingError::NotFound), - _ => Err(HandlingError::InternalError), - }, - Err(e) => Err(HandlingError::DBError(e)), - } - } -} - -impl robots::DefaultRepository for RobotsImpl<'_> { - fn add(&mut self, r: robots::Robot) -> TraitFuture> { - Box::pin(self.add(r)) - } - - fn update(&mut self, r: robots::Robot) -> TraitFuture> { - Box::pin(self.update(r)) - } - - fn delete(&mut self, id: i64) -> TraitFuture> { - Box::pin(self.delete(id)) - } -} diff --git a/src/data/robots/mod.rs b/src/data/robots/mod.rs new file mode 100644 index 0000000..26e9103 --- /dev/null +++ b/src/data/robots/mod.rs @@ -0,0 +1 @@ +pub mod postgres; diff --git a/src/data/robots/postgres.rs b/src/data/robots/postgres.rs new file mode 100644 index 0000000..6403128 --- /dev/null +++ b/src/data/robots/postgres.rs @@ -0,0 +1,183 @@ +use crate::simple::TraitFuture; + +use std::boxed::Box; + +use crate::base::robots; +use crate::simple::HandlingError; + +use sqlx::Done; +use sqlx::FromRow; + +pub struct Collection<'a, E> { + tx: &'a mut Box, + q: sqlx::query::Query<'a, sqlx::Postgres, sqlx::postgres::PgArguments>, + q_id: Option, +} + +#[allow(dead_code)] +impl<'c, E> Collection<'c, E> +where + &'c mut E: sqlx::Executor<'c, Database = sqlx::Postgres> + 'c, +{ + pub fn new(tx: &'c mut Box) -> Collection<'c, E> { + let all = sqlx::query::( + "SELECT id, name + FROM robots + WHERE + (CASE WHEN $1::BOOLEAN THEN (id = $2::INTEGER) ELSE TRUE END) + OFFSET $3 + LIMIT $4", + ); + Self { + tx: tx, + q: all, + q_id: None, + } + } + + pub fn by_id(self, id: i64) -> Self { + Self { + tx: self.tx, + q: self.q, + q_id: Some(id), + } + } + + pub async fn fetch(self, offset: i64, limit: i64) -> Result, HandlingError> { + let result = self + .q + .bind(self.q_id.is_some()) + .bind(self.q_id.unwrap_or_default()) + .bind(offset) + .bind(limit) + .fetch_all(self.tx.as_mut()) + .await; + match result { + Ok(v) => v + .into_iter() + .map(|d| { + let (id, name) = <(i64, String)>::from_row(&d)?; + Ok(robots::Robot { id: id, name: name }) + }) + .collect(), + Err(e) => Err(HandlingError::DBError(e)), + } + } + + pub async fn paginate( + self, + offset: i64, + limit: i64, + ) -> Result<(Vec, bool), HandlingError> { + let mut items = self.fetch(offset, limit + 1).await?; + if items.len() > (limit as usize) { + let _ = items.pop(); + Ok((items, true)) + } else { + Ok((items, false)) + } + } +} + +pub struct RobotsImpl<'a, E> { + pub tx: &'a mut Box, +} + +pub fn new<'a, E>(tx: &'a mut Box) -> RobotsImpl<'a, E> { + RobotsImpl::<'a, E> { tx: tx } +} + +async fn add( + tx: impl sqlx::Executor<'_, Database = sqlx::Postgres>, + r: robots::Robot, +) -> Result { + let result = sqlx::query("INSERT INTO robots (name) VALUES ($1::TEXT) RETURNING id") + .bind(r.name) + .fetch_one(tx) + .await; + match result { + Ok(v) => { + let (id,) = <(i64,)>::from_row(&v)?; + Ok(id) + } + Err(sqlx::Error::RowNotFound) => Err(HandlingError::NotFound), + Err(e) => Err(HandlingError::DBError(e)), + } +} + +pub async fn get_by_id( + tx: impl sqlx::Executor<'_, Database = sqlx::Postgres>, + id: i64, +) -> Result { + let result = sqlx::query("SELECT * FROM robots WHERE id = $1::INTEGER") + .bind(id) + .fetch_one(tx) + .await; + match result { + Ok(v) => { + let (id, name) = <(i64, String)>::from_row(&v)?; + Ok(robots::Robot { id: id, name: name }) + } + Err(sqlx::Error::RowNotFound) => Err(HandlingError::NotFound), + Err(e) => Err(HandlingError::DBError(e)), + } +} + +async fn update( + tx: impl sqlx::Executor<'_, Database = sqlx::Postgres>, + r: robots::Robot, +) -> Result<(), HandlingError> { + let result = sqlx::query("UPDATE robots SET name = $1::TEXT WHERE id = $2::INTEGER") + .bind(r.name) + .bind(r.id) + .execute(tx) + .await; + match result { + Ok(v) => match v.rows_affected() { + 1 => Ok(()), + 0 => Err(HandlingError::NotFound), + _ => Err(HandlingError::InternalError), + }, + Err(sqlx::Error::RowNotFound) => Err(HandlingError::NotFound), + Err(e) => Err(HandlingError::DBError(e)), + } +} + +async fn delete( + tx: impl sqlx::Executor<'_, Database = sqlx::Postgres>, + id: i64, +) -> Result<(), HandlingError> { + let result = sqlx::query("DELETE FROM robots WHERE id = $1::INTEGER") + .bind(id) + .execute(tx) + .await; + match result { + Ok(v) => match v.rows_affected() { + 1 => Ok(()), + 0 => Err(HandlingError::NotFound), + _ => Err(HandlingError::InternalError), + }, + Err(e) => Err(HandlingError::DBError(e)), + } +} + +impl<'a, 'c, E> robots::DefaultRepository<'c> for RobotsImpl<'a, E> +where + &'c mut E: sqlx::Executor<'c, Database = sqlx::Postgres> + 'c, +{ + fn add(&'c mut self, r: robots::Robot) -> TraitFuture> { + Box::pin(add(self.tx.as_mut(), r)) + } + + fn get_by_id(&'c mut self, id: i64) -> TraitFuture> { + Box::pin(get_by_id(self.tx.as_mut(), id)) + } + + fn update(&'c mut self, r: robots::Robot) -> TraitFuture> { + Box::pin(update(self.tx.as_mut(), r)) + } + + fn delete(&'c mut self, id: i64) -> TraitFuture> { + Box::pin(delete(self.tx.as_mut(), id)) + } +} diff --git a/src/simple/mod.rs b/src/simple/mod.rs index 141a61d..0819bf6 100644 --- a/src/simple/mod.rs +++ b/src/simple/mod.rs @@ -4,7 +4,7 @@ use std::pin::Pin; pub mod database; pub mod http; -pub type TraitFuture<'a, T> = Pin + Send + 'a>>; +pub type TraitFuture<'f, T> = Pin + Send + 'f>>; #[derive(Debug)] pub enum HandlingError {