1use std::{
21 collections::{HashMap, HashSet},
22 fmt,
23 sync::Arc,
24};
25
26use itertools::Itertools;
27use tracing::{debug, error};
28
29use crate::{
30 Credentials, DatabaseManager, DriverOptions, Transaction, TransactionOptions, TransactionType, UserManager,
31 common::{
32 Result,
33 address::Address,
34 error::{ConnectionError, Error},
35 },
36 connection::{runtime::BackgroundRuntime, server_connection::ServerConnection},
37};
38
39pub struct TypeDBDriver {
41 server_connections: HashMap<Address, ServerConnection>,
42 database_manager: DatabaseManager,
43 user_manager: UserManager,
44 background_runtime: Arc<BackgroundRuntime>,
45}
46
47impl TypeDBDriver {
48 const DRIVER_LANG: &'static str = "rust";
49 const VERSION: &'static str = match option_env!("CARGO_PKG_VERSION") {
50 None => "0.0.0",
51 Some(version) => version,
52 };
53
54 pub const DEFAULT_ADDRESS: &'static str = "localhost:1729";
55
56 #[cfg_attr(
68 feature = "sync",
69 doc = "TypeDBDriver::new(\"127.0.0.1:1729\", Credentials::new(\"username\", \"password\"), DriverOptions::new(true, None))"
70 )]
71 #[cfg_attr(
72 not(feature = "sync"),
73 doc = "TypeDBDriver::new(\"127.0.0.1:1729\", Credentials::new(\"username\", \"password\"), DriverOptions::new(true, None)).await"
74 )]
75 #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
77 pub async fn new(
78 address: impl AsRef<str>,
79 credentials: Credentials,
80 driver_options: DriverOptions,
81 ) -> Result<Self> {
82 debug!("Creating new TypeDB driver connection to {}", address.as_ref());
83 Self::new_with_description(address, credentials, driver_options, Self::DRIVER_LANG).await
84 }
85
86 #[cfg_attr(
101 feature = "sync",
102 doc = "TypeDBDriver::new_with_description(\"127.0.0.1:1729\", Credentials::new(\"username\", \"password\"), DriverOptions::new(true, None), \"rust\")"
103 )]
104 #[cfg_attr(
105 not(feature = "sync"),
106 doc = "TypeDBDriver::new_with_description(\"127.0.0.1:1729\", Credentials::new(\"username\", \"password\"), DriverOptions::new(true, None), \"rust\").await"
107 )]
108 #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
110 pub async fn new_with_description(
111 address: impl AsRef<str>,
112 credentials: Credentials,
113 driver_options: DriverOptions,
114 driver_lang: impl AsRef<str>,
115 ) -> Result<Self> {
116 debug!("Initializing TypeDB driver with description: {}", driver_lang.as_ref());
117 let id = address.as_ref().to_string();
118 let address: Address = id.parse()?;
119
120 let background_runtime = Arc::new(BackgroundRuntime::new()?);
121
122 debug!("Establishing server connection to {}", address);
123 let (server_connection, database_info) = ServerConnection::new(
124 background_runtime.clone(),
125 address.clone(),
126 credentials,
127 driver_options,
128 driver_lang.as_ref(),
129 Self::VERSION,
130 )
131 .await?;
132 debug!("Successfully connected to server at {}", address);
133
134 let server_connections: HashMap<Address, ServerConnection> = [(address, server_connection)].into();
143 let database_manager = DatabaseManager::new(server_connections.clone(), database_info)?;
144 let user_manager = UserManager::new(server_connections.clone());
145 debug!("Created database manager and user manager");
146
147 debug!("TypeDB driver initialization completed successfully");
148 Ok(Self { server_connections, database_manager, user_manager, background_runtime })
149 }
150
151 #[expect(unused, reason = "automatic discovery is not yet implemented")]
152 #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
153 async fn fetch_server_list(
154 background_runtime: Arc<BackgroundRuntime>,
155 addresses: impl IntoIterator<Item = impl AsRef<str>> + Clone,
156 credentials: Credentials,
157 driver_options: DriverOptions,
158 ) -> Result<HashSet<Address>> {
159 let addresses: Vec<Address> = addresses.into_iter().map(|addr| addr.as_ref().parse()).try_collect()?;
160 for address in &addresses {
161 let server_connection = ServerConnection::new(
162 background_runtime.clone(),
163 address.clone(),
164 credentials.clone(),
165 driver_options.clone(),
166 Self::DRIVER_LANG,
167 Self::VERSION,
168 )
169 .await;
170 match server_connection {
171 Ok((server_connection, _)) => match server_connection.servers_all() {
172 Ok(servers) => return Ok(servers.into_iter().collect()),
173 Err(Error::Connection(
174 ConnectionError::ServerConnectionFailedStatusError { .. } | ConnectionError::ConnectionFailed,
175 )) => (),
176 Err(err) => Err(err)?,
177 },
178 Err(Error::Connection(
179 ConnectionError::ServerConnectionFailedStatusError { .. } | ConnectionError::ConnectionFailed,
180 )) => (),
181 Err(err) => Err(err)?,
182 }
183 }
184 Err(ConnectionError::ServerConnectionFailed { addresses }.into())
185 }
186
187 pub fn is_open(&self) -> bool {
195 self.background_runtime.is_open()
196 }
197
198 pub fn databases(&self) -> &DatabaseManager {
199 &self.database_manager
200 }
201
202 pub fn users(&self) -> &UserManager {
203 &self.user_manager
204 }
205
206 #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
209 pub async fn transaction(
210 &self,
211 database_name: impl AsRef<str>,
212 transaction_type: TransactionType,
213 ) -> Result<Transaction> {
214 self.transaction_with_options(database_name, transaction_type, TransactionOptions::new()).await
215 }
216
217 #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
231 pub async fn transaction_with_options(
232 &self,
233 database_name: impl AsRef<str>,
234 transaction_type: TransactionType,
235 options: TransactionOptions,
236 ) -> Result<Transaction> {
237 let database_name = database_name.as_ref();
238 debug!("Opening transaction for database: {} with type: {:?}", database_name, transaction_type);
239
240 let database = self.database_manager.get_cached_or_fetch(database_name).await?;
241 let transaction_stream = database
242 .run_failsafe(|database| async move {
243 database.connection().open_transaction(database.name(), transaction_type, options).await
244 })
245 .await?;
246
247 debug!("Successfully opened transaction for database: {}", database_name);
248 Ok(Transaction::new(transaction_stream))
249 }
250
251 pub fn force_close(&self) -> Result {
259 if !self.is_open() {
260 return Ok(());
261 }
262
263 debug!("Closing TypeDB driver connection");
264 let result = self.server_connections.values().map(ServerConnection::force_close).try_collect();
265 let close_result = self.background_runtime.force_close().and(result);
266
267 match &close_result {
268 Ok(_) => debug!("Successfully closed TypeDB driver connection"),
269 Err(e) => error!("Failed to close TypeDB driver connection: {}", e),
270 }
271
272 close_result
273 }
274
275 #[expect(unused, reason = "automatic discovery is not yet implemented")]
276 pub(crate) fn server_count(&self) -> usize {
277 self.server_connections.len()
278 }
279
280 #[expect(unused, reason = "automatic discovery is not yet implemented")]
281 pub(crate) fn servers(&self) -> impl Iterator<Item = &Address> {
282 self.server_connections.keys()
283 }
284
285 #[expect(unused, reason = "automatic discovery is not yet implemented")]
286 pub(crate) fn connection(&self, id: &Address) -> Option<&ServerConnection> {
287 self.server_connections.get(id)
288 }
289
290 #[expect(unused, reason = "automatic discovery is not yet implemented")]
291 pub(crate) fn connections(&self) -> impl Iterator<Item = (&Address, &ServerConnection)> + '_ {
292 self.server_connections.iter()
293 }
294}
295
296impl fmt::Debug for TypeDBDriver {
297 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298 f.debug_struct("Connection").field("server_connections", &self.server_connections).finish()
299 }
300}