use std::cell::Cell;
use std::future::Future;
use std::panic::{self, AssertUnwindSafe};
use std::sync::Arc;
use tokio::runtime::{Builder, Runtime};
use crate::{Error, Model, Value};
#[derive(Debug)]
pub struct Database {
inner: crate::Database,
runtime: Option<Arc<Runtime>>,
}
impl Database {
pub fn connect(url: &str) -> Result<Self, Error> {
let runtime = Builder::new_current_thread()
.enable_all()
.build()
.map_err(|e| Error::Database(sqlx::Error::Io(e)))?;
match block_on(&runtime, crate::Database::connect(url)).and_then(|connected| connected) {
Ok(inner) => Ok(Self {
inner,
runtime: Some(Arc::new(runtime)),
}),
Err(error) => {
runtime.shutdown_background();
Err(error)
}
}
}
pub fn driver(&self) -> crate::Driver {
self.inner.driver()
}
#[cfg(feature = "mysql")]
pub fn pool(&self) -> &sqlx::MySqlPool {
self.inner.pool()
}
pub fn run<F: Future>(&self, future: F) -> Result<F::Output, Error> {
block_on(self.runtime(), future)
}
pub fn ping(&self) -> Result<(), Error> {
self.run(self.inner.ping())?
}
pub fn transaction<T, E, F>(&self, f: F) -> Result<T, E>
where
E: From<Error>,
F: FnOnce(&mut Transaction) -> Result<T, E>,
{
let inner = self.run(crate::Transaction::begin(&self.inner.pool))??;
let mut tx = Transaction {
inner,
runtime: Arc::clone(self.runtime_arc()),
};
match panic::catch_unwind(AssertUnwindSafe(|| f(&mut tx))) {
Ok(Ok(value)) => {
self.run(tx.inner.commit())??;
Ok(value)
}
Ok(Err(error)) => {
let _ = self.run(tx.inner.rollback());
Err(error)
}
Err(payload) => {
let _ = self.run(tx.inner.rollback());
panic::resume_unwind(payload)
}
}
}
pub fn close(&self) -> Result<(), Error> {
self.run(self.inner.close())
}
fn runtime(&self) -> &Runtime {
self.runtime_arc()
}
fn runtime_arc(&self) -> &Arc<Runtime> {
self.runtime
.as_ref()
.expect("the runtime is only taken in drop")
}
}
impl Drop for Database {
fn drop(&mut self) {
if let Some(runtime) = self.runtime.take().and_then(|rt| Arc::try_unwrap(rt).ok()) {
runtime.shutdown_background();
}
}
}
pub struct Transaction {
inner: crate::Transaction,
runtime: Arc<Runtime>,
}
pub trait Executor<'a> {
#[doc(hidden)]
type Target: crate::Executor<'a>;
#[doc(hidden)]
fn parts(self) -> (&'a Runtime, Self::Target);
}
impl<'a> Executor<'a> for &'a Database {
type Target = &'a crate::Database;
fn parts(self) -> (&'a Runtime, &'a crate::Database) {
(self.runtime(), &self.inner)
}
}
impl<'a> Executor<'a> for &'a mut Transaction {
type Target = &'a mut crate::Transaction;
fn parts(self) -> (&'a Runtime, &'a mut crate::Transaction) {
let Transaction { inner, runtime } = self;
(runtime, inner)
}
}
pub struct Query<M: Model> {
inner: crate::Query<M>,
}
impl<M: Model> crate::Query<M> {
pub fn blocking(self) -> Query<M> {
Query { inner: self }
}
}
impl<M: Model> Query<M> {
pub fn get<'a>(self, db: impl Executor<'a>) -> Result<Vec<M>, Error> {
let (runtime, target) = db.parts();
block_on(runtime, self.inner.get(target))?
}
pub fn first<'a>(self, db: impl Executor<'a>) -> Result<Option<M>, Error> {
let (runtime, target) = db.parts();
block_on(runtime, self.inner.first(target))?
}
pub fn first_or_fail<'a>(self, db: impl Executor<'a>) -> Result<M, Error> {
let (runtime, target) = db.parts();
block_on(runtime, self.inner.first_or_fail(target))?
}
pub fn find<'a>(self, db: impl Executor<'a>, id: impl Into<Value>) -> Result<Option<M>, Error> {
let (runtime, target) = db.parts();
block_on(runtime, self.inner.find(target, id))?
}
pub fn find_or_fail<'a>(self, db: impl Executor<'a>, id: impl Into<Value>) -> Result<M, Error> {
let (runtime, target) = db.parts();
block_on(runtime, self.inner.find_or_fail(target, id))?
}
pub fn count<'a>(self, db: impl Executor<'a>) -> Result<i64, Error> {
let (runtime, target) = db.parts();
block_on(runtime, self.inner.count(target))?
}
pub fn exists<'a>(self, db: impl Executor<'a>) -> Result<bool, Error> {
let (runtime, target) = db.parts();
block_on(runtime, self.inner.exists(target))?
}
pub fn insert<'a>(self, db: impl Executor<'a>, values: &[(&str, Value)]) -> Result<u64, Error> {
let (runtime, target) = db.parts();
block_on(runtime, self.inner.insert(target, values))?
}
pub fn replace<'a>(
self,
db: impl Executor<'a>,
values: &[(&str, Value)],
) -> Result<u64, Error> {
let (runtime, target) = db.parts();
block_on(runtime, self.inner.replace(target, values))?
}
pub fn update<'a>(self, db: impl Executor<'a>, values: &[(&str, Value)]) -> Result<u64, Error> {
let (runtime, target) = db.parts();
block_on(runtime, self.inner.update(target, values))?
}
pub fn delete<'a>(self, db: impl Executor<'a>) -> Result<u64, Error> {
let (runtime, target) = db.parts();
block_on(runtime, self.inner.delete(target))?
}
}
fn block_on<F: Future>(runtime: &Runtime, future: F) -> Result<F::Output, Error> {
let polled = Cell::new(false);
let future = async {
polled.set(true);
future.await
};
match panic::catch_unwind(AssertUnwindSafe(|| runtime.block_on(future))) {
Ok(output) => Ok(output),
Err(_) if !polled.get() => Err(Error::BlockingInRuntime),
Err(payload) => panic::resume_unwind(payload),
}
}