Created
April 13, 2018 19:52
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![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