1
//! Program configurations.
2

            
3
use std::{collections::HashMap, env};
4

            
5
use clap::{builder::RangedU64ValueParser, Arg, ArgMatches, Command};
6
use serde::Deserialize;
7
use serde_json;
8

            
9
use sylvia_iot_corelib::constants::DbEngine;
10

            
11
/// Configuration file object.
12
#[derive(Default, Deserialize)]
13
pub struct Config {
14
    pub db: Option<Db>,
15
    #[serde(rename = "apiScopes")]
16
    pub api_scopes: Option<HashMap<String, Vec<String>>>,
17
    pub templates: Option<HashMap<String, String>>,
18
}
19

            
20
/// Database configuration object.
21
#[derive(Default, Deserialize)]
22
pub struct Db {
23
    /// Select the model implementation.
24
    /// - `mongodb`: pure MongoDB.
25
    /// - `sqlite`: pure SQLite.
26
    pub engine: Option<String>,
27
    pub mongodb: Option<MongoDb>,
28
    pub sqlite: Option<Sqlite>,
29
}
30

            
31
/// MongoDB configuration object.
32
#[derive(Default, Deserialize)]
33
pub struct MongoDb {
34
    /// Use `mongodb://username:password@host:port` format.
35
    pub url: Option<String>,
36
    pub database: Option<String>,
37
    #[serde(rename = "poolSize")]
38
    pub pool_size: Option<u32>,
39
}
40

            
41
/// SQLite configuration object.
42
#[derive(Default, Deserialize)]
43
pub struct Sqlite {
44
    /// Use absolute/relative path.
45
    pub path: Option<String>,
46
}
47

            
48
pub const DEF_ENGINE: &'static str = DbEngine::SQLITE;
49
pub const DEF_MONGODB_URL: &'static str = "mongodb://localhost:27017";
50
pub const DEF_MONGODB_DB: &'static str = "auth";
51
pub const DEF_SQLITE_PATH: &'static str = "auth.db";
52

            
53
/// To register Clap arguments.
54
8
pub fn reg_args(cmd: Command) -> Command {
55
8
    cmd.arg(
56
8
        Arg::new("auth.db.engine")
57
8
            .long("auth.db.engine")
58
8
            .help("database engine")
59
8
            .num_args(1)
60
8
            .value_parser([DbEngine::MONGODB, DbEngine::SQLITE]),
61
8
    )
62
8
    .arg(
63
8
        Arg::new("auth.db.mongodb.url")
64
8
            .long("auth.db.mongodb.url")
65
8
            .help("MongoDB URL (scheme://[username][:password][@][host][:port]")
66
8
            .num_args(1),
67
8
    )
68
8
    .arg(
69
8
        Arg::new("auth.db.mongodb.database")
70
8
            .long("auth.db.mongodb.database")
71
8
            .help("database nane")
72
8
            .num_args(1),
73
8
    )
74
8
    .arg(
75
8
        Arg::new("auth.db.mongodb.poolsize")
76
8
            .long("auth.db.mongodb.poolsize")
77
8
            .help("connection pool size")
78
8
            .num_args(1)
79
8
            .value_parser(RangedU64ValueParser::<u64>::new().range(1..=u32::MAX as u64)),
80
8
    )
81
8
    .arg(
82
8
        Arg::new("auth.db.sqlite.path")
83
8
            .long("auth.db.sqlite.path")
84
8
            .help("SQLite path")
85
8
            .num_args(1),
86
8
    )
87
8
    .arg(
88
8
        Arg::new("auth.api-scopes")
89
8
            .long("auth.api-scopes")
90
8
            .help("API scopes")
91
8
            .num_args(0..),
92
8
    )
93
8
    .arg(
94
8
        Arg::new("auth.templates")
95
8
            .long("auth.templates")
96
8
            .help("Template paths for pages")
97
8
            .num_args(0..),
98
8
    )
99
8
}
100

            
101
/// To read input arguments from command-line arguments and environment variables.
102
///
103
/// This function will call [`apply_default()`] to fill missing values so you do not need call it
104
/// again.
105
8
pub fn read_args(args: &ArgMatches) -> Config {
106
8
    apply_default(&Config {
107
8
        db: Some(Db {
108
8
            engine: match args.get_one::<String>("auth.db.engine") {
109
5
                None => match env::var("AUTH_DB_ENGINE") {
110
2
                    Err(_) => None,
111
3
                    Ok(v) => Some(v),
112
                },
113
3
                Some(v) => Some(v.clone()),
114
            },
115
            mongodb: Some(MongoDb {
116
8
                url: match args.get_one::<String>("auth.db.mongodb.url") {
117
6
                    None => match env::var("AUTH_DB_MONGODB_URL") {
118
3
                        Err(_) => None,
119
3
                        Ok(v) => Some(v),
120
                    },
121
2
                    Some(v) => Some(v.clone()),
122
                },
123
8
                database: match args.get_one::<String>("auth.db.mongodb.database") {
124
6
                    None => match env::var("AUTH_DB_MONGODB_DATABASE") {
125
3
                        Err(_) => None,
126
3
                        Ok(v) => Some(v),
127
                    },
128
2
                    Some(v) => Some(v.clone()),
129
                },
130
8
                pool_size: match args.get_one::<u64>("auth.db.mongodb.poolsize") {
131
6
                    None => match env::var("AUTH_DB_MONGODB_POOLSIZE") {
132
3
                        Err(_) => None,
133
3
                        Ok(v) => match v.parse::<u32>() {
134
1
                            Err(_) => None,
135
2
                            Ok(v) => Some(v),
136
                        },
137
                    },
138
2
                    Some(v) => Some(*v as u32),
139
                },
140
            }),
141
            sqlite: Some(Sqlite {
142
8
                path: match args.get_one::<String>("auth.db.sqlite.path") {
143
6
                    None => match env::var("AUTH_DB_SQLITE_PATH") {
144
3
                        Err(_) => None,
145
3
                        Ok(v) => Some(v),
146
                    },
147
2
                    Some(v) => Some(v.clone()),
148
                },
149
            }),
150
        }),
151
8
        api_scopes: match args.get_one::<String>("auth.api-scopes") {
152
4
            None => match env::var("AUTH_API_SCOPES") {
153
1
                Err(_) => None,
154
3
                Ok(v) => match v.len() {
155
1
                    0 => None,
156
2
                    _ => match serde_json::from_str::<HashMap<String, Vec<String>>>(v.as_str()) {
157
1
                        Err(_) => None,
158
1
                        Ok(v) => Some(v),
159
                    },
160
                },
161
            },
162
4
            Some(v) => match v.len() {
163
1
                0 => None,
164
3
                _ => match serde_json::from_str::<HashMap<String, Vec<String>>>(v.as_str()) {
165
1
                    Err(_) => None,
166
2
                    Ok(v) => Some(v),
167
                },
168
            },
169
        },
170
8
        templates: match args.get_one::<String>("auth.templates") {
171
4
            None => match env::var("AUTH_TEMPLATES") {
172
1
                Err(_) => None,
173
3
                Ok(v) => match v.len() {
174
1
                    0 => None,
175
2
                    _ => match serde_json::from_str::<HashMap<String, String>>(v.as_str()) {
176
1
                        Err(_) => None,
177
1
                        Ok(v) => Some(v),
178
                    },
179
                },
180
            },
181
4
            Some(v) => match serde_json::from_str::<HashMap<String, String>>(v.as_str()) {
182
2
                Err(_) => None,
183
2
                Ok(v) => Some(v),
184
            },
185
        },
186
    })
187
8
}
188

            
189
/// Fill missing configuration with default values.
190
20
pub fn apply_default(config: &Config) -> Config {
191
20
    Config {
192
20
        db: match config.db.as_ref() {
193
2
            None => Some(Db {
194
2
                engine: Some(DEF_ENGINE.to_string()),
195
2
                mongodb: Some(MongoDb {
196
2
                    url: Some(DEF_MONGODB_URL.to_string()),
197
2
                    database: Some(DEF_MONGODB_DB.to_string()),
198
2
                    pool_size: None,
199
2
                }),
200
2
                sqlite: Some(Sqlite {
201
2
                    path: Some(DEF_SQLITE_PATH.to_string()),
202
2
                }),
203
2
            }),
204
18
            Some(db) => Some(Db {
205
18
                engine: match db.engine.as_ref() {
206
3
                    None => Some(DEF_ENGINE.to_string()),
207
15
                    Some(engine) => match engine.as_str() {
208
15
                        DbEngine::MONGODB => Some(DbEngine::MONGODB.to_string()),
209
8
                        DbEngine::SQLITE => Some(DbEngine::SQLITE.to_string()),
210
3
                        _ => Some(DEF_ENGINE.to_string()),
211
                    },
212
                },
213
18
                mongodb: match db.mongodb.as_ref() {
214
5
                    None => Some(MongoDb {
215
5
                        url: Some(DEF_MONGODB_URL.to_string()),
216
5
                        database: Some(DEF_MONGODB_DB.to_string()),
217
5
                        pool_size: None,
218
5
                    }),
219
13
                    Some(mongodb) => Some(MongoDb {
220
13
                        url: match mongodb.url.as_ref() {
221
3
                            None => Some(DEF_MONGODB_URL.to_string()),
222
10
                            Some(url) => Some(url.to_string()),
223
                        },
224
13
                        database: match mongodb.database.as_ref() {
225
3
                            None => Some(DEF_MONGODB_DB.to_string()),
226
10
                            Some(database) => Some(database.to_string()),
227
                        },
228
13
                        pool_size: mongodb.pool_size,
229
                    }),
230
                },
231
18
                sqlite: match db.sqlite.as_ref() {
232
5
                    None => Some(Sqlite {
233
5
                        path: Some(DEF_SQLITE_PATH.to_string()),
234
5
                    }),
235
13
                    Some(sqlite) => Some(Sqlite {
236
13
                        path: match sqlite.path.as_ref() {
237
3
                            None => Some(DEF_SQLITE_PATH.to_string()),
238
10
                            Some(path) => Some(path.to_string()),
239
                        },
240
                    }),
241
                },
242
            }),
243
        },
244
20
        api_scopes: match config.api_scopes.as_ref() {
245
16
            None => Some(HashMap::new()),
246
4
            Some(scopes) => Some(scopes.clone()),
247
        },
248
20
        templates: match config.templates.as_ref() {
249
16
            None => Some(HashMap::new()),
250
4
            Some(templates) => Some(templates.clone()),
251
        },
252
    }
253
20
}