Archived
1
0
Fork 0
This repository has been archived on 2024-07-25. You can view files and clone it, but cannot push or open issues or pull requests.
migra/migra-cli/src/database.rs

109 lines
2.8 KiB
Rust
Raw Normal View History

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>;
}
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>;
}
pub struct PostgresConnection {
2021-02-13 23:44:41 +03:00
client: Client,
2021-01-31 13:39:00 +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> {
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)?;
Ok(PostgresConnection { client })
2021-02-13 23:44:41 +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-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-15 13:06:09 +03:00
Ok(res)
2021-02-13 23:44:41 +03:00
}
}