Accept any postgres connection object kind

develop
cahe 5 years ago
parent 2025a39e62
commit 79bbed4fe0

@ -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<GetRobot, Hand
.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 id = robots::add(
&mut repo,
robots::Robot {
@ -57,12 +59,12 @@ pub async fn add_robot(pool: PgSharedPool, r: GetRobot) -> Result<GetRobot, Hand
pub async fn get_robot(pool: PgSharedPool, id: i64) -> Result<GetRobot, 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 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<GetRobot>, 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))?;

@ -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<Result<i64, HandlingError>>;
fn update(&mut self, r: Robot) -> TraitFuture<Result<(), HandlingError>>;
fn delete(&mut self, id: i64) -> TraitFuture<Result<(), HandlingError>>;
pub trait DefaultRepository<'f> {
fn add(&'f mut self, r: Robot) -> TraitFuture<'f, Result<i64, HandlingError>>;
fn get_by_id(&'f mut self, id: i64) -> TraitFuture<'f, Result<Robot, HandlingError>>;
fn update(&'f mut self, r: Robot) -> TraitFuture<Result<(), HandlingError>>;
fn delete(&'f mut self, id: i64) -> TraitFuture<Result<(), HandlingError>>;
}
pub async fn add(repo: &mut impl DefaultRepository, r: Robot) -> Result<i64, HandlingError> {
pub async fn add<'a>(
repo: &'a mut impl DefaultRepository<'a>,
r: Robot,
) -> Result<i64, HandlingError> {
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
}

@ -1 +1 @@
pub mod postgres;
pub mod robots;

@ -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<sqlx::Transaction<'static, sqlx::Postgres>>,
q: sqlx::query::Query<'a, sqlx::Postgres, sqlx::postgres::PgArguments>,
q_id: Option<i64>,
}
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<Vec<robots::Robot>, 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<robots::Robot>, 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<sqlx::Transaction<'static, sqlx::Postgres>>,
}
impl<'a> RobotsImpl<'_> {
pub fn new<'b>(tx: &'b mut Box<sqlx::Transaction<'static, sqlx::Postgres>>) -> RobotsImpl<'b> {
RobotsImpl { tx: tx }
}
async fn add(&mut self, r: robots::Robot) -> Result<i64, HandlingError> {
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<robots::Robot, HandlingError> {
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<Collection<'a>, HandlingError> {
let all = sqlx::query::<sqlx::Postgres>(
"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<Result<i64, HandlingError>> {
Box::pin(self.add(r))
}
fn update(&mut self, r: robots::Robot) -> TraitFuture<Result<(), HandlingError>> {
Box::pin(self.update(r))
}
fn delete(&mut self, id: i64) -> TraitFuture<Result<(), HandlingError>> {
Box::pin(self.delete(id))
}
}

@ -0,0 +1 @@
pub mod postgres;

@ -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<E>,
q: sqlx::query::Query<'a, sqlx::Postgres, sqlx::postgres::PgArguments>,
q_id: Option<i64>,
}
#[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<E>) -> Collection<'c, E> {
let all = sqlx::query::<sqlx::Postgres>(
"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<Vec<robots::Robot>, 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<robots::Robot>, 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<E>,
}
pub fn new<'a, E>(tx: &'a mut Box<E>) -> RobotsImpl<'a, E> {
RobotsImpl::<'a, E> { tx: tx }
}
async fn add(
tx: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
r: robots::Robot,
) -> Result<i64, HandlingError> {
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<robots::Robot, HandlingError> {
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<Result<i64, HandlingError>> {
Box::pin(add(self.tx.as_mut(), r))
}
fn get_by_id(&'c mut self, id: i64) -> TraitFuture<Result<robots::Robot, HandlingError>> {
Box::pin(get_by_id(self.tx.as_mut(), id))
}
fn update(&'c mut self, r: robots::Robot) -> TraitFuture<Result<(), HandlingError>> {
Box::pin(update(self.tx.as_mut(), r))
}
fn delete(&'c mut self, id: i64) -> TraitFuture<Result<(), HandlingError>> {
Box::pin(delete(self.tx.as_mut(), id))
}
}

@ -4,7 +4,7 @@ use std::pin::Pin;
pub mod database;
pub mod http;
pub type TraitFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub type TraitFuture<'f, T> = Pin<Box<dyn Future<Output = T> + Send + 'f>>;
#[derive(Debug)]
pub enum HandlingError {

Loading…
Cancel
Save