use std::sync::Arc;
use super::backend::{DriverBackend, QueryResult, TxType};
use super::context::TransactionContext;
use super::transaction::Transaction;
use crate::error::Result;
pub struct Database {
backend: Box<dyn DriverBackend>,
database_name: String,
}
impl Database {
pub fn with_backend(backend: Box<dyn DriverBackend>, database_name: impl Into<String>) -> Self {
Self {
backend,
database_name: database_name.into(),
}
}
#[cfg(feature = "typedb")]
pub async fn connect(
address: &str,
database: &str,
username: &str,
password: &str,
) -> Result<Self> {
Self::connect_with_options(
address,
database,
username,
password,
super::real_driver::ConnectOptions::default(),
)
.await
}
#[cfg(feature = "typedb")]
pub async fn connect_with_options(
address: &str,
database: &str,
username: &str,
password: &str,
options: super::real_driver::ConnectOptions,
) -> Result<Self> {
let backend =
super::real_driver::RealBackend::connect(address, username, password, options).await?;
Ok(Self {
backend: Box::new(backend),
database_name: database.to_string(),
})
}
pub async fn read_transaction(&self) -> Result<Transaction> {
let tx = self
.backend
.open_transaction(&self.database_name, TxType::Read)
.await?;
Ok(Transaction::new(tx, TxType::Read))
}
pub async fn write_transaction(&self) -> Result<Transaction> {
let tx = self
.backend
.open_transaction(&self.database_name, TxType::Write)
.await?;
Ok(Transaction::new(tx, TxType::Write))
}
pub async fn transaction_context(&self, tx_type: TxType) -> Result<TransactionContext> {
let tx = self
.backend
.open_transaction(&self.database_name, tx_type)
.await?;
Ok(TransactionContext::new(tx, tx_type))
}
pub fn database_name(&self) -> &str {
&self.database_name
}
pub fn is_connected(&self) -> bool {
self.backend.is_open()
}
pub async fn database_exists(&self) -> Result<bool> {
self.backend.database_exists(&self.database_name).await
}
pub async fn create_database(&self) -> Result<()> {
if !self.database_exists().await? {
self.backend.create_database(&self.database_name).await?;
}
Ok(())
}
pub async fn delete_database(&self) -> Result<()> {
if self.database_exists().await? {
self.backend.delete_database(&self.database_name).await?;
}
Ok(())
}
pub async fn schema_text(&self) -> Result<String> {
self.backend.schema_text(&self.database_name).await
}
pub fn into_shared(self) -> Arc<Self> {
Arc::new(self)
}
#[tracing::instrument(skip(self, typeql), fields(db = %self.database_name))]
pub async fn execute_raw(&self, typeql: &str, tx_type: TxType) -> Result<QueryResult> {
let mut tx = self
.backend
.open_transaction(&self.database_name, tx_type)
.await?;
let result = tx.query(typeql).await?;
if matches!(tx_type, TxType::Write | TxType::Schema) {
tx.commit().await?;
}
Ok(result)
}
}