use core::future::Future;
use std::{collections::HashMap, sync::Mutex};
use xitca_unsafe_collection::no_hash::NoHashBuilder;
use super::{
copy::{CopyIn, CopyOut},
driver::{
DriverTx,
codec::{Response, encode::Encode, response::IntoResponse},
},
error::Error,
session::Session,
statement::Statement,
transaction::{Transaction, TransactionBuilder},
types::{Oid, Type},
};
pub trait ClientBorrowMut: ClientBorrow {
fn borrow_cli_mut(&mut self) -> &mut Client;
}
impl<T> ClientBorrowMut for &mut T
where
T: ClientBorrowMut,
{
#[inline]
fn borrow_cli_mut(&mut self) -> &mut Client {
T::borrow_cli_mut(*self)
}
}
pub trait ClientBorrow {
fn borrow_cli_ref(&self) -> &Client;
}
impl<T> ClientBorrow for &T
where
T: ClientBorrow,
{
#[inline]
fn borrow_cli_ref(&self) -> &Client {
T::borrow_cli_ref(*self)
}
}
impl<T> ClientBorrow for &mut T
where
T: ClientBorrow,
{
#[inline]
fn borrow_cli_ref(&self) -> &Client {
T::borrow_cli_ref(&**self)
}
}
pub struct Client {
pub(crate) tx: DriverTx,
pub(crate) cache: Box<ClientCache>,
}
pub(crate) struct ClientCache {
session: Session,
type_info: Mutex<CachedTypeInfo>,
}
struct CachedTypeInfo {
typeinfo: Option<Statement>,
typeinfo_composite: Option<Statement>,
typeinfo_enum: Option<Statement>,
types: HashMap<Oid, Type, NoHashBuilder>,
}
impl Client {
#[inline]
pub fn transaction(&mut self) -> impl Future<Output = Result<Transaction<'_, Self>, Error>> + Send {
TransactionBuilder::new().begin(self)
}
#[inline]
pub fn transaction_owned(self) -> impl Future<Output = Result<Transaction<'static, Self>, Error>> + Send {
TransactionBuilder::new().begin_owned(self)
}
#[inline]
pub fn copy_in(&mut self, stmt: &Statement) -> impl Future<Output = Result<CopyIn<'_, Self>, Error>> + Send {
CopyIn::new(self, stmt)
}
#[inline]
pub async fn copy_out(&self, stmt: &Statement) -> Result<CopyOut, Error> {
CopyOut::new(self, stmt).await
}
pub fn cancel_token(&self) -> Session {
Session::clone(&self.cache.session)
}
pub fn closed(&self) -> bool {
self.tx.is_closed()
}
pub fn typeinfo(&self) -> Option<Statement> {
self.cache
.type_info
.lock()
.unwrap()
.typeinfo
.as_ref()
.map(Statement::duplicate)
}
pub fn set_typeinfo(&self, statement: &Statement) {
self.cache.type_info.lock().unwrap().typeinfo = Some(statement.duplicate());
}
pub fn typeinfo_composite(&self) -> Option<Statement> {
self.cache
.type_info
.lock()
.unwrap()
.typeinfo_composite
.as_ref()
.map(Statement::duplicate)
}
pub fn set_typeinfo_composite(&self, statement: &Statement) {
self.cache.type_info.lock().unwrap().typeinfo_composite = Some(statement.duplicate());
}
pub fn typeinfo_enum(&self) -> Option<Statement> {
self.cache
.type_info
.lock()
.unwrap()
.typeinfo_enum
.as_ref()
.map(Statement::duplicate)
}
pub fn set_typeinfo_enum(&self, statement: &Statement) {
self.cache.type_info.lock().unwrap().typeinfo_enum = Some(statement.duplicate());
}
pub fn type_(&self, oid: Oid) -> Option<Type> {
self.cache.type_info.lock().unwrap().types.get(&oid).cloned()
}
pub fn set_type(&self, oid: Oid, type_: &Type) {
self.cache.type_info.lock().unwrap().types.insert(oid, type_.clone());
}
pub fn clear_type_cache(&self) {
self.cache.type_info.lock().unwrap().types.clear();
}
#[inline]
pub(crate) fn query<S>(&self, stmt: S) -> Result<<S::Output as IntoResponse>::Response, Error>
where
S: Encode,
{
self.query_raw(stmt).map(|(opt, res)| opt.into_response(res))
}
#[inline]
pub(crate) fn query_raw<S>(&self, stmt: S) -> Result<(S::Output, Response), Error>
where
S: Encode,
{
self.tx.send(|buf| stmt.encode(buf))
}
pub(crate) fn new(tx: DriverTx, session: Session) -> Self {
Self {
tx,
cache: Box::new(ClientCache {
session,
type_info: Mutex::new(CachedTypeInfo {
typeinfo: None,
typeinfo_composite: None,
typeinfo_enum: None,
types: HashMap::default(),
}),
}),
}
}
}
impl ClientBorrowMut for Client {
#[inline]
fn borrow_cli_mut(&mut self) -> &mut Client {
self
}
}
impl ClientBorrow for Client {
#[inline]
fn borrow_cli_ref(&self) -> &Client {
self
}
}
impl Drop for Client {
fn drop(&mut self) {
let (type_info, typeinfo_composite, typeinfo_enum) = {
let cache = self.cache.type_info.get_mut().unwrap();
(
cache.typeinfo.take(),
cache.typeinfo_composite.take(),
cache.typeinfo_enum.take(),
)
};
if let Some(stmt) = type_info {
drop(stmt.into_guarded(&*self));
}
if let Some(stmt) = typeinfo_composite {
drop(stmt.into_guarded(&*self));
}
if let Some(stmt) = typeinfo_enum {
drop(stmt.into_guarded(&*self));
}
}
}