Skip to main content

deadpool_citadeldb/
config.rs

1use std::path::PathBuf;
2
3use crate::{CreatePoolError, Manager, Pool, PoolBuilder, PoolConfig, Runtime};
4
5/// Configuration object.
6///
7/// # Example (from environment)
8///
9/// By enabling the `serde` feature you can read the configuration using the
10/// [`config`](https://crates.io/crates/config) crate as following:
11/// ```env
12/// CITADELDB__PATH=citadel.db
13/// CITADELDB__PASSPHRASE=secret
14/// CITADELDB__POOL__MAX_SIZE=16
15/// CITADELDB__POOL__TIMEOUTS__WAIT__SECS=5
16/// CITADELDB__POOL__TIMEOUTS__WAIT__NANOS=0
17/// ```
18/// ```rust,ignore
19/// #[derive(serde::Deserialize, serde::Serialize)]
20/// struct Config {
21///     citadeldb: deadpool_citadeldb::Config,
22/// }
23/// impl Config {
24///     pub fn from_env() -> Result<Self, config::ConfigError> {
25///         let mut cfg = config::Config::builder()
26///            .add_source(config::Environment::default().separator("__"))
27///            .build()?;
28///            cfg.try_deserialize()
29///     }
30/// }
31/// ```
32#[derive(Clone, Debug)]
33#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
34pub struct Config {
35    /// Path to CitadelDB database file. Empty string creates an in-memory database.
36    pub path: PathBuf,
37    /// Passphrase for the CitadelDB database.
38    #[cfg_attr(feature = "serde", serde(skip_serializing))]
39    pub passphrase: Vec<u8>,
40    /// [`Pool`] configuration.
41    pub pool: Option<PoolConfig>,
42}
43
44impl Config {
45    /// Create a new [`Config`] with the given `path` of CitadelDB database
46    /// file and `passphrase`. Use an empty path for an in-memory database.
47    #[must_use]
48    pub fn new(path: impl Into<PathBuf>, passphrase: &[u8]) -> Self {
49        Self {
50            path: path.into(),
51            passphrase: passphrase.to_vec(),
52            pool: None,
53        }
54    }
55
56    /// Creates a new [`Pool`] using this [`Config`].
57    ///
58    /// # Errors
59    ///
60    /// See [`CreatePoolError`] for details.
61    pub fn create_pool(&self, runtime: Runtime) -> Result<Pool, CreatePoolError> {
62        self.builder(runtime)
63            .map_err(CreatePoolError::Config)?
64            .build()
65            .map_err(CreatePoolError::Build)
66    }
67
68    /// Creates a new [`PoolBuilder`] using this [`Config`].
69    ///
70    /// # Errors
71    ///
72    /// See [`ConfigError`] for details.
73    pub fn builder(&self, runtime: Runtime) -> Result<PoolBuilder, ConfigError> {
74        let manager = Manager::from_config(self, runtime);
75        Ok(Pool::builder(manager)
76            .config(self.get_pool_config())
77            .runtime(runtime))
78    }
79
80    /// Returns [`PoolConfig`] which can be used to construct
81    /// a [`Pool`] instance.
82    #[must_use]
83    pub fn get_pool_config(&self) -> PoolConfig {
84        self.pool.unwrap_or_default()
85    }
86
87    /// Create a [`citadel::Database`] from this configuration.
88    ///
89    /// If `path` is empty, an in-memory database is created.
90    /// If the file exists, it is opened; otherwise a new database is created.
91    pub(crate) fn create_database(&self) -> Result<citadel::Database, ConfigError> {
92        if self.path.as_os_str().is_empty() {
93            citadel::DatabaseBuilder::new("")
94                .passphrase(&self.passphrase)
95                .create_in_memory()
96        } else if self.path.exists() {
97            citadel::DatabaseBuilder::new(&self.path)
98                .passphrase(&self.passphrase)
99                .open()
100        } else {
101            citadel::DatabaseBuilder::new(&self.path)
102                .passphrase(&self.passphrase)
103                .create()
104        }
105    }
106}
107/// This error is returned if there is something wrong with the SQLite configuration.
108///
109/// This is just a type alias to [`citadel::Error`] at the moment as there
110/// is no validation happening at the configuration phase.
111pub type ConfigError = citadel::Error;