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
5
pub fn reg_args(cmd: Command) -> Command {
81
5
    cmd.arg(
82
5
        Arg::new("data.auth")
83
5
            .long("data.auth")
84
5
            .help("sylvia-iot-auth host (ex: http://localhost:1080/auth)")
85
5
            .num_args(1),
86
5
    )
87
5
    .arg(
88
5
        Arg::new("data.broker")
89
5
            .long("data.broker")
90
5
            .help("sylvia-iot-broker host (ex: http://localhost:2080/broker)")
91
5
            .num_args(1),
92
5
    )
93
5
    .arg(
94
5
        Arg::new("data.db.engine")
95
5
            .long("data.db.engine")
96
5
            .help("database engine")
97
5
            .num_args(1)
98
5
            .value_parser([DbEngine::MONGODB, DbEngine::SQLITE]),
99
5
    )
100
5
    .arg(
101
5
        Arg::new("data.db.mongodb.url")
102
5
            .long("data.db.mongodb.url")
103
5
            .help("MongoDB URL (scheme://[username][:password][@][host][:port]")
104
5
            .num_args(1),
105
5
    )
106
5
    .arg(
107
5
        Arg::new("data.db.mongodb.database")
108
5
            .long("data.db.mongodb.database")
109
5
            .help("database nane")
110
5
            .num_args(1),
111
5
    )
112
5
    .arg(
113
5
        Arg::new("data.db.mongodb.poolsize")
114
5
            .long("data.db.mongodb.poolsize")
115
5
            .help("connection pool size")
116
5
            .num_args(1)
117
5
            .value_parser(RangedU64ValueParser::<u64>::new().range(1..=u32::MAX as u64)),
118
5
    )
119
5
    .arg(
120
5
        Arg::new("data.db.sqlite.path")
121
5
            .long("data.db.sqlite.path")
122
5
            .help("SQLite path")
123
5
            .num_args(1),
124
5
    )
125
5
    .arg(
126
5
        Arg::new("data.mq-channels.broker.url")
127
5
            .long("data.mq-channels.broker.url")
128
5
            .help("URL of `broker.data` channel")
129
5
            .num_args(1),
130
5
    )
131
5
    .arg(
132
5
        Arg::new("data.mq-channels.broker.prefetch")
133
5
            .long("data.mq-channels.broker.prefetch")
134
5
            .help("AMQP prefetch for `broker.data` channel")
135
5
            .num_args(1)
136
5
            .value_parser(RangedU64ValueParser::<u64>::new().range(1..=u16::MAX as u64)),
137
5
    )
138
5
    .arg(
139
5
        Arg::new("data.mq-channels.broker.sharedprefix")
140
5
            .long("data.mq-channels.broker.sharedprefix")
141
5
            .help("MQTT shared subscription prefix of `broker.data` channel")
142
5
            .num_args(1),
143
5
    )
144
5
    .arg(
145
5
        Arg::new("data.mq-channels.coremgr.url")
146
5
            .long("data.mq-channels.coremgr.url")
147
5
            .help("URL of `coremgr.data` channel")
148
5
            .num_args(1),
149
5
    )
150
5
    .arg(
151
5
        Arg::new("data.mq-channels.coremgr.prefetch")
152
5
            .long("data.mq-channels.coremgr.prefetch")
153
5
            .help("AMQP prefetch for `coremgr.data` channel")
154
5
            .num_args(1)
155
5
            .value_parser(RangedU64ValueParser::<u64>::new().range(1..=u16::MAX as u64)),
156
5
    )
157
5
    .arg(
158
5
        Arg::new("data.mq-channels.coremgr.sharedprefix")
159
5
            .long("data.mq-channels.coremgr.sharedprefix")
160
5
            .help("MQTT shared subscription prefix of `coremgr.data` channel")
161
5
            .num_args(1),
162
5
    )
163
5
}
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
5
pub fn read_args(args: &ArgMatches) -> Config {
170
5
    apply_default(&Config {
171
5
        auth: match args.get_one::<String>("data.auth") {
172
3
            None => match env::var("DATA_AUTH") {
173
1
                Err(_) => None,
174
2
                Ok(v) => Some(v),
175
            },
176
2
            Some(v) => Some(v.clone()),
177
        },
178
5
        broker: match args.get_one::<String>("data.broker") {
179
3
            None => match env::var("DATA_BROKER") {
180
1
                Err(_) => None,
181
2
                Ok(v) => Some(v),
182
            },
183
2
            Some(v) => Some(v.clone()),
184
        },
185
        db: Some(Db {
186
5
            engine: match args.get_one::<String>("data.db.engine") {
187
3
                None => match env::var("DATA_DB_ENGINE") {
188
1
                    Err(_) => None,
189
2
                    Ok(v) => Some(v),
190
                },
191
2
                Some(v) => Some(v.clone()),
192
            },
193
            mongodb: Some(MongoDb {
194
5
                url: match args.get_one::<String>("data.db.mongodb.url") {
195
3
                    None => match env::var("DATA_DB_MONGODB_URL") {
196
1
                        Err(_) => None,
197
2
                        Ok(v) => Some(v),
198
                    },
199
2
                    Some(v) => Some(v.clone()),
200
                },
201
5
                database: match args.get_one::<String>("data.db.mongodb.database") {
202
3
                    None => match env::var("DATA_DB_MONGODB_DATABASE") {
203
1
                        Err(_) => None,
204
2
                        Ok(v) => Some(v),
205
                    },
206
2
                    Some(v) => Some(v.clone()),
207
                },
208
5
                pool_size: match args.get_one::<u64>("data.db.mongodb.poolsize") {
209
3
                    None => match env::var("DATA_DB_MONGODB_POOLSIZE") {
210
1
                        Err(_) => None,
211
2
                        Ok(v) => match v.parse::<u32>() {
212
1
                            Err(_) => None,
213
1
                            Ok(v) => Some(v),
214
                        },
215
                    },
216
2
                    Some(v) => Some(*v as u32),
217
                },
218
            }),
219
            sqlite: Some(Sqlite {
220
5
                path: match args.get_one::<String>("data.db.sqlite.path") {
221
3
                    None => match env::var("DATA_DB_SQLITE_PATH") {
222
1
                        Err(_) => None,
223
2
                        Ok(v) => Some(v),
224
                    },
225
2
                    Some(v) => Some(v.clone()),
226
                },
227
            }),
228
        }),
229
        mq_channels: Some(MqChannels {
230
            broker: Some(DataData {
231
5
                url: match args.get_one::<String>("data.mq-channels.broker.url") {
232
3
                    None => match env::var("DATA_MQCHANNELS_BROKER_URL") {
233
1
                        Err(_) => None,
234
2
                        Ok(v) => Some(v),
235
                    },
236
2
                    Some(v) => Some(v.clone()),
237
                },
238
5
                prefetch: match args.get_one::<u64>("data.mq-channels.broker.prefetch") {
239
3
                    None => match env::var("DATA_MQCHANNELS_BROKER_PREFETCH") {
240
1
                        Err(_) => None,
241
2
                        Ok(v) => match v.parse::<u16>() {
242
1
                            Err(_) => None,
243
1
                            Ok(v) => Some(v),
244
                        },
245
                    },
246
2
                    Some(v) => Some(*v as u16),
247
                },
248
5
                shared_prefix: match args.get_one::<String>("data.mq-channels.broker.sharedprefix")
249
                {
250
3
                    None => match env::var("DATA_MQCHANNELS_BROKER_SHAREDPREFIX") {
251
1
                        Err(_) => None,
252
2
                        Ok(v) => Some(v),
253
                    },
254
2
                    Some(v) => Some(v.clone()),
255
                },
256
            }),
257
            coremgr: Some(DataData {
258
5
                url: match args.get_one::<String>("data.mq-channels.coremgr.url") {
259
3
                    None => match env::var("DATA_MQCHANNELS_COREMGR_URL") {
260
1
                        Err(_) => None,
261
2
                        Ok(v) => Some(v),
262
                    },
263
2
                    Some(v) => Some(v.clone()),
264
                },
265
5
                prefetch: match args.get_one::<u64>("data.mq-channels.coremgr.prefetch") {
266
3
                    None => match env::var("DATA_MQCHANNELS_COREMGR_PREFETCH") {
267
1
                        Err(_) => None,
268
2
                        Ok(v) => match v.parse::<u16>() {
269
1
                            Err(_) => None,
270
1
                            Ok(v) => Some(v),
271
                        },
272
                    },
273
2
                    Some(v) => Some(*v as u16),
274
                },
275
5
                shared_prefix: match args.get_one::<String>("data.mq-channels.coremgr.sharedprefix")
276
                {
277
3
                    None => match env::var("DATA_MQCHANNELS_COREMGR_SHAREDPREFIX") {
278
1
                        Err(_) => None,
279
2
                        Ok(v) => Some(v),
280
                    },
281
2
                    Some(v) => Some(v.clone()),
282
                },
283
            }),
284
        }),
285
    })
286
5
}
287

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