1
//! Program configurations.
2

            
3
use std::env;
4

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

            
8
use sylvia_iot_corelib::constants::DbEngine;
9

            
10
/// Configuration file object.
11
#[derive(Default, Deserialize)]
12
pub struct Config {
13
    /// **sylvia-iot-auth** API base path with host. For example: `http://localhost:1080/auth`.
14
    pub auth: Option<String>,
15
    /// **sylvia-iot-broker** API base path with host. For example: `http://localhost:2080/broker`.
16
    pub broker: Option<String>,
17
    pub db: Option<Db>,
18
    #[serde(rename = "mqChannels")]
19
    pub mq_channels: Option<MqChannels>,
20
}
21

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

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

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

            
50
/// Message channels configuration object.
51
#[derive(Default, Deserialize)]
52
pub struct MqChannels {
53
    pub broker: Option<DataData>,
54
    pub coremgr: Option<DataData>,
55
}
56

            
57
/// Channel `broker.data` `coremgr.data` configuration object.
58
#[derive(Default, Deserialize)]
59
pub struct DataData {
60
    /// Queue connection URL of the data channel.
61
    pub url: Option<String>,
62
    /// AMQP QoS prefetch from **1** to **65535**. None or zero use default value **100**.
63
    pub prefetch: Option<u16>,
64
    /// MQTT shared subscription topic prefix.
65
    #[serde(rename = "sharedPrefix")]
66
    pub shared_prefix: Option<String>,
67
}
68

            
69
pub const DEF_AUTH: &'static str = "http://localhost:1080/auth";
70
pub const DEF_BROKER: &'static str = "http://localhost:2080/broker";
71
pub const DEF_ENGINE: &'static str = DbEngine::SQLITE;
72
pub const DEF_MONGODB_URL: &'static str = "mongodb://localhost:27017";
73
pub const DEF_MONGODB_DB: &'static str = "data";
74
pub const DEF_SQLITE_PATH: &'static str = "data.db";
75
pub const DEF_MQ_PREFETCH: u16 = 100;
76
pub const DEF_MQ_SHAREDPREFIX: &'static str = "$share/sylvia-iot-data/";
77
pub const DEF_MQ_CHANNEL_URL: &'static str = "amqp://localhost";
78

            
79
/// To register Clap arguments.
80
10
pub fn reg_args(cmd: Command) -> Command {
81
10
    cmd.arg(
82
10
        Arg::new("data.auth")
83
10
            .long("data.auth")
84
10
            .help("sylvia-iot-auth host (ex: http://localhost:1080/auth)")
85
10
            .num_args(1),
86
10
    )
87
10
    .arg(
88
10
        Arg::new("data.broker")
89
10
            .long("data.broker")
90
10
            .help("sylvia-iot-broker host (ex: http://localhost:2080/broker)")
91
10
            .num_args(1),
92
10
    )
93
10
    .arg(
94
10
        Arg::new("data.db.engine")
95
10
            .long("data.db.engine")
96
10
            .help("database engine")
97
10
            .num_args(1)
98
10
            .value_parser([DbEngine::MONGODB, DbEngine::SQLITE]),
99
10
    )
100
10
    .arg(
101
10
        Arg::new("data.db.mongodb.url")
102
10
            .long("data.db.mongodb.url")
103
10
            .help("MongoDB URL (scheme://[username][:password][@][host][:port]")
104
10
            .num_args(1),
105
10
    )
106
10
    .arg(
107
10
        Arg::new("data.db.mongodb.database")
108
10
            .long("data.db.mongodb.database")
109
10
            .help("database nane")
110
10
            .num_args(1),
111
10
    )
112
10
    .arg(
113
10
        Arg::new("data.db.mongodb.poolsize")
114
10
            .long("data.db.mongodb.poolsize")
115
10
            .help("connection pool size")
116
10
            .num_args(1)
117
10
            .value_parser(RangedU64ValueParser::<u64>::new().range(1..=u32::MAX as u64)),
118
10
    )
119
10
    .arg(
120
10
        Arg::new("data.db.sqlite.path")
121
10
            .long("data.db.sqlite.path")
122
10
            .help("SQLite path")
123
10
            .num_args(1),
124
10
    )
125
10
    .arg(
126
10
        Arg::new("data.mq-channels.broker.url")
127
10
            .long("data.mq-channels.broker.url")
128
10
            .help("URL of `broker.data` channel")
129
10
            .num_args(1),
130
10
    )
131
10
    .arg(
132
10
        Arg::new("data.mq-channels.broker.prefetch")
133
10
            .long("data.mq-channels.broker.prefetch")
134
10
            .help("AMQP prefetch for `broker.data` channel")
135
10
            .num_args(1)
136
10
            .value_parser(RangedU64ValueParser::<u64>::new().range(1..=u16::MAX as u64)),
137
10
    )
138
10
    .arg(
139
10
        Arg::new("data.mq-channels.broker.sharedprefix")
140
10
            .long("data.mq-channels.broker.sharedprefix")
141
10
            .help("MQTT shared subscription prefix of `broker.data` channel")
142
10
            .num_args(1),
143
10
    )
144
10
    .arg(
145
10
        Arg::new("data.mq-channels.coremgr.url")
146
10
            .long("data.mq-channels.coremgr.url")
147
10
            .help("URL of `coremgr.data` channel")
148
10
            .num_args(1),
149
10
    )
150
10
    .arg(
151
10
        Arg::new("data.mq-channels.coremgr.prefetch")
152
10
            .long("data.mq-channels.coremgr.prefetch")
153
10
            .help("AMQP prefetch for `coremgr.data` channel")
154
10
            .num_args(1)
155
10
            .value_parser(RangedU64ValueParser::<u64>::new().range(1..=u16::MAX as u64)),
156
10
    )
157
10
    .arg(
158
10
        Arg::new("data.mq-channels.coremgr.sharedprefix")
159
10
            .long("data.mq-channels.coremgr.sharedprefix")
160
10
            .help("MQTT shared subscription prefix of `coremgr.data` channel")
161
10
            .num_args(1),
162
10
    )
163
10
}
164

            
165
/// To read input arguments from command-line arguments and environment variables.
166
///
167
/// This function will call [`apply_default()`] to fill missing values so you do not need call it
168
/// again.
169
10
pub fn read_args(args: &ArgMatches) -> Config {
170
10
    apply_default(&Config {
171
10
        auth: match args.get_one::<String>("data.auth") {
172
6
            None => match env::var("DATA_AUTH") {
173
2
                Err(_) => None,
174
4
                Ok(v) => Some(v),
175
            },
176
4
            Some(v) => Some(v.clone()),
177
        },
178
10
        broker: match args.get_one::<String>("data.broker") {
179
6
            None => match env::var("DATA_BROKER") {
180
2
                Err(_) => None,
181
4
                Ok(v) => Some(v),
182
            },
183
4
            Some(v) => Some(v.clone()),
184
        },
185
        db: Some(Db {
186
10
            engine: match args.get_one::<String>("data.db.engine") {
187
6
                None => match env::var("DATA_DB_ENGINE") {
188
2
                    Err(_) => None,
189
4
                    Ok(v) => Some(v),
190
                },
191
4
                Some(v) => Some(v.clone()),
192
            },
193
            mongodb: Some(MongoDb {
194
10
                url: match args.get_one::<String>("data.db.mongodb.url") {
195
6
                    None => match env::var("DATA_DB_MONGODB_URL") {
196
2
                        Err(_) => None,
197
4
                        Ok(v) => Some(v),
198
                    },
199
4
                    Some(v) => Some(v.clone()),
200
                },
201
10
                database: match args.get_one::<String>("data.db.mongodb.database") {
202
6
                    None => match env::var("DATA_DB_MONGODB_DATABASE") {
203
2
                        Err(_) => None,
204
4
                        Ok(v) => Some(v),
205
                    },
206
4
                    Some(v) => Some(v.clone()),
207
                },
208
10
                pool_size: match args.get_one::<u64>("data.db.mongodb.poolsize") {
209
6
                    None => match env::var("DATA_DB_MONGODB_POOLSIZE") {
210
2
                        Err(_) => None,
211
4
                        Ok(v) => match v.parse::<u32>() {
212
2
                            Err(_) => None,
213
2
                            Ok(v) => Some(v),
214
                        },
215
                    },
216
4
                    Some(v) => Some(*v as u32),
217
                },
218
            }),
219
            sqlite: Some(Sqlite {
220
10
                path: match args.get_one::<String>("data.db.sqlite.path") {
221
6
                    None => match env::var("DATA_DB_SQLITE_PATH") {
222
2
                        Err(_) => None,
223
4
                        Ok(v) => Some(v),
224
                    },
225
4
                    Some(v) => Some(v.clone()),
226
                },
227
            }),
228
        }),
229
        mq_channels: Some(MqChannels {
230
            broker: Some(DataData {
231
10
                url: match args.get_one::<String>("data.mq-channels.broker.url") {
232
6
                    None => match env::var("DATA_MQCHANNELS_BROKER_URL") {
233
2
                        Err(_) => None,
234
4
                        Ok(v) => Some(v),
235
                    },
236
4
                    Some(v) => Some(v.clone()),
237
                },
238
10
                prefetch: match args.get_one::<u64>("data.mq-channels.broker.prefetch") {
239
6
                    None => match env::var("DATA_MQCHANNELS_BROKER_PREFETCH") {
240
2
                        Err(_) => None,
241
4
                        Ok(v) => match v.parse::<u16>() {
242
2
                            Err(_) => None,
243
2
                            Ok(v) => Some(v),
244
                        },
245
                    },
246
4
                    Some(v) => Some(*v as u16),
247
                },
248
10
                shared_prefix: match args.get_one::<String>("data.mq-channels.broker.sharedprefix")
249
                {
250
6
                    None => match env::var("DATA_MQCHANNELS_BROKER_SHAREDPREFIX") {
251
2
                        Err(_) => None,
252
4
                        Ok(v) => Some(v),
253
                    },
254
4
                    Some(v) => Some(v.clone()),
255
                },
256
            }),
257
            coremgr: Some(DataData {
258
10
                url: match args.get_one::<String>("data.mq-channels.coremgr.url") {
259
6
                    None => match env::var("DATA_MQCHANNELS_COREMGR_URL") {
260
2
                        Err(_) => None,
261
4
                        Ok(v) => Some(v),
262
                    },
263
4
                    Some(v) => Some(v.clone()),
264
                },
265
10
                prefetch: match args.get_one::<u64>("data.mq-channels.coremgr.prefetch") {
266
6
                    None => match env::var("DATA_MQCHANNELS_COREMGR_PREFETCH") {
267
2
                        Err(_) => None,
268
4
                        Ok(v) => match v.parse::<u16>() {
269
2
                            Err(_) => None,
270
2
                            Ok(v) => Some(v),
271
                        },
272
                    },
273
4
                    Some(v) => Some(*v as u16),
274
                },
275
10
                shared_prefix: match args.get_one::<String>("data.mq-channels.coremgr.sharedprefix")
276
                {
277
6
                    None => match env::var("DATA_MQCHANNELS_COREMGR_SHAREDPREFIX") {
278
2
                        Err(_) => None,
279
4
                        Ok(v) => Some(v),
280
                    },
281
4
                    Some(v) => Some(v.clone()),
282
                },
283
            }),
284
        }),
285
    })
286
10
}
287

            
288
/// Fill missing configuration with default values.
289
34
pub fn apply_default(config: &Config) -> Config {
290
34
    Config {
291
34
        auth: match config.auth.as_ref() {
292
14
            None => Some(DEF_AUTH.to_string()),
293
20
            Some(auth) => Some(auth.clone()),
294
        },
295
34
        broker: match config.broker.as_ref() {
296
14
            None => Some(DEF_BROKER.to_string()),
297
20
            Some(broker) => Some(broker.clone()),
298
        },
299
34
        db: match config.db.as_ref() {
300
4
            None => Some(Db {
301
4
                engine: Some(DEF_ENGINE.to_string()),
302
4
                mongodb: Some(MongoDb {
303
4
                    url: Some(DEF_MONGODB_URL.to_string()),
304
4
                    database: Some(DEF_MONGODB_DB.to_string()),
305
4
                    pool_size: None,
306
4
                }),
307
4
                sqlite: Some(Sqlite {
308
4
                    path: Some(DEF_SQLITE_PATH.to_string()),
309
4
                }),
310
4
            }),
311
30
            Some(db) => Some(Db {
312
30
                engine: match db.engine.as_ref() {
313
4
                    None => Some(DEF_ENGINE.to_string()),
314
26
                    Some(engine) => match engine.as_str() {
315
26
                        DbEngine::MONGODB => Some(DbEngine::MONGODB.to_string()),
316
14
                        DbEngine::SQLITE => Some(DbEngine::SQLITE.to_string()),
317
6
                        _ => Some(DEF_ENGINE.to_string()),
318
                    },
319
                },
320
30
                mongodb: match db.mongodb.as_ref() {
321
10
                    None => Some(MongoDb {
322
10
                        url: Some(DEF_MONGODB_URL.to_string()),
323
10
                        database: Some(DEF_MONGODB_DB.to_string()),
324
10
                        pool_size: None,
325
10
                    }),
326
20
                    Some(mongodb) => Some(MongoDb {
327
20
                        url: match mongodb.url.as_ref() {
328
2
                            None => Some(DEF_MONGODB_URL.to_string()),
329
18
                            Some(url) => Some(url.to_string()),
330
                        },
331
20
                        database: match mongodb.database.as_ref() {
332
2
                            None => Some(DEF_MONGODB_DB.to_string()),
333
18
                            Some(database) => Some(database.to_string()),
334
                        },
335
20
                        pool_size: mongodb.pool_size,
336
                    }),
337
                },
338
30
                sqlite: match db.sqlite.as_ref() {
339
10
                    None => Some(Sqlite {
340
10
                        path: Some(DEF_SQLITE_PATH.to_string()),
341
10
                    }),
342
20
                    Some(sqlite) => Some(Sqlite {
343
20
                        path: match sqlite.path.as_ref() {
344
2
                            None => Some(DEF_SQLITE_PATH.to_string()),
345
18
                            Some(path) => Some(path.to_string()),
346
                        },
347
                    }),
348
                },
349
            }),
350
        },
351
34
        mq_channels: match config.mq_channels.as_ref() {
352
18
            None => Some(MqChannels {
353
18
                broker: Some(DataData {
354
18
                    url: Some(DEF_MQ_CHANNEL_URL.to_string()),
355
18
                    prefetch: Some(DEF_MQ_PREFETCH),
356
18
                    shared_prefix: Some(DEF_MQ_SHAREDPREFIX.to_string()),
357
18
                }),
358
18
                coremgr: Some(DataData {
359
18
                    url: Some(DEF_MQ_CHANNEL_URL.to_string()),
360
18
                    prefetch: Some(DEF_MQ_PREFETCH),
361
18
                    shared_prefix: Some(DEF_MQ_SHAREDPREFIX.to_string()),
362
18
                }),
363
18
            }),
364
16
            Some(mq_channels) => Some(MqChannels {
365
16
                broker: match mq_channels.broker.as_ref() {
366
2
                    None => Some(DataData {
367
2
                        url: Some(DEF_MQ_CHANNEL_URL.to_string()),
368
2
                        prefetch: Some(DEF_MQ_PREFETCH),
369
2
                        shared_prefix: Some(DEF_MQ_SHAREDPREFIX.to_string()),
370
2
                    }),
371
14
                    Some(channel) => Some(DataData {
372
14
                        url: match channel.url.as_ref() {
373
4
                            None => Some(DEF_MQ_CHANNEL_URL.to_string()),
374
10
                            Some(url) => Some(url.to_string()),
375
                        },
376
14
                        prefetch: match channel.prefetch {
377
6
                            None => Some(DEF_MQ_PREFETCH),
378
8
                            Some(prefetch) => Some(prefetch),
379
                        },
380
14
                        shared_prefix: match channel.shared_prefix.as_ref() {
381
4
                            None => Some(DEF_MQ_SHAREDPREFIX.to_string()),
382
10
                            Some(shared_prefix) => Some(shared_prefix.to_string()),
383
                        },
384
                    }),
385
                },
386
16
                coremgr: match mq_channels.coremgr.as_ref() {
387
2
                    None => Some(DataData {
388
2
                        url: Some(DEF_MQ_CHANNEL_URL.to_string()),
389
2
                        prefetch: Some(DEF_MQ_PREFETCH),
390
2
                        shared_prefix: Some(DEF_MQ_SHAREDPREFIX.to_string()),
391
2
                    }),
392
14
                    Some(channel) => Some(DataData {
393
14
                        url: match channel.url.as_ref() {
394
4
                            None => Some(DEF_MQ_CHANNEL_URL.to_string()),
395
10
                            Some(url) => Some(url.to_string()),
396
                        },
397
14
                        prefetch: match channel.prefetch {
398
6
                            None => Some(DEF_MQ_PREFETCH),
399
8
                            Some(prefetch) => Some(prefetch),
400
                        },
401
14
                        shared_prefix: match channel.shared_prefix.as_ref() {
402
4
                            None => Some(DEF_MQ_SHAREDPREFIX.to_string()),
403
10
                            Some(shared_prefix) => Some(shared_prefix.to_string()),
404
                        },
405
                    }),
406
                },
407
            }),
408
        },
409
    }
410
34
}