notegraf_web/
configuration.rs

1use notegraf::notestore::BoxedNoteStore;
2use notegraf::{InMemoryStore, PostgreSQLStoreBuilder};
3use sqlx::postgres::PgConnectOptions;
4use sqlx::{ConnectOptions, Connection, Executor, PgConnection};
5use tracing::log::LevelFilter;
6use uuid::Uuid;
7
8#[derive(serde::Deserialize, Debug)]
9pub enum NoteStoreType {
10    InMemory,
11    PostgreSQL,
12}
13
14#[derive(serde::Deserialize, Debug)]
15pub struct Settings {
16    database: Option<DatabaseSettings>,
17    pub host: String,
18    pub port: u16,
19    pub debug: bool,
20    notestoretype: NoteStoreType,
21    populatetestdata: bool,
22    pub otlpendpoint: Option<String>,
23    pub loglevel: Option<String>,
24}
25
26impl Settings {
27    pub async fn get_note_store(
28        &self,
29        random_db: bool,
30        log_statement_filter: LevelFilter,
31    ) -> BoxedNoteStore<crate::NoteType> {
32        let store: BoxedNoteStore<crate::NoteType> = match self.notestoretype {
33            NoteStoreType::InMemory => Box::new(InMemoryStore::new()),
34            NoteStoreType::PostgreSQL => {
35                let database_settings = CONFIGURATION.database
36                    .as_ref()
37                    .expect("When notestoretype is set to PostgreSQL, you must configure the keys under database");
38                let mut db_options = if random_db {
39                    let db_name = Uuid::new_v4().to_string();
40                    let db_options = database_settings.options_without_db();
41                    let mut connection = PgConnection::connect_with(&db_options)
42                        .await
43                        .expect("Failed to connect to Postgres");
44                    connection
45                        .execute(&*format!(r#"CREATE DATABASE "{}";"#, db_name))
46                        .await
47                        .expect("Failed to create database.");
48                    db_options.database(&db_name)
49                } else {
50                    database_settings.options()
51                };
52                db_options.log_statements(log_statement_filter);
53                Box::new(PostgreSQLStoreBuilder::new(db_options).build().await)
54            }
55        };
56        if cfg!(feature = "notetype_markdown") && self.populatetestdata {
57            notegraf::notestore::util::populate_test_data(&store).await;
58        }
59        store
60    }
61}
62
63#[derive(serde::Deserialize, Clone, Debug)]
64pub struct DatabaseSettings {
65    pub port: String,
66    pub host: String,
67    pub name: String,
68    pub username: Option<String>,
69    pub password: Option<String>,
70}
71
72impl DatabaseSettings {
73    pub fn options(&self) -> PgConnectOptions {
74        self.options_without_db().database(&self.name)
75    }
76
77    pub fn options_without_db(&self) -> PgConnectOptions {
78        let options = PgConnectOptions::new()
79            .host(&self.host)
80            .port(self.port.parse().expect("Failed to parse port number"));
81        if let Some(ref username) = self.username {
82            let password = self
83                .password
84                .as_ref()
85                .expect("Password expected when a username is set");
86            options.username(username).password(password)
87        } else {
88            options
89        }
90    }
91}
92
93lazy_static! {
94    pub static ref CONFIGURATION: Settings =
95        get_configuration().expect("Failed to read configuration.yml.");
96}
97
98pub fn get_configuration() -> Result<Settings, config::ConfigError> {
99    let config = config::Config::builder()
100        .set_default("debug", false)?
101        .set_default("host", "localhost")?
102        .set_default("populatetestdata", false)?
103        .add_source(config::File::with_name("configuration").required(false))
104        .add_source(
105            config::Environment::default()
106                .prefix("notegraf")
107                .separator("_"),
108        )
109        .build()?;
110    config.try_deserialize()
111}