strut_config/
assembler.rs

1use crate::Scanner;
2use config::builder::{AsyncState, DefaultState};
3use config::{ConfigBuilder, Environment};
4
5/// A small facade for assembling the opinionated version of [`ConfigBuilder`].
6pub struct Assembler;
7
8/// A simple preference collection accepted by the [`Assembler`] facade.
9pub struct AssemblerChoices {
10    /// The dir name/path passed to the [`Scanner::find_config_files`] method.
11    pub dir_name: Option<String>,
12    /// Whether to add the [`Environment`] source to the [`ConfigBuilder`].
13    pub env_enabled: bool,
14    /// If given, defines the prefix to pass to the [`Environment::prefix`]
15    /// method.
16    pub env_prefix: Option<String>,
17    /// If given, defines which separator to pass to the [`Environment::prefix`]
18    /// method.
19    pub env_separator: Option<String>,
20}
21
22impl Default for AssemblerChoices {
23    fn default() -> Self {
24        Self {
25            dir_name: Some("config".to_string()),
26            env_enabled: true,
27            env_prefix: Some("APP".to_string()),
28            env_separator: Some("_".to_string()),
29        }
30    }
31}
32
33macro_rules! bootstrap_builder {
34    ($builder:expr, $choices:expr) => {{
35        let mut builder = $builder;
36
37        // Find and add all config files as sources
38        for config_file in Scanner::find_config_files($choices.dir_name.as_deref()) {
39            builder = builder.add_source(config::File::from(config_file));
40        }
41
42        // Conditionally add an environment-based source
43        if $choices.env_enabled {
44            // Create the base source
45            let mut env_source = Environment::default();
46
47            // Set the prefix
48            if let Some(prefix) = $choices.env_prefix.as_deref() {
49                env_source = env_source.prefix(prefix);
50            }
51
52            // Set the separator
53            if let Some(separator) = $choices.env_separator.as_deref() {
54                env_source = env_source.separator(separator);
55            }
56
57            // Add to the builder
58            builder = builder.add_source(env_source);
59        }
60
61        builder
62    }};
63}
64
65impl Assembler {
66    /// Creates and returns the opinionated [`ConfigBuilder`] in [`DefaultState`].
67    ///
68    /// By default, the `choices` is `config`, but a custom name may be
69    /// given.
70    pub fn make_sync_builder(choices: &AssemblerChoices) -> ConfigBuilder<DefaultState> {
71        bootstrap_builder!(ConfigBuilder::<DefaultState>::default(), choices)
72    }
73
74    /// Creates and returns the opinionated [`ConfigBuilder`] in [`AsyncState`].
75    ///
76    /// By default, the `choices` is `config`, but a custom name may be
77    /// given.
78    pub fn make_async_builder(choices: &AssemblerChoices) -> ConfigBuilder<AsyncState> {
79        bootstrap_builder!(ConfigBuilder::<AsyncState>::default(), choices)
80    }
81}