use ::futures::Future;
use mysql_async::error::Error;
use mysql_async::prelude::*;
use mysql_async::{MyFuture, Opts, Pool, QueryResult};
use std::env;
use std::io;
fn run<F, T, U>(future: F) -> Result<T, U>
where
F: Future<Item = T, Error = U> + Send + 'static,
T: Send + 'static,
U: Send + 'static,
{
let mut runtime = tokio::runtime::Runtime::new().unwrap();
let result = runtime.block_on(future);
runtime.shutdown_on_idle().wait().unwrap();
result
}
fn get_url() -> String {
if let Ok(url) = env::var("DATABASE_URL") {
let opts = Opts::from_url(&url).expect("DATABASE_URL invalid");
if opts
.get_db_name()
.expect("a database name is required")
.is_empty()
{
panic!("database name is empty");
}
url
} else {
"mysql://root:password@127.0.0.1:3307/mysql".into()
}
}
pub fn get_all_results<TupleType, T, P>(result: QueryResult<T, P>) -> impl MyFuture<Vec<TupleType>>
where
TupleType: FromRow + Send + 'static,
P: Protocol + Send + 'static,
T: ConnectionLike + Sized + Send + 'static,
{
result.collect().map(|(_, data)| data)
}
pub fn get_single_result<TupleType, T, P>(result: QueryResult<T, P>) -> impl MyFuture<TupleType>
where
TupleType: FromRow + Send + 'static,
P: Protocol + Send + 'static,
T: ConnectionLike + Sized + Send + 'static,
{
get_all_results(result).and_then(|mut data| {
if data.len() != 1 {
Err(Error::from(io::Error::from(io::ErrorKind::InvalidData)))
} else {
Ok(data.remove(0))
}
})
}
#[test]
fn use_generic_code() {
let pool = Pool::new(Opts::from_url(&*get_url()).unwrap());
let fut = pool
.get_conn()
.and_then(move |conn| conn.query("SELECT 1, 2, 3"))
.and_then(get_single_result::<(u8, u8, u8), _, _>)
.and_then(|out| pool.disconnect().map(move |_| out));
let result = run(fut).unwrap();
assert_eq!(result, (1, 2, 3));
}