sylvia_iot_data/libs/
config.rs

1//! Program configurations.
2
3use std::env;
4
5use clap::{builder::RangedU64ValueParser, Arg, ArgMatches, Command};
6use serde::Deserialize;
7
8use sylvia_iot_corelib::constants::DbEngine;
9
10/// Configuration file object.
11#[derive(Default, Deserialize)]
12pub 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)]
24pub 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)]
35pub 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)]
45pub struct Sqlite {
46    /// Use absolute/relative path.
47    pub path: Option<String>,
48}
49
50/// Message channels configuration object.
51#[derive(Default, Deserialize)]
52pub 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)]
59pub 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
69pub const DEF_AUTH: &'static str = "http://localhost:1080/auth";
70pub const DEF_BROKER: &'static str = "http://localhost:2080/broker";
71pub const DEF_ENGINE: &'static str = DbEngine::SQLITE;
72pub const DEF_MONGODB_URL: &'static str = "mongodb://localhost:27017";
73pub const DEF_MONGODB_DB: &'static str = "data";
74pub const DEF_SQLITE_PATH: &'static str = "data.db";
75pub const DEF_MQ_PREFETCH: u16 = 100;
76pub const DEF_MQ_SHAREDPREFIX: &'static str = "$share/sylvia-iot-data/";
77pub const DEF_MQ_CHANNEL_URL: &'static str = "amqp://localhost";
78
79/// To register Clap arguments.
80pub fn reg_args(cmd: Command) -> Command {
81    cmd.arg(
82        Arg::new("data.auth")
83            .long("data.auth")
84            .help("sylvia-iot-auth host (ex: http://localhost:1080/auth)")
85            .num_args(1),
86    )
87    .arg(
88        Arg::new("data.broker")
89            .long("data.broker")
90            .help("sylvia-iot-broker host (ex: http://localhost:2080/broker)")
91            .num_args(1),
92    )
93    .arg(
94        Arg::new("data.db.engine")
95            .long("data.db.engine")
96            .help("database engine")
97            .num_args(1)
98            .value_parser([DbEngine::MONGODB, DbEngine::SQLITE]),
99    )
100    .arg(
101        Arg::new("data.db.mongodb.url")
102            .long("data.db.mongodb.url")
103            .help("MongoDB URL (scheme://[username][:password][@][host][:port]")
104            .num_args(1),
105    )
106    .arg(
107        Arg::new("data.db.mongodb.database")
108            .long("data.db.mongodb.database")
109            .help("database nane")
110            .num_args(1),
111    )
112    .arg(
113        Arg::new("data.db.mongodb.poolsize")
114            .long("data.db.mongodb.poolsize")
115            .help("connection pool size")
116            .num_args(1)
117            .value_parser(RangedU64ValueParser::<u64>::new().range(1..=u32::MAX as u64)),
118    )
119    .arg(
120        Arg::new("data.db.sqlite.path")
121            .long("data.db.sqlite.path")
122            .help("SQLite path")
123            .num_args(1),
124    )
125    .arg(
126        Arg::new("data.mq-channels.broker.url")
127            .long("data.mq-channels.broker.url")
128            .help("URL of `broker.data` channel")
129            .num_args(1),
130    )
131    .arg(
132        Arg::new("data.mq-channels.broker.prefetch")
133            .long("data.mq-channels.broker.prefetch")
134            .help("AMQP prefetch for `broker.data` channel")
135            .num_args(1)
136            .value_parser(RangedU64ValueParser::<u64>::new().range(1..=u16::MAX as u64)),
137    )
138    .arg(
139        Arg::new("data.mq-channels.broker.sharedprefix")
140            .long("data.mq-channels.broker.sharedprefix")
141            .help("MQTT shared subscription prefix of `broker.data` channel")
142            .num_args(1),
143    )
144    .arg(
145        Arg::new("data.mq-channels.coremgr.url")
146            .long("data.mq-channels.coremgr.url")
147            .help("URL of `coremgr.data` channel")
148            .num_args(1),
149    )
150    .arg(
151        Arg::new("data.mq-channels.coremgr.prefetch")
152            .long("data.mq-channels.coremgr.prefetch")
153            .help("AMQP prefetch for `coremgr.data` channel")
154            .num_args(1)
155            .value_parser(RangedU64ValueParser::<u64>::new().range(1..=u16::MAX as u64)),
156    )
157    .arg(
158        Arg::new("data.mq-channels.coremgr.sharedprefix")
159            .long("data.mq-channels.coremgr.sharedprefix")
160            .help("MQTT shared subscription prefix of `coremgr.data` channel")
161            .num_args(1),
162    )
163}
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.
169pub fn read_args(args: &ArgMatches) -> Config {
170    apply_default(&Config {
171        auth: match args.get_one::<String>("data.auth") {
172            None => match env::var("DATA_AUTH") {
173                Err(_) => None,
174                Ok(v) => Some(v),
175            },
176            Some(v) => Some(v.clone()),
177        },
178        broker: match args.get_one::<String>("data.broker") {
179            None => match env::var("DATA_BROKER") {
180                Err(_) => None,
181                Ok(v) => Some(v),
182            },
183            Some(v) => Some(v.clone()),
184        },
185        db: Some(Db {
186            engine: match args.get_one::<String>("data.db.engine") {
187                None => match env::var("DATA_DB_ENGINE") {
188                    Err(_) => None,
189                    Ok(v) => Some(v),
190                },
191                Some(v) => Some(v.clone()),
192            },
193            mongodb: Some(MongoDb {
194                url: match args.get_one::<String>("data.db.mongodb.url") {
195                    None => match env::var("DATA_DB_MONGODB_URL") {
196                        Err(_) => None,
197                        Ok(v) => Some(v),
198                    },
199                    Some(v) => Some(v.clone()),
200                },
201                database: match args.get_one::<String>("data.db.mongodb.database") {
202                    None => match env::var("DATA_DB_MONGODB_DATABASE") {
203                        Err(_) => None,
204                        Ok(v) => Some(v),
205                    },
206                    Some(v) => Some(v.clone()),
207                },
208                pool_size: match args.get_one::<u64>("data.db.mongodb.poolsize") {
209                    None => match env::var("DATA_DB_MONGODB_POOLSIZE") {
210                        Err(_) => None,
211                        Ok(v) => match v.parse::<u32>() {
212                            Err(_) => None,
213                            Ok(v) => Some(v),
214                        },
215                    },
216                    Some(v) => Some(*v as u32),
217                },
218            }),
219            sqlite: Some(Sqlite {
220                path: match args.get_one::<String>("data.db.sqlite.path") {
221                    None => match env::var("DATA_DB_SQLITE_PATH") {
222                        Err(_) => None,
223                        Ok(v) => Some(v),
224                    },
225                    Some(v) => Some(v.clone()),
226                },
227            }),
228        }),
229        mq_channels: Some(MqChannels {
230            broker: Some(DataData {
231                url: match args.get_one::<String>("data.mq-channels.broker.url") {
232                    None => match env::var("DATA_MQCHANNELS_BROKER_URL") {
233                        Err(_) => None,
234                        Ok(v) => Some(v),
235                    },
236                    Some(v) => Some(v.clone()),
237                },
238                prefetch: match args.get_one::<u64>("data.mq-channels.broker.prefetch") {
239                    None => match env::var("DATA_MQCHANNELS_BROKER_PREFETCH") {
240                        Err(_) => None,
241                        Ok(v) => match v.parse::<u16>() {
242                            Err(_) => None,
243                            Ok(v) => Some(v),
244                        },
245                    },
246                    Some(v) => Some(*v as u16),
247                },
248                shared_prefix: match args.get_one::<String>("data.mq-channels.broker.sharedprefix")
249                {
250                    None => match env::var("DATA_MQCHANNELS_BROKER_SHAREDPREFIX") {
251                        Err(_) => None,
252                        Ok(v) => Some(v),
253                    },
254                    Some(v) => Some(v.clone()),
255                },
256            }),
257            coremgr: Some(DataData {
258                url: match args.get_one::<String>("data.mq-channels.coremgr.url") {
259                    None => match env::var("DATA_MQCHANNELS_COREMGR_URL") {
260                        Err(_) => None,
261                        Ok(v) => Some(v),
262                    },
263                    Some(v) => Some(v.clone()),
264                },
265                prefetch: match args.get_one::<u64>("data.mq-channels.coremgr.prefetch") {
266                    None => match env::var("DATA_MQCHANNELS_COREMGR_PREFETCH") {
267                        Err(_) => None,
268                        Ok(v) => match v.parse::<u16>() {
269                            Err(_) => None,
270                            Ok(v) => Some(v),
271                        },
272                    },
273                    Some(v) => Some(*v as u16),
274                },
275                shared_prefix: match args.get_one::<String>("data.mq-channels.coremgr.sharedprefix")
276                {
277                    None => match env::var("DATA_MQCHANNELS_COREMGR_SHAREDPREFIX") {
278                        Err(_) => None,
279                        Ok(v) => Some(v),
280                    },
281                    Some(v) => Some(v.clone()),
282                },
283            }),
284        }),
285    })
286}
287
288/// Fill missing configuration with default values.
289pub fn apply_default(config: &Config) -> Config {
290    Config {
291        auth: match config.auth.as_ref() {
292            None => Some(DEF_AUTH.to_string()),
293            Some(auth) => Some(auth.clone()),
294        },
295        broker: match config.broker.as_ref() {
296            None => Some(DEF_BROKER.to_string()),
297            Some(broker) => Some(broker.clone()),
298        },
299        db: match config.db.as_ref() {
300            None => Some(Db {
301                engine: Some(DEF_ENGINE.to_string()),
302                mongodb: Some(MongoDb {
303                    url: Some(DEF_MONGODB_URL.to_string()),
304                    database: Some(DEF_MONGODB_DB.to_string()),
305                    pool_size: None,
306                }),
307                sqlite: Some(Sqlite {
308                    path: Some(DEF_SQLITE_PATH.to_string()),
309                }),
310            }),
311            Some(db) => Some(Db {
312                engine: match db.engine.as_ref() {
313                    None => Some(DEF_ENGINE.to_string()),
314                    Some(engine) => match engine.as_str() {
315                        DbEngine::MONGODB => Some(DbEngine::MONGODB.to_string()),
316                        DbEngine::SQLITE => Some(DbEngine::SQLITE.to_string()),
317                        _ => Some(DEF_ENGINE.to_string()),
318                    },
319                },
320                mongodb: match db.mongodb.as_ref() {
321                    None => Some(MongoDb {
322                        url: Some(DEF_MONGODB_URL.to_string()),
323                        database: Some(DEF_MONGODB_DB.to_string()),
324                        pool_size: None,
325                    }),
326                    Some(mongodb) => Some(MongoDb {
327                        url: match mongodb.url.as_ref() {
328                            None => Some(DEF_MONGODB_URL.to_string()),
329                            Some(url) => Some(url.to_string()),
330                        },
331                        database: match mongodb.database.as_ref() {
332                            None => Some(DEF_MONGODB_DB.to_string()),
333                            Some(database) => Some(database.to_string()),
334                        },
335                        pool_size: mongodb.pool_size,
336                    }),
337                },
338                sqlite: match db.sqlite.as_ref() {
339                    None => Some(Sqlite {
340                        path: Some(DEF_SQLITE_PATH.to_string()),
341                    }),
342                    Some(sqlite) => Some(Sqlite {
343                        path: match sqlite.path.as_ref() {
344                            None => Some(DEF_SQLITE_PATH.to_string()),
345                            Some(path) => Some(path.to_string()),
346                        },
347                    }),
348                },
349            }),
350        },
351        mq_channels: match config.mq_channels.as_ref() {
352            None => Some(MqChannels {
353                broker: Some(DataData {
354                    url: Some(DEF_MQ_CHANNEL_URL.to_string()),
355                    prefetch: Some(DEF_MQ_PREFETCH),
356                    shared_prefix: Some(DEF_MQ_SHAREDPREFIX.to_string()),
357                }),
358                coremgr: Some(DataData {
359                    url: Some(DEF_MQ_CHANNEL_URL.to_string()),
360                    prefetch: Some(DEF_MQ_PREFETCH),
361                    shared_prefix: Some(DEF_MQ_SHAREDPREFIX.to_string()),
362                }),
363            }),
364            Some(mq_channels) => Some(MqChannels {
365                broker: match mq_channels.broker.as_ref() {
366                    None => Some(DataData {
367                        url: Some(DEF_MQ_CHANNEL_URL.to_string()),
368                        prefetch: Some(DEF_MQ_PREFETCH),
369                        shared_prefix: Some(DEF_MQ_SHAREDPREFIX.to_string()),
370                    }),
371                    Some(channel) => Some(DataData {
372                        url: match channel.url.as_ref() {
373                            None => Some(DEF_MQ_CHANNEL_URL.to_string()),
374                            Some(url) => Some(url.to_string()),
375                        },
376                        prefetch: match channel.prefetch {
377                            None => Some(DEF_MQ_PREFETCH),
378                            Some(prefetch) => Some(prefetch),
379                        },
380                        shared_prefix: match channel.shared_prefix.as_ref() {
381                            None => Some(DEF_MQ_SHAREDPREFIX.to_string()),
382                            Some(shared_prefix) => Some(shared_prefix.to_string()),
383                        },
384                    }),
385                },
386                coremgr: match mq_channels.coremgr.as_ref() {
387                    None => Some(DataData {
388                        url: Some(DEF_MQ_CHANNEL_URL.to_string()),
389                        prefetch: Some(DEF_MQ_PREFETCH),
390                        shared_prefix: Some(DEF_MQ_SHAREDPREFIX.to_string()),
391                    }),
392                    Some(channel) => Some(DataData {
393                        url: match channel.url.as_ref() {
394                            None => Some(DEF_MQ_CHANNEL_URL.to_string()),
395                            Some(url) => Some(url.to_string()),
396                        },
397                        prefetch: match channel.prefetch {
398                            None => Some(DEF_MQ_PREFETCH),
399                            Some(prefetch) => Some(prefetch),
400                        },
401                        shared_prefix: match channel.shared_prefix.as_ref() {
402                            None => Some(DEF_MQ_SHAREDPREFIX.to_string()),
403                            Some(shared_prefix) => Some(shared_prefix.to_string()),
404                        },
405                    }),
406                },
407            }),
408        },
409    }
410}