Skip to content

Instantly share code, notes, and snippets.

@danneu
Created April 13, 2018 19:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danneu/23fab766d541eeec6b26a1c4e919e931 to your computer and use it in GitHub Desktop.
Save danneu/23fab766d541eeec6b26a1c4e919e931 to your computer and use it in GitHub Desktop.
Creates a Conn wrapper around postgres::Connection and r2d2::PooledConnection so db fns work with both.
#![allow(warnings)]
// This example stubs out a Conn wrapper around postgres::Connection
// and r2d2_postgres::PooledConnection so that my database functions
// like `insert_user(&conn)` can take my wrapper as an argument and
// work regardless of the underlying connection type.
//
// Example:
//
// // conn is r2d2::PooledConnection
// let conn = pool.get().unwrap();
// insert_user(&conn, "foo").unwrap();
//
// // conn is r2d2::PooledConnection,
// // but tx.connection() is &postgres::Connection
// let conn = pool.get().unwrap();
// let tx = conn.transaction().unwrap();
// insert_user(tx.connection(), "foo").unwrap();
// tx.commit().unwrap();
mod lib1 {
pub struct PooledConnection;
impl PooledConnection {
pub fn execute(&self, sql: &str, params: &[&str]) -> usize { 42 }
}
}
mod lib2 {
pub struct Connection;
impl Connection {
pub fn execute(&self, sql: &str, params: &[&str]) -> usize { 100 }
}
}
enum Inner<'conn> {
PooledConn(&'conn lib1::PooledConnection),
Conn(&'conn lib2::Connection),
}
struct Conn<'conn> {
inner: Inner<'conn>
}
impl <'conn> Conn<'conn> {
// I'll have to manually proxy the assoc fns that both underlying conns
// have in common (execute, query, ...).
fn execute(&self, sql: &str, params: &[&str]) -> usize {
match self.inner {
Inner::PooledConn(conn) => conn.execute(sql, params),
Inner::Conn(conn) => conn.execute(sql, params),
}
}
}
impl <'conn> From<&'conn lib1::PooledConnection> for Conn<'conn> {
fn from(x: &'conn lib1::PooledConnection) -> Self {
Conn { inner: Inner::PooledConn(x) }
}
}
impl <'conn> From<&'conn lib2::Connection> for Conn<'conn> {
fn from(x: &'conn lib2::Connection) -> Self {
Conn { inner: Inner::Conn(x) }
}
}
fn insert_user<'conn, C: Into<Conn<'conn>>>(conn: C, username: &str) {
// Downside: Have to conn.into() in every fn body.
let conn = conn.into();
conn.execute("insert into users (username) values ($1)", &[username]);
}
/// Dumb example of a transaction
/// Commented out because it prevents example from compiling.
// fn insert_parents<'conn, C: Into<Conn<'conn>>>(conn: C, father: &str, mother: &str) {
// let conn = conn.into();
// let tx = conn.transaction();
// insert_user(tx.connection(), father);
// insert_user(tx.connection(), mother);
// tx.commit()?;
// }
fn main() {
// Can pass in Conn's directly
let conn1: Conn = Conn::from(&lib1::PooledConnection);
let conn2: Conn = Conn::from(&lib2::Connection);
insert_user(conn1, "foo");
insert_user(conn2, "bar");
// Or pass in a underlying reference that can be turned into a Conn
let conn1: lib1::PooledConnection = lib1::PooledConnection;
let conn2: lib2::Connection = lib2::Connection;
insert_user(&conn1, "foo");
insert_user(&conn2, "bar");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment