2021-02-13 23:44:41 +03:00
|
|
|
use crate::config::Config;
|
|
|
|
use crate::StdResult;
|
2021-02-15 13:06:09 +03:00
|
|
|
use postgres::{Client, NoTls};
|
2021-02-13 23:44:41 +03:00
|
|
|
use std::convert::TryFrom;
|
2021-01-31 13:39:00 +03:00
|
|
|
|
2021-02-15 13:06:09 +03:00
|
|
|
pub trait ToSql {
|
|
|
|
fn to_sql(&self) -> String;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ToSql for &str {
|
|
|
|
fn to_sql(&self) -> String {
|
|
|
|
format!(r#""{}""#, self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait TryFromSql<QueryResultRow>: Sized {
|
|
|
|
fn try_from_sql(row: QueryResultRow) -> StdResult<Self>;
|
|
|
|
}
|
|
|
|
|
2021-02-15 13:47:45 +03:00
|
|
|
impl TryFromSql<postgres::Row> for String {
|
|
|
|
fn try_from_sql(row: postgres::Row) -> StdResult<Self> {
|
|
|
|
let res: String = row.get(0);
|
|
|
|
Ok(res)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-15 13:06:09 +03:00
|
|
|
pub trait DatabaseConnection: Sized {
|
|
|
|
type QueryResultRow;
|
|
|
|
type QueryResult;
|
|
|
|
|
|
|
|
fn open(connection_string: &str) -> StdResult<Self>;
|
|
|
|
|
|
|
|
fn batch_execute(&mut self, query: &str) -> StdResult<()>;
|
|
|
|
|
|
|
|
fn execute<'b>(&mut self, query: &str, params: &'b [&'b dyn ToSql]) -> StdResult<u64>;
|
|
|
|
|
|
|
|
fn query<'b, OutputItem>(
|
|
|
|
&mut self,
|
|
|
|
query: &str,
|
|
|
|
params: &'b [&'b dyn ToSql],
|
|
|
|
) -> StdResult<Vec<OutputItem>>
|
|
|
|
where
|
|
|
|
OutputItem: ?Sized + TryFromSql<Self::QueryResultRow>;
|
|
|
|
}
|
|
|
|
|
2021-02-14 12:10:12 +03:00
|
|
|
pub struct PostgresConnection {
|
2021-02-13 23:44:41 +03:00
|
|
|
client: Client,
|
2021-01-31 13:39:00 +03:00
|
|
|
}
|
|
|
|
|
2021-02-14 12:10:12 +03:00
|
|
|
impl TryFrom<&Config> for PostgresConnection {
|
2021-02-13 23:44:41 +03:00
|
|
|
type Error = Box<dyn std::error::Error>;
|
|
|
|
|
|
|
|
fn try_from(config: &Config) -> Result<Self, Self::Error> {
|
2021-02-14 12:10:12 +03:00
|
|
|
PostgresConnection::open(&config.database_connection_string()?)
|
2021-02-13 23:44:41 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-15 13:06:09 +03:00
|
|
|
impl DatabaseConnection for PostgresConnection {
|
|
|
|
type QueryResultRow = postgres::Row;
|
|
|
|
type QueryResult = Vec<Self::QueryResultRow>;
|
|
|
|
|
|
|
|
fn open(connection_string: &str) -> StdResult<Self> {
|
2021-02-13 23:44:41 +03:00
|
|
|
let client = Client::connect(connection_string, NoTls)?;
|
2021-02-14 12:10:12 +03:00
|
|
|
Ok(PostgresConnection { client })
|
2021-02-13 23:44:41 +03:00
|
|
|
}
|
2021-02-06 01:22:00 +03:00
|
|
|
|
2021-02-15 13:06:09 +03:00
|
|
|
fn batch_execute(&mut self, query: &str) -> StdResult<()> {
|
|
|
|
self.client.batch_execute(query)?;
|
|
|
|
Ok(())
|
2021-02-13 23:44:41 +03:00
|
|
|
}
|
2021-02-06 01:22:00 +03:00
|
|
|
|
2021-02-15 13:06:09 +03:00
|
|
|
fn execute<'b>(&mut self, query: &str, params: &'b [&'b dyn ToSql]) -> StdResult<u64> {
|
|
|
|
let stmt = params
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.fold(query.to_string(), |acc, (i, p)| {
|
|
|
|
str::replace(&acc, &format!("${}", i), &p.to_sql())
|
|
|
|
});
|
2021-02-13 23:44:41 +03:00
|
|
|
|
2021-02-15 13:06:09 +03:00
|
|
|
let res = self.client.execute(stmt.as_str(), &[])?;
|
|
|
|
Ok(res)
|
2021-02-13 23:44:41 +03:00
|
|
|
}
|
|
|
|
|
2021-02-15 13:06:09 +03:00
|
|
|
fn query<'b, OutputItem>(
|
|
|
|
&mut self,
|
|
|
|
query: &str,
|
|
|
|
params: &'b [&'b dyn ToSql],
|
|
|
|
) -> StdResult<Vec<OutputItem>>
|
|
|
|
where
|
|
|
|
OutputItem: ?Sized + TryFromSql<Self::QueryResultRow>,
|
|
|
|
{
|
|
|
|
let stmt = params
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.fold(query.to_string(), |acc, (i, p)| {
|
|
|
|
str::replace(&acc, &format!("${}", i), &p.to_sql())
|
|
|
|
});
|
|
|
|
|
|
|
|
let res: Self::QueryResult = self.client.query(stmt.as_str(), &[])?;
|
|
|
|
|
|
|
|
let res = res
|
|
|
|
.into_iter()
|
|
|
|
.map(OutputItem::try_from_sql)
|
|
|
|
.collect::<Result<Vec<OutputItem>, _>>()?;
|
2021-02-06 01:37:30 +03:00
|
|
|
|
2021-02-15 13:06:09 +03:00
|
|
|
Ok(res)
|
2021-02-13 23:44:41 +03:00
|
|
|
}
|
2021-02-06 01:37:30 +03:00
|
|
|
}
|