aragog/db/
database_connection_builder.rs

1#![allow(clippy::redundant_pub_crate)]
2use std::convert::{TryFrom, TryInto};
3
4use crate::schema::{DatabaseSchema, SCHEMA_DEFAULT_FILE_NAME, SCHEMA_DEFAULT_PATH};
5use crate::{AuthMode, DatabaseConnection, Error, OperationOptions};
6
7#[derive(Debug, Clone)]
8pub(crate) struct DbCredentials {
9    db_host: String,
10    db_name: String,
11    db_user: String,
12    db_password: String,
13}
14
15#[derive(Debug, Clone)]
16pub(crate) enum DbCredentialsOption {
17    Auto,
18    Custom(DbCredentials),
19}
20
21#[derive(Debug)]
22pub(crate) enum DatabaseSchemaOption {
23    Auto,
24    Path(String),
25    Custom(DatabaseSchema),
26}
27
28impl From<DbCredentialsOption> for DbCredentials {
29    fn from(option: DbCredentialsOption) -> Self {
30        match option {
31            DbCredentialsOption::Custom(cred) => cred,
32            DbCredentialsOption::Auto => Self {
33                db_host: std::env::var("DB_HOST").expect("Please define DB_HOST env var."),
34                db_name: std::env::var("DB_NAME").expect("Please define DB_NAME env var."),
35                db_user: std::env::var("DB_USER").expect("Please define DB_USER env var."),
36                db_password: std::env::var("DB_PASSWORD")
37                    .expect("Please define DB_PASSWORD env var."),
38            },
39        }
40    }
41}
42
43impl TryFrom<DatabaseSchemaOption> for DatabaseSchema {
44    type Error = Error;
45
46    fn try_from(option: DatabaseSchemaOption) -> Result<Self, Self::Error> {
47        match option {
48            DatabaseSchemaOption::Custom(schema) => Ok(schema),
49            DatabaseSchemaOption::Path(path) => Self::load(&path),
50            DatabaseSchemaOption::Auto => {
51                let schema_path = match std::env::var("SCHEMA_PATH") {
52                    Ok(path) => path,
53                    Err(_err) => {
54                        log::debug!(
55                            "Missing SCHEMA_PATH env var, using default value: {}",
56                            SCHEMA_DEFAULT_PATH
57                        );
58                        SCHEMA_DEFAULT_PATH.to_string()
59                    }
60                };
61                Self::load(&format!("{}/{}", schema_path, SCHEMA_DEFAULT_FILE_NAME))
62            }
63        }
64    }
65}
66
67/// Builder for `DatabaseConnection`
68pub struct DatabaseConnectionBuilder {
69    pub(crate) apply_schema: bool,
70    pub(crate) auth_mode: AuthMode,
71    pub(crate) credentials: DbCredentialsOption,
72    pub(crate) schema: DatabaseSchemaOption,
73    pub(crate) operation_options: OperationOptions,
74}
75
76impl DatabaseConnectionBuilder {
77    /// Initializes the Database connection according to specified building methods.
78    ///
79    /// If nothing was set like in this example:
80    /// ```rust
81    /// # use aragog::DatabaseConnection;
82    /// # #[tokio::main]
83    /// # async fn main() {
84    /// let db_connection = DatabaseConnection::builder()
85    /// # .with_schema_path("tests/schema.yaml")
86    /// # .apply_schema()
87    ///     .build()
88    ///     .await
89    ///     .unwrap();
90    /// # }
91    /// ```
92    /// The builder will use the following:
93    /// - The schema will be loaded though `SCHEMA_PATH` env var or the default value: `./src/config/db/schema.yaml`
94    /// - The credentials will be loaded through the following env vars:
95    ///     * `DB_HOST` - The `ArangoDB` host to connect to (`http://localhost:8529` by default or `http://arangodb:8529` on docker containers)
96    ///     * `DB_NAME` - The name of the `ArangoDB` database to connect to
97    ///     * `DB_USER` - The username of a ArangoDb user with access to the database
98    ///     * `DB_PASSWORD` - The password associated with `DB_USER`
99    /// - The auth mode will be `AuthMode::Basic`
100    ///
101    /// # Panics
102    ///
103    /// If the provided credentials are wrong or if the database is not running the function will panic.
104    /// If any of the previous env var is not specified the function will panic with an explanation message.
105    #[maybe_async::maybe_async]
106    pub async fn build(self) -> Result<DatabaseConnection, Error> {
107        let credentials = self.credentials();
108        let auth_mode = self.auth_mode();
109        let apply_schema = self.apply_schema;
110        let operation_options = self.operation_options.clone();
111        let schema = self.schema()?;
112        let database = DatabaseConnection::connect(
113            &credentials.db_host,
114            &credentials.db_name,
115            &credentials.db_user,
116            &credentials.db_password,
117            auth_mode,
118        )
119        .await?;
120        DatabaseConnection::new(database, schema, apply_schema, operation_options).await
121    }
122
123    /// Specifies a custom authentication mode for `ArangoDB` connection.
124    ///
125    /// If not specified `AuthMode::Basic` will be used.
126    #[must_use]
127    #[inline]
128    pub fn with_auth_mode(mut self, mode: AuthMode) -> Self {
129        log::debug!(
130            "[Database Connection Builder] Auth mode {:?} will be used",
131            mode
132        );
133        self.auth_mode = mode;
134        self
135    }
136
137    /// Specifies custom credentials for `ArangoDB` connection
138    ///
139    /// # Arguments
140    ///
141    /// * `db_host` - The `ArangoDB` host to connect to (`http://localhost:8529` by default or `http://arangodb:8529` on docker containers)
142    /// * `db_name` - The name of the `ArangoDB` database to connect to
143    /// * `db_user` - The username of a `ArangoDB` user with access to the database
144    /// * `db_password` - The password associated with `db_user`
145    #[must_use]
146    #[inline]
147    pub fn with_credentials(
148        mut self,
149        db_host: &str,
150        db_name: &str,
151        db_user: &str,
152        db_password: &str,
153    ) -> Self {
154        log::debug!(
155            "[Database Connection Builder] Custom credentials for `ArangoDB` host {} will be used",
156            db_host
157        );
158        self.credentials = DbCredentialsOption::Custom(DbCredentials {
159            db_host: String::from(db_host),
160            db_name: String::from(db_name),
161            db_user: String::from(db_user),
162            db_password: String::from(db_password),
163        });
164        self
165    }
166
167    /// Specifies a custom schema for `ArangoDB` initialization.
168    ///
169    /// If not specified,`SCHEMA_PATH` env var will be used or the default value: `./src/config/db/schema.yaml`
170    #[must_use]
171    #[inline]
172    pub fn with_schema(mut self, schema: DatabaseSchema) -> Self {
173        log::debug!("[Database Connection Builder] Custom schema will be used");
174        self.schema = DatabaseSchemaOption::Custom(schema);
175        self
176    }
177
178    /// Call this method if you want the schema to be applied.
179    /// This will ignore any errors, so check the `debug` to find a hidden issue.
180    ///
181    /// Use it when you use your own custom schema and no `aragog_cli` migrations.
182    #[must_use]
183    #[inline]
184    pub fn apply_schema(mut self) -> Self {
185        log::debug!("[Database Connection Builder] Schema will be silently applied");
186        self.apply_schema = true;
187        self
188    }
189
190    /// Specifies a custom schema path for `ArangoDB` initialization.
191    ///
192    /// If not specified,`SCHEMA_PATH` env var will be used or the default value: `./src/config/db/schema.yaml`
193    #[must_use]
194    #[inline]
195    pub fn with_schema_path(mut self, path: &str) -> Self {
196        log::debug!(
197            "[Database Connection Builder] Schema from {} will be used",
198            path
199        );
200        self.schema = DatabaseSchemaOption::Path(String::from(path));
201        self
202    }
203
204    /// Specifies custom options for `write` operations (`create`, `save`, `delete`)
205    ///
206    /// # Note
207    ///
208    /// These options will be used globally as a default value, meaning you don't need to specify
209    /// duplicate options when using the [`DatabaseRecord`] API.
210    ///
211    /// If you set `ignore_hooks` here, every [`DatabaseRecord`] operation will skip hooks.
212    ///
213    /// [`DatabaseRecord`]: crate::DatabaseRecord
214    #[must_use]
215    #[inline]
216    pub fn with_operation_options(mut self, options: OperationOptions) -> Self {
217        log::debug!(
218            "[Database Connection Builder] custom operation options will be used: {:?}",
219            options
220        );
221        self.operation_options = options;
222        self
223    }
224
225    #[must_use]
226    #[inline]
227    fn credentials(&self) -> DbCredentials {
228        self.credentials.clone().into()
229    }
230
231    fn schema(self) -> Result<DatabaseSchema, Error> {
232        self.schema.try_into()
233    }
234
235    #[must_use]
236    #[inline]
237    const fn auth_mode(&self) -> AuthMode {
238        self.auth_mode
239    }
240}