• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

qubit-ltd / rust-config / 84ab02af-da5f-49b6-aa4e-20ed6b5f3fd3

09 Apr 2026 04:48PM UTC coverage: 97.136% (+0.01%) from 97.122%
84ab02af-da5f-49b6-aa4e-20ed6b5f3fd3

push

circleci

Haixing-Hu
chore: bump version to 0.7.0

1221 of 1257 relevant lines covered (97.14%)

45.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.33
/src/source/env_config_source.rs
1
/*******************************************************************************
2
 *
3
 *    Copyright (c) 2025 - 2026.
4
 *    Haixing Hu, Qubit Co. Ltd.
5
 *
6
 *    All rights reserved.
7
 *
8
 ******************************************************************************/
9
//! # System Environment Variable Configuration Source
10
//!
11
//! Loads configuration from the current process's environment variables.
12
//!
13
//! # Key Transformation
14
//!
15
//! When a prefix is set, only variables matching the prefix are loaded, and
16
//! the prefix is stripped from the key name. The key is then lowercased and
17
//! underscores are converted to dots to produce the config key.
18
//!
19
//! For example, with prefix `APP_`:
20
//! - `APP_SERVER_HOST=localhost` → `server.host = "localhost"`
21
//! - `APP_SERVER_PORT=8080` → `server.port = "8080"`
22
//!
23
//! Without a prefix, all environment variables are loaded as-is.
24
//!
25
//! # Author
26
//!
27
//! Haixing Hu
28

29
use crate::{Config, ConfigResult};
30

31
use super::ConfigSource;
32

33
/// Configuration source that loads from system environment variables
34
///
35
/// # Examples
36
///
37
/// ```rust,ignore
38
/// use qubit_config::source::{EnvConfigSource, ConfigSource};
39
/// use qubit_config::Config;
40
///
41
/// // Load all env vars
42
/// let source = EnvConfigSource::new();
43
///
44
/// // Load only vars with prefix "APP_", strip prefix and normalize key
45
/// let source = EnvConfigSource::with_prefix("APP_");
46
///
47
/// let mut config = Config::new();
48
/// source.load(&mut config).unwrap();
49
/// ```
50
///
51
/// # Author
52
///
53
/// Haixing Hu
54
#[derive(Debug, Clone)]
55
pub struct EnvConfigSource {
56
    /// Optional prefix filter; only variables with this prefix are loaded
57
    prefix: Option<String>,
58
    /// Whether to strip the prefix from the key
59
    strip_prefix: bool,
60
    /// Whether to convert underscores to dots in the key
61
    convert_underscores: bool,
62
    /// Whether to lowercase the key
63
    lowercase_keys: bool,
64
}
65

66
impl EnvConfigSource {
67
    /// Creates a new `EnvConfigSource` that loads all environment variables.
68
    ///
69
    /// Keys are loaded as-is (no prefix filtering, no transformation).
70
    ///
71
    /// # Returns
72
    ///
73
    /// A source that ingests every `std::env::vars()` entry.
74
    pub fn new() -> Self {
2✔
75
        Self {
2✔
76
            prefix: None,
2✔
77
            strip_prefix: false,
2✔
78
            convert_underscores: false,
2✔
79
            lowercase_keys: false,
2✔
80
        }
2✔
81
    }
2✔
82

83
    /// Creates a new `EnvConfigSource` that filters by prefix and normalizes
84
    /// keys.
85
    ///
86
    /// Only variables with the given prefix are loaded. The prefix is stripped,
87
    /// the key is lowercased, and underscores are converted to dots.
88
    ///
89
    /// # Parameters
90
    ///
91
    /// * `prefix` - The prefix to filter by (e.g., `"APP_"`)
92
    ///
93
    /// # Returns
94
    ///
95
    /// A source with prefix filtering and key normalization enabled.
96
    pub fn with_prefix(prefix: &str) -> Self {
6✔
97
        Self {
6✔
98
            prefix: Some(prefix.to_string()),
6✔
99
            strip_prefix: true,
6✔
100
            convert_underscores: true,
6✔
101
            lowercase_keys: true,
6✔
102
        }
6✔
103
    }
6✔
104

105
    /// Creates a new `EnvConfigSource` with a custom prefix and explicit
106
    /// options.
107
    ///
108
    /// # Parameters
109
    ///
110
    /// * `prefix` - The prefix to filter by
111
    /// * `strip_prefix` - Whether to strip the prefix from the key
112
    /// * `convert_underscores` - Whether to convert underscores to dots
113
    /// * `lowercase_keys` - Whether to lowercase the key
114
    ///
115
    /// # Returns
116
    ///
117
    /// A configured [`EnvConfigSource`].
118
    pub fn with_options(
2✔
119
        prefix: &str,
2✔
120
        strip_prefix: bool,
2✔
121
        convert_underscores: bool,
2✔
122
        lowercase_keys: bool,
2✔
123
    ) -> Self {
2✔
124
        Self {
2✔
125
            prefix: Some(prefix.to_string()),
2✔
126
            strip_prefix,
2✔
127
            convert_underscores,
2✔
128
            lowercase_keys,
2✔
129
        }
2✔
130
    }
2✔
131

132
    /// Transforms an environment variable key according to the source's
133
    /// settings.
134
    ///
135
    /// # Parameters
136
    ///
137
    /// * `key` - Original environment variable name.
138
    ///
139
    /// # Returns
140
    ///
141
    /// The key after optional prefix strip, lowercasing, and underscore
142
    /// replacement.
143
    fn transform_key(&self, key: &str) -> String {
145✔
144
        let mut result = key.to_string();
145✔
145

146
        if self.strip_prefix {
145✔
147
            if let Some(prefix) = &self.prefix {
7✔
148
                if result.starts_with(prefix.as_str()) {
7✔
149
                    result = result[prefix.len()..].to_string();
7✔
150
                }
7✔
151
            }
×
152
        }
138✔
153

154
        if self.lowercase_keys {
145✔
155
            result = result.to_lowercase();
7✔
156
        }
138✔
157

158
        if self.convert_underscores {
145✔
159
            result = result.replace('_', ".");
7✔
160
        }
138✔
161

162
        result
145✔
163
    }
145✔
164
}
165

166
impl Default for EnvConfigSource {
167
    fn default() -> Self {
1✔
168
        Self::new()
1✔
169
    }
1✔
170
}
171

172
impl ConfigSource for EnvConfigSource {
173
    fn load(&self, config: &mut Config) -> ConfigResult<()> {
10✔
174
        for (key, value) in std::env::vars() {
690✔
175
            // Filter by prefix if set
176
            if let Some(prefix) = &self.prefix {
690✔
177
                if !key.starts_with(prefix.as_str()) {
554✔
178
                    continue;
545✔
179
                }
9✔
180
            }
136✔
181

182
            let transformed_key = self.transform_key(&key);
145✔
183
            config.set(&transformed_key, value)?;
145✔
184
        }
185

186
        Ok(())
10✔
187
    }
10✔
188
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc