use std::fmt::Display;
use bb8_postgres::{
bb8::{Pool, RunError},
tokio_postgres::{
tls::{MakeTlsConnect, TlsConnect},
Row, Socket,
},
PostgresConnectionManager,
};
use postgres_types::ToSql;
use thiserror::Error;
use crate::model::filter::{brand::Brand, category::Category, country::Country, size::Size};
const GET_BRAND_BY_NAME: &str = include_str!("sql_queries/GET_BRAND_BY_NAME.sql");
const GET_BRANDS_BY_NAME: &str = include_str!("sql_queries/GET_BRANDS_BY_NAME.sql");
const GET_CATEGORY_BY_NAME: &str = include_str!("sql_queries/GET_CATEGORY_BY_NAME.sql");
const GET_COUNTRY_BY_ISO_CODE: &str = include_str!("sql_queries/GET_COUNTRY_BY_ISO_CODE.sql");
const _GET_SIZE_BY_TITLE_AND_TYPE: &str =
include_str!("sql_queries/GET_SIZE_BY_TITLE_AND_TYPE.sql");
const GET_SIZES_FOR_CATEGORY: &str = include_str!("sql_queries/GET_SIZES_FOR_CATEGORY.sql");
#[derive(Error, Debug)]
pub enum DbError {
#[error(transparent)]
PoolError(#[from] RunError<bb8_postgres::tokio_postgres::Error>),
#[error(transparent)]
PgError(#[from] bb8_postgres::tokio_postgres::Error),
}
pub struct DbController<Tls>
where
Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static,
<Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
<Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
<<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
{
pool: Pool<PostgresConnectionManager<Tls>>,
}
impl<Tls> DbController<Tls>
where
Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static,
<Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
<Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
<<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
{
pub async fn new(uri: &str, pool_size: u32, tls: Tls) -> Result<DbController<Tls>, DbError> {
let manager = PostgresConnectionManager::new_from_stringlike(uri, tls)?;
let pool = Pool::builder().max_size(pool_size).build(manager).await?;
Ok(DbController { pool })
}
pub async fn get_brand_by_name<S: AsRef<str> + Sync + ToSql>(
&self,
name: &S,
) -> Result<Brand, DbError> {
let conn = self.pool.get().await?;
let row: Row = conn.query_one(GET_BRAND_BY_NAME, &[name]).await?;
let b: Brand = row.into();
Ok(b)
}
pub async fn get_brands_by_name<S: AsRef<str> + Sync + ToSql + Display>(
&self,
name: &S,
) -> Result<Vec<Brand>, DbError> {
let conn = self.pool.get().await?;
let name_to_sql = format!("{}%", &name);
let rows: Vec<Row> = conn.query(GET_BRANDS_BY_NAME, &[&name_to_sql]).await?;
let brands: Vec<Brand> = rows.into_iter().map(|row| row.into()).collect();
Ok(brands)
}
pub async fn get_category_by_title<S: AsRef<str> + Sync + ToSql + Display>(
&self,
name: &S,
) -> Result<Category, DbError> {
let conn = self.pool.get().await?;
let row: Row = conn.query_one(GET_CATEGORY_BY_NAME, &[&name]).await?;
let cat: Category = row.into();
Ok(cat)
}
pub async fn get_country_by_iso<S: AsRef<str> + Sync + ToSql + Display>(
&self,
code: &S,
) -> Result<Country, DbError> {
let conn = self.pool.get().await?;
let row: Row = conn.query_one(GET_COUNTRY_BY_ISO_CODE, &[&code]).await?;
let country: Country = row.into();
Ok(country)
}
pub async fn get_size_by_title_and_type<
S1: AsRef<str> + Sync + ToSql + Display,
S2: AsRef<str> + Sync + ToSql + Display,
S3: AsRef<str> + Sync + ToSql + Display,
>(
&self,
lang: S1,
title: S2,
size_type: S3,
) -> Result<Size, DbError> {
let conn = self.pool.get().await?;
let col1;
let col2;
match lang.as_ref() {
"es" | "ES" | "esp" => {
col1 = "title_es";
col2 = "size_type_es";
}
"en" | "EN" | "eng" => {
col1 = "title_en";
col2 = "size_type_en";
}
"fr" | "FR" => {
col1 = "title_fr";
col2 = "size_type_fr";
}
_ => unreachable!("Invalid language"),
}
let query =
format!("SELECT * FROM SIZE WHERE {col1} = '{title}' AND {col2} = '{size_type}'");
let row: Row = conn.query_one(&query, &[]).await?;
let size: Size = row.into();
Ok(size)
}
pub async fn get_sizes_for_category(&self, category_id: i32) -> Result<Vec<Size>, DbError> {
let conn = self.pool.get().await?;
let rows: Vec<Row> = conn
.query(GET_SIZES_FOR_CATEGORY, &[&category_id])
.await
.unwrap();
let sizes: Vec<Size> = rows.into_iter().map(|row| row.into()).collect();
Ok(sizes)
}
}