use super::*;
use crate::connection::{conn_string, TransactionConfiguration};
use std::marker::PhantomData;
#[doc(hidden)]
pub use rsfbclient_native::{DynLink, DynLoad};
use crate::transaction::{transaction_builder, TransactionConfigurationBuilder};
use rsfbclient_native::{LinkageMarker, NativeFbAttachmentConfig, NativeFbClient, RemoteConfig};
#[doc(hidden)]
#[derive(Clone)]
pub struct LinkageNotConfigured;
#[doc(hidden)]
#[derive(Clone)]
pub struct ConnTypeNotConfigured;
#[doc(hidden)]
#[derive(Clone)]
pub struct ConnEmbedded;
#[doc(hidden)]
#[derive(Clone)]
pub struct ConnRemote;
#[doc(hidden)]
#[derive(Clone)]
pub struct ConnByString;
#[doc(hidden)]
#[cfg(feature = "linking")]
pub type DynByString = DynLink;
#[cfg(not(feature = "linking"))]
pub type DynByString = DynLoad;
#[doc(hidden)]
pub trait ConfiguredConnType: Send + Sync {}
#[doc(hidden)]
impl ConfiguredConnType for ConnEmbedded {}
#[doc(hidden)]
impl ConfiguredConnType for ConnRemote {}
#[doc(hidden)]
impl ConfiguredConnType for ConnByString {}
#[doc(hidden)]
pub trait ConfiguredLinkage {}
#[doc(hidden)]
impl ConfiguredLinkage for DynLink {}
#[doc(hidden)]
impl ConfiguredLinkage for DynLoad {}
#[derive(Clone)]
pub struct NativeConnectionBuilder<LinkageType, ConnectionType> {
_marker_linkage: PhantomData<LinkageType>,
_marker_conntype: PhantomData<ConnectionType>,
conn_conf: ConnectionConfiguration<NativeFbAttachmentConfig>,
charset: Charset,
lib_path: Option<String>,
page_size: Option<u32>,
}
impl<A, B> From<&NativeConnectionBuilder<A, B>>
for ConnectionConfiguration<NativeFbAttachmentConfig>
{
fn from(arg: &NativeConnectionBuilder<A, B>) -> Self {
arg.conn_conf.clone()
}
}
pub fn builder_native() -> NativeConnectionBuilder<LinkageNotConfigured, ConnTypeNotConfigured> {
Default::default()
}
#[cfg(feature = "dynamic_loading")]
impl<A> FirebirdClientFactory for NativeConnectionBuilder<DynLoad, A>
where
A: ConfiguredConnType,
{
type C = NativeFbClient<rsfbclient_native::DynLoad>;
fn new_instance(&self) -> Result<Self::C, FbError> {
let path = self
.lib_path
.as_ref()
.ok_or_else(|| FbError::from("The lib path is required to use the dynload loading"))?;
rsfbclient_native::DynLoad {
charset: self.charset.clone(),
lib_path: path.clone(),
}
.try_to_client()
}
fn get_conn_conf(&self) -> &ConnectionConfiguration<NativeFbAttachmentConfig> {
&self.conn_conf
}
}
#[cfg(feature = "linking")]
impl<A> FirebirdClientFactory for NativeConnectionBuilder<DynLink, A>
where
A: ConfiguredConnType,
{
type C = NativeFbClient<rsfbclient_native::DynLink>;
fn new_instance(&self) -> Result<Self::C, FbError> {
Ok(rsfbclient_native::DynLink(self.charset.clone()).to_client())
}
fn get_conn_conf(&self) -> &ConnectionConfiguration<NativeFbAttachmentConfig> {
&self.conn_conf
}
}
impl<A, B> NativeConnectionBuilder<A, B>
where
A: ConfiguredLinkage,
B: ConfiguredConnType,
A: LinkageMarker,
Self: FirebirdClientFactory<C = NativeFbClient<A>>,
{
pub fn connect(&self) -> Result<Connection<NativeFbClient<A>>, FbError> {
Connection::open(self.new_instance()?, &self.conn_conf)
}
pub fn create_database(&self) -> Result<Connection<NativeFbClient<A>>, FbError> {
Connection::create_database(self.new_instance()?, &self.conn_conf, self.page_size)
}
}
impl<A, B> NativeConnectionBuilder<A, B>
where
A: ConfiguredLinkage,
B: ConfiguredConnType,
{
pub fn user<S: Into<String>>(&mut self, user: S) -> &mut Self {
self.conn_conf.attachment_conf.user = user.into();
self
}
pub fn db_name<S: Into<String>>(&mut self, db_name: S) -> &mut Self {
self.conn_conf.attachment_conf.db_name = db_name.into();
self
}
pub fn dialect(&mut self, dialect: Dialect) -> &mut Self {
self.conn_conf.dialect = dialect;
self
}
pub fn charset(&mut self, charset: Charset) -> &mut Self {
self.charset = charset;
self
}
pub fn stmt_cache_size(&mut self, stmt_cache_size: usize) -> &mut Self {
self.conn_conf.stmt_cache_size = stmt_cache_size;
self
}
pub fn page_size(&mut self, size: u32) -> &mut Self {
self.page_size = Some(size);
self
}
pub fn role<S: Into<String>>(&mut self, name: S) -> &mut Self {
self.conn_conf.attachment_conf.role_name = Some(name.into());
self
}
pub fn transaction(&mut self, conf: TransactionConfiguration) -> &mut Self {
self.conn_conf.transaction_conf = conf;
self
}
pub fn with_transaction<F>(&mut self, builder: F) -> &mut Self
where
F: FnOnce(&mut TransactionConfigurationBuilder) -> &mut TransactionConfigurationBuilder,
{
self.conn_conf.transaction_conf = builder(&mut transaction_builder()).build();
self
}
}
impl<A, B> NativeConnectionBuilder<A, B> {
fn safe_transmute<X, Y>(self) -> NativeConnectionBuilder<X, Y> {
NativeConnectionBuilder {
_marker_linkage: PhantomData,
_marker_conntype: PhantomData,
conn_conf: self.conn_conf,
charset: self.charset,
lib_path: self.lib_path,
page_size: self.page_size,
}
}
}
impl Default for NativeConnectionBuilder<LinkageNotConfigured, ConnTypeNotConfigured> {
fn default() -> Self {
let mut self_result = Self {
_marker_linkage: PhantomData,
_marker_conntype: PhantomData,
conn_conf: Default::default(),
charset: charset::UTF_8,
lib_path: None,
page_size: None,
};
self_result.conn_conf.dialect = Dialect::D3;
self_result.conn_conf.stmt_cache_size = 20;
self_result.conn_conf.attachment_conf.remote = None;
self_result.conn_conf.attachment_conf.user = "SYSDBA".to_string();
self_result.conn_conf.attachment_conf.db_name = "test.fdb".to_string();
self_result.conn_conf.attachment_conf.role_name = None;
self_result
}
}
impl<A> NativeConnectionBuilder<A, ConnRemote> {
fn get_initialized_remote(&mut self) -> &mut RemoteConfig {
self.conn_conf
.attachment_conf
.remote
.get_or_insert(Default::default())
}
pub fn host<S: Into<String>>(&mut self, host: S) -> &mut Self {
self.get_initialized_remote().host = host.into();
self
}
pub fn port(&mut self, port: u16) -> &mut Self {
self.get_initialized_remote().port = port;
self
}
pub fn pass<S: Into<String>>(&mut self, pass: S) -> &mut Self {
self.get_initialized_remote().pass = pass.into();
self
}
}
impl<A> NativeConnectionBuilder<A, ConnTypeNotConfigured> {
pub fn with_remote(mut self) -> NativeConnectionBuilder<A, ConnRemote> {
let remote = rsfbclient_native::RemoteConfig {
host: "localhost".to_string(),
port: 3050,
pass: "masterkey".to_string(),
};
self.conn_conf.attachment_conf.remote = Some(remote);
self.safe_transmute()
}
pub fn with_embedded(self) -> NativeConnectionBuilder<A, ConnEmbedded> {
self.safe_transmute()
}
}
impl<A> NativeConnectionBuilder<LinkageNotConfigured, A> {
#[cfg(feature = "linking")]
pub fn with_dyn_link(self) -> NativeConnectionBuilder<DynLink, A> {
self.safe_transmute()
}
#[cfg(feature = "dynamic_loading")]
pub fn with_dyn_load<S: Into<String>>(
mut self,
lib_path: S,
) -> NativeConnectionBuilder<DynLoad, A> {
self.lib_path = Some(lib_path.into());
self.safe_transmute()
}
#[allow(clippy::wrong_self_convention)]
pub fn from_string(
self,
s_conn: &str,
) -> Result<NativeConnectionBuilder<DynByString, ConnByString>, FbError> {
let settings = conn_string::parse(s_conn)?;
let mut cb: NativeConnectionBuilder<DynByString, ConnRemote> = {
if let Some(host) = settings.host {
let mut cb = self.safe_transmute().with_remote();
cb.host(host);
if let Some(port) = settings.port {
cb.port(port);
}
if let Some(user) = settings.user {
cb.user(user);
}
if let Some(pass) = settings.pass {
cb.pass(pass);
}
cb
} else {
self.safe_transmute()
}
};
cb.db_name(settings.db_name);
if let Some(charset) = settings.charset {
cb.charset(charset);
}
if let Some(dialect) = settings.dialect {
cb.dialect(dialect);
}
if let Some(lib_path) = settings.lib_path {
cb.lib_path = Some(lib_path);
}
if let Some(stmt_cache_size) = settings.stmt_cache_size {
cb.stmt_cache_size(stmt_cache_size);
}
if let Some(role_name) = settings.role_name {
cb.role(role_name);
}
Ok(cb.safe_transmute())
}
}