Skip to main content

typedb_driver/
driver.rs

1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19use std::{collections::HashSet, fmt, sync::Arc};
20
21use tracing::{debug, error};
22
23use crate::{
24    Credentials, DatabaseManager, DriverOptions, Transaction, TransactionOptions, TransactionType, UserManager,
25    common::{Addresses, Result},
26    connection::{
27        runtime::BackgroundRuntime,
28        server::{
29            AvailableServer, Server, server_connection::ServerConnection, server_manager::ServerManager,
30            server_routing::ServerRouting, server_version::ServerVersion,
31        },
32    },
33};
34
35/// A connection to a TypeDB server which serves as the starting point for all interaction.
36pub struct TypeDBDriver {
37    server_manager: Arc<ServerManager>,
38    database_manager: DatabaseManager,
39    user_manager: UserManager,
40    background_runtime: Arc<BackgroundRuntime>,
41}
42
43impl TypeDBDriver {
44    const DRIVER_LANG: &'static str = "rust";
45    const VERSION: &'static str = match option_env!("CARGO_PKG_VERSION") {
46        None => "0.0.0",
47        Some(version) => version,
48    };
49
50    pub const DEFAULT_ADDRESS: &'static str = "127.0.0.1:1729";
51
52    /// Creates a new TypeDB Server connection.
53    ///
54    /// # Arguments
55    ///
56    /// * `addresses` — The address(es) of the TypeDB Server(s), provided in a unified format
57    /// * `credentials` — The Credentials to connect with
58    /// * `driver_options` — The DriverOptions to connect with
59    ///
60    /// # Examples
61    ///
62    /// ```rust
63    #[cfg_attr(
64        feature = "sync",
65        doc = "TypeDBDriver::new(Addresses::try_from_address_str(\"127.0.0.1:1729\").unwrap(), Credentials::new(\"username\", \"password\"), DriverOptions::new(true, None))"
66    )]
67    #[cfg_attr(
68        not(feature = "sync"),
69        doc = "TypeDBDriver::new(Addresses::try_from_address_str(\"127.0.0.1:1729\").unwrap(), Credentials::new(\"username\", \"password\"), DriverOptions::new(true, None)).await"
70    )]
71    /// ```
72    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
73    pub async fn new(addresses: Addresses, credentials: Credentials, driver_options: DriverOptions) -> Result<Self> {
74        debug!("Creating new TypeDB driver connection to {:?}", addresses);
75        Self::new_with_description(addresses, credentials, driver_options, Self::DRIVER_LANG).await
76    }
77
78    /// Creates a new TypeDB Server connection with a description.
79    /// This method is generally used by TypeDB drivers built on top of the Rust driver.
80    /// In other cases, use [`Self::new`] instead.
81    ///
82    /// # Arguments
83    ///
84    /// * `addresses` — The address(es) of the TypeDB Server(s), provided in a unified format
85    /// * `credentials` — The Credentials to connect with
86    /// * `driver_options` — The DriverOptions to connect with
87    /// * `driver_lang` — The language of the driver connecting to the server
88    ///
89    /// # Examples
90    ///
91    /// ```rust
92    #[cfg_attr(
93        feature = "sync",
94        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\")"
95    )]
96    #[cfg_attr(
97        not(feature = "sync"),
98        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"
99    )]
100    /// ```
101    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
102    pub async fn new_with_description(
103        addresses: Addresses,
104        credentials: Credentials,
105        driver_options: DriverOptions,
106        driver_lang: impl AsRef<str>,
107    ) -> Result<Self> {
108        debug!("Initializing TypeDB driver with description: {}", driver_lang.as_ref());
109        let background_runtime = Arc::new(BackgroundRuntime::new()?);
110
111        debug!("Establishing server connection to {:?}", addresses);
112        let server_manager = Arc::new(
113            ServerManager::new(
114                background_runtime.clone(),
115                addresses,
116                credentials,
117                driver_options,
118                driver_lang.as_ref(),
119                Self::VERSION,
120            )
121            .await?,
122        );
123        debug!("Successfully connected to servers");
124
125        let database_manager = DatabaseManager::new(server_manager.clone())?;
126        let user_manager = UserManager::new(server_manager.clone());
127        debug!("Created database manager and user manager");
128
129        debug!("TypeDB driver initialization completed successfully");
130        Ok(Self { server_manager, database_manager, user_manager, background_runtime })
131    }
132
133    /// Checks it this connection is opened.
134    ///
135    /// # Examples
136    ///
137    /// ```rust
138    /// driver.is_open()
139    /// ```
140    pub fn is_open(&self) -> bool {
141        self.background_runtime.is_open()
142    }
143
144    /// The ``DatabaseManager`` for this connection, providing access to database management methods.
145    ///
146    /// # Examples
147    ///
148    /// ```rust
149    /// driver.databases()
150    /// ```
151    pub fn databases(&self) -> &DatabaseManager {
152        &self.database_manager
153    }
154
155    /// The ``UserManager`` for this connection, providing access to user management methods.
156    ///
157    /// # Examples
158    ///
159    /// ```rust
160    /// driver.databases()
161    /// ```
162    pub fn users(&self) -> &UserManager {
163        &self.user_manager
164    }
165
166    /// Retrieves the server's version, using default automatic server routing.
167    ///
168    /// See [`Self::server_version_with_routing`] for more details and options.
169    ///
170    /// # Examples
171    ///
172    /// ```rust
173    #[cfg_attr(feature = "sync", doc = "driver.server_version()")]
174    #[cfg_attr(not(feature = "sync"), doc = "driver.server_version().await")]
175    /// ```
176    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
177    pub async fn server_version(&self) -> Result<ServerVersion> {
178        self.server_version_with_routing(ServerRouting::Auto).await
179    }
180
181    /// Retrieves the server's version.
182    ///
183    /// # Arguments
184    ///
185    /// * `server_routing` — The server routing directive to use for the operation
186    ///
187    /// # Examples
188    ///
189    /// ```rust
190    #[cfg_attr(feature = "sync", doc = "driver.server_version_with_routing(ServerRouting::Auto);")]
191    #[cfg_attr(not(feature = "sync"), doc = "driver.server_version_with_routing(ServerRouting::Auto).await;")]
192    /// ```
193    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
194    pub async fn server_version_with_routing(&self, server_routing: ServerRouting) -> Result<ServerVersion> {
195        self.server_manager
196            .execute(server_routing, |server_connection| async move { server_connection.version().await })
197            .await
198    }
199
200    /// Retrieves the servers, using default automatic server routing.
201    ///
202    /// See [`Self::servers_with_routing`] for more details and options.
203    ///
204    /// # Examples
205    ///
206    /// ```rust
207    #[cfg_attr(feature = "sync", doc = "driver.servers();")]
208    #[cfg_attr(not(feature = "sync"), doc = "driver.servers().await;")]
209    /// ```
210    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
211    pub async fn servers(&self) -> Result<HashSet<Server>> {
212        self.servers_with_routing(ServerRouting::Auto).await
213    }
214
215    /// Retrieves the servers.
216    ///
217    /// # Arguments
218    ///
219    /// * `server_routing` — The server routing directive to use for the operation
220    ///
221    /// # Examples
222    ///
223    /// ```rust
224    #[cfg_attr(feature = "sync", doc = "driver.servers_with_routing(ServerRouting::Auto);")]
225    #[cfg_attr(not(feature = "sync"), doc = "driver.servers_with_routing(ServerRouting::Auto).await;")]
226    /// ```
227    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
228    pub async fn servers_with_routing(&self, server_routing: ServerRouting) -> Result<HashSet<Server>> {
229        self.server_manager.fetch_servers(server_routing).await
230    }
231
232    // TODO: Add servers_get call for a specific server. How to design it?
233
234    /// Retrieves the primary server, if exists, using default automatic server routing.
235    ///
236    /// See [`Self::primary_server_with_routing`] for more details and options.
237    ///
238    /// # Examples
239    ///
240    /// ```rust
241    #[cfg_attr(feature = "sync", doc = "driver.primary_server();")]
242    #[cfg_attr(not(feature = "sync"), doc = "driver.primary_server().await;")]
243    /// ```
244    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
245    pub async fn primary_server(&self) -> Result<Option<AvailableServer>> {
246        self.primary_server_with_routing(ServerRouting::Auto).await
247    }
248
249    /// Retrieves the primary server, if exists.
250    ///
251    /// # Arguments
252    ///
253    /// * `server_routing` — The server routing directive to use for the operation
254    ///
255    /// # Examples
256    ///
257    /// ```rust
258    #[cfg_attr(feature = "sync", doc = "driver.primary_server_with_routing(ServerRouting::Auto);")]
259    #[cfg_attr(not(feature = "sync"), doc = "driver.primary_server_with_routing(ServerRouting::Auto).await;")]
260    /// ```
261    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
262    pub async fn primary_server_with_routing(&self, server_routing: ServerRouting) -> Result<Option<AvailableServer>> {
263        self.server_manager.fetch_primary_server(server_routing).await
264    }
265
266    /// Updates address translation of the driver. This lets you actualize new translation
267    /// information without recreating the driver from scratch. Useful after registering new
268    /// replicas requiring address translation.
269    /// This operation will update existing connections using the provided addresses.
270    ///
271    /// The ``DriverOptions`` for this connection.
272    ///
273    /// # Examples
274    ///
275    /// ```rust
276    /// driver.options()
277    /// ```
278    pub fn options(&self) -> &DriverOptions {
279        self.server_manager.driver_options()
280    }
281
282    /// The ``Addresses`` this connection is configured to.
283    ///
284    /// # Examples
285    ///
286    /// ```rust
287    /// driver.configured_addresses()
288    /// ```
289    pub fn configured_addresses(&self) -> &Addresses {
290        self.server_manager.configured_addresses()
291    }
292
293    /// Opens a transaction with default options.
294    ///
295    /// See [`TypeDBDriver::transaction_with_options`] for more details.
296    ///
297    /// # Examples
298    ///
299    /// ```rust
300    #[cfg_attr(feature = "sync", doc = "driver.transaction(database_name, TransactionType::Read);")]
301    #[cfg_attr(not(feature = "sync"), doc = "driver.transaction(database_name, TransactionType::Read).await;")]
302    /// ```
303    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
304    pub async fn transaction(
305        &self,
306        database_name: impl AsRef<str>,
307        transaction_type: TransactionType,
308    ) -> Result<Transaction> {
309        self.transaction_with_options(database_name, transaction_type, TransactionOptions::new()).await
310    }
311
312    /// Opens a new transaction with custom transaction options.
313    ///
314    /// # Arguments
315    ///
316    /// * `database_name` — The name of the database to connect to
317    /// * `transaction_type` — The TransactionType to open the transaction with
318    /// * `options` — The TransactionOptions to open the transaction with
319    ///
320    /// # Examples
321    ///
322    /// ```rust
323    #[cfg_attr(
324        feature = "sync",
325        doc = "transaction.transaction_with_options(database_name, transaction_type, options)"
326    )]
327    #[cfg_attr(
328        not(feature = "sync"),
329        doc = "transaction.transaction_with_options(database_name, transaction_type, options).await"
330    )]
331    /// ```
332    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
333    pub async fn transaction_with_options(
334        &self,
335        database_name: impl AsRef<str>,
336        transaction_type: TransactionType,
337        options: TransactionOptions,
338    ) -> Result<Transaction> {
339        let database_name = database_name.as_ref();
340        let open_fn = |server_connection: ServerConnection| {
341            let options = options.clone();
342            async move { server_connection.open_transaction(database_name, transaction_type, options).await }
343        };
344
345        debug!("Opening transaction for database: {} with type: {:?}", database_name, transaction_type);
346        let transaction_stream = self.server_manager.execute(ServerRouting::Auto, open_fn).await?;
347
348        debug!("Successfully opened transaction for database: {}", database_name);
349        Ok(Transaction::new(transaction_stream))
350    }
351
352    /// Closes this connection if it is open.
353    ///
354    /// # Examples
355    ///
356    /// ```rust
357    /// driver.force_close()
358    /// ```
359    pub fn force_close(&self) -> Result {
360        if !self.is_open() {
361            return Ok(());
362        }
363
364        debug!("Closing TypeDB driver connection");
365        let close_result = self.server_manager.force_close().and(self.background_runtime.force_close());
366        match &close_result {
367            Ok(_) => debug!("Successfully closed TypeDB driver connection"),
368            Err(e) => error!("Failed to close TypeDB driver connection: {}", e),
369        }
370        close_result
371    }
372}
373
374impl fmt::Debug for TypeDBDriver {
375    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376        f.debug_struct("TypeDBDriver").field("server_manager", &self.server_manager).finish()
377    }
378}