use std::{collections::HashSet, fmt, sync::Arc};
use tracing::{debug, error};
use crate::{
Credentials, DatabaseManager, DriverOptions, Transaction, TransactionOptions, TransactionType, UserManager,
common::{Addresses, Result},
connection::{
runtime::BackgroundRuntime,
server::{
AvailableServer, Server, server_connection::ServerConnection, server_manager::ServerManager,
server_routing::ServerRouting, server_version::ServerVersion,
},
},
};
pub struct TypeDBDriver {
server_manager: Arc<ServerManager>,
database_manager: DatabaseManager,
user_manager: UserManager,
background_runtime: Arc<BackgroundRuntime>,
}
impl TypeDBDriver {
const DRIVER_LANG: &'static str = "rust";
const VERSION: &'static str = match option_env!("CARGO_PKG_VERSION") {
None => "0.0.0",
Some(version) => version,
};
pub const DEFAULT_ADDRESS: &'static str = "127.0.0.1:1729";
#[cfg_attr(
feature = "sync",
doc = "TypeDBDriver::new(Addresses::try_from_address_str(\"127.0.0.1:1729\").unwrap(), Credentials::new(\"username\", \"password\"), DriverOptions::new(true, None))"
)]
#[cfg_attr(
not(feature = "sync"),
doc = "TypeDBDriver::new(Addresses::try_from_address_str(\"127.0.0.1:1729\").unwrap(), Credentials::new(\"username\", \"password\"), DriverOptions::new(true, None)).await"
)]
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub async fn new(addresses: Addresses, credentials: Credentials, driver_options: DriverOptions) -> Result<Self> {
debug!("Creating new TypeDB driver connection to {:?}", addresses);
Self::new_with_description(addresses, credentials, driver_options, Self::DRIVER_LANG).await
}
#[cfg_attr(
feature = "sync",
doc = "TypeDBDriver::new_with_description(Addresses::try_from_address_str(\"127.0.0.1:1729\").unwrap(), Credentials::new(\"username\", \"password\"), DriverOptions::new(true, None), \"rust\")"
)]
#[cfg_attr(
not(feature = "sync"),
doc = "TypeDBDriver::new_with_description(Addresses::try_from_address_str(\"127.0.0.1:1729\").unwrap(), Credentials::new(\"username\", \"password\"), DriverOptions::new(true, None), \"rust\").await"
)]
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub async fn new_with_description(
addresses: Addresses,
credentials: Credentials,
driver_options: DriverOptions,
driver_lang: impl AsRef<str>,
) -> Result<Self> {
debug!("Initializing TypeDB driver with description: {}", driver_lang.as_ref());
let background_runtime = Arc::new(BackgroundRuntime::new()?);
debug!("Establishing server connection to {:?}", addresses);
let server_manager = Arc::new(
ServerManager::new(
background_runtime.clone(),
addresses,
credentials,
driver_options,
driver_lang.as_ref(),
Self::VERSION,
)
.await?,
);
debug!("Successfully connected to servers");
let database_manager = DatabaseManager::new(server_manager.clone())?;
let user_manager = UserManager::new(server_manager.clone());
debug!("Created database manager and user manager");
debug!("TypeDB driver initialization completed successfully");
Ok(Self { server_manager, database_manager, user_manager, background_runtime })
}
pub fn is_open(&self) -> bool {
self.background_runtime.is_open()
}
pub fn databases(&self) -> &DatabaseManager {
&self.database_manager
}
pub fn users(&self) -> &UserManager {
&self.user_manager
}
#[cfg_attr(feature = "sync", doc = "driver.server_version()")]
#[cfg_attr(not(feature = "sync"), doc = "driver.server_version().await")]
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub async fn server_version(&self) -> Result<ServerVersion> {
self.server_version_with_routing(ServerRouting::Auto).await
}
#[cfg_attr(feature = "sync", doc = "driver.server_version_with_routing(ServerRouting::Auto);")]
#[cfg_attr(not(feature = "sync"), doc = "driver.server_version_with_routing(ServerRouting::Auto).await;")]
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub async fn server_version_with_routing(&self, server_routing: ServerRouting) -> Result<ServerVersion> {
self.server_manager
.execute(server_routing, |server_connection| async move { server_connection.version().await })
.await
}
#[cfg_attr(feature = "sync", doc = "driver.servers();")]
#[cfg_attr(not(feature = "sync"), doc = "driver.servers().await;")]
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub async fn servers(&self) -> Result<HashSet<Server>> {
self.servers_with_routing(ServerRouting::Auto).await
}
#[cfg_attr(feature = "sync", doc = "driver.servers_with_routing(ServerRouting::Auto);")]
#[cfg_attr(not(feature = "sync"), doc = "driver.servers_with_routing(ServerRouting::Auto).await;")]
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub async fn servers_with_routing(&self, server_routing: ServerRouting) -> Result<HashSet<Server>> {
self.server_manager.fetch_servers(server_routing).await
}
#[cfg_attr(feature = "sync", doc = "driver.primary_server();")]
#[cfg_attr(not(feature = "sync"), doc = "driver.primary_server().await;")]
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub async fn primary_server(&self) -> Result<Option<AvailableServer>> {
self.primary_server_with_routing(ServerRouting::Auto).await
}
#[cfg_attr(feature = "sync", doc = "driver.primary_server_with_routing(ServerRouting::Auto);")]
#[cfg_attr(not(feature = "sync"), doc = "driver.primary_server_with_routing(ServerRouting::Auto).await;")]
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub async fn primary_server_with_routing(&self, server_routing: ServerRouting) -> Result<Option<AvailableServer>> {
self.server_manager.fetch_primary_server(server_routing).await
}
pub fn options(&self) -> &DriverOptions {
self.server_manager.driver_options()
}
pub fn configured_addresses(&self) -> &Addresses {
self.server_manager.configured_addresses()
}
#[cfg_attr(feature = "sync", doc = "driver.transaction(database_name, TransactionType::Read);")]
#[cfg_attr(not(feature = "sync"), doc = "driver.transaction(database_name, TransactionType::Read).await;")]
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub async fn transaction(
&self,
database_name: impl AsRef<str>,
transaction_type: TransactionType,
) -> Result<Transaction> {
self.transaction_with_options(database_name, transaction_type, TransactionOptions::new()).await
}
#[cfg_attr(
feature = "sync",
doc = "transaction.transaction_with_options(database_name, transaction_type, options)"
)]
#[cfg_attr(
not(feature = "sync"),
doc = "transaction.transaction_with_options(database_name, transaction_type, options).await"
)]
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub async fn transaction_with_options(
&self,
database_name: impl AsRef<str>,
transaction_type: TransactionType,
options: TransactionOptions,
) -> Result<Transaction> {
let database_name = database_name.as_ref();
let open_fn = |server_connection: ServerConnection| {
let options = options.clone();
async move { server_connection.open_transaction(database_name, transaction_type, options).await }
};
debug!("Opening transaction for database: {} with type: {:?}", database_name, transaction_type);
let transaction_stream = self.server_manager.execute(ServerRouting::Auto, open_fn).await?;
debug!("Successfully opened transaction for database: {}", database_name);
Ok(Transaction::new(transaction_stream))
}
pub fn force_close(&self) -> Result {
if !self.is_open() {
return Ok(());
}
debug!("Closing TypeDB driver connection");
let close_result = self.server_manager.force_close().and(self.background_runtime.force_close());
match &close_result {
Ok(_) => debug!("Successfully closed TypeDB driver connection"),
Err(e) => error!("Failed to close TypeDB driver connection: {}", e),
}
close_result
}
}
impl fmt::Debug for TypeDBDriver {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TypeDBDriver").field("server_manager", &self.server_manager).finish()
}
}