Skip to main content

kellnr_settings/
config_source.rs

1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4use utoipa::ToSchema;
5
6/// Represents the source of a configuration value.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
8#[serde(rename_all = "lowercase")]
9pub enum ConfigSource {
10    /// Value comes from compiled-in defaults
11    Default,
12    /// Value was set in a TOML configuration file
13    Toml,
14    /// Value was set via environment variable
15    Env,
16    /// Value was set via command-line argument
17    Cli,
18}
19
20/// All setting keys used for configuration tracking.
21const SETTING_KEYS: &[&str] = &[
22    // Registry settings
23    "registry.data_dir",
24    "registry.session_age_seconds",
25    "registry.cache_size",
26    "registry.max_crate_size",
27    "registry.max_db_connections",
28    "registry.auth_required",
29    "registry.required_crate_fields",
30    "registry.new_crates_restricted",
31    "registry.cookie_signing_key",
32    "registry.allow_ownerless_crates",
33    "registry.token_cache_enabled",
34    "registry.token_cache_ttl_seconds",
35    "registry.token_cache_max_capacity",
36    "registry.token_db_retry_count",
37    "registry.token_db_retry_delay_ms",
38    // Local settings
39    "local.ip",
40    "local.port",
41    // Origin settings
42    "origin.hostname",
43    "origin.port",
44    "origin.protocol",
45    "origin.path",
46    // Log settings
47    "log.level",
48    "log.format",
49    "log.level_web_server",
50    // Docs settings
51    "docs.enabled",
52    "docs.max_size",
53    // Proxy settings
54    "proxy.enabled",
55    "proxy.num_threads",
56    "proxy.download_on_update",
57    "proxy.url",
58    "proxy.index",
59    // PostgreSQL settings
60    "postgresql.enabled",
61    "postgresql.address",
62    "postgresql.port",
63    "postgresql.db",
64    "postgresql.user",
65    "postgresql.pwd",
66    // S3 settings
67    "s3.enabled",
68    "s3.access_key",
69    "s3.secret_key",
70    "s3.region",
71    "s3.endpoint",
72    "s3.allow_http",
73    "s3.crates_bucket",
74    "s3.cratesio_bucket",
75    "s3.toolchain_bucket",
76    // Setup settings
77    "setup.admin_pwd",
78    "setup.admin_token",
79    // OAuth2 settings
80    "oauth2.enabled",
81    "oauth2.issuer_url",
82    "oauth2.client_id",
83    "oauth2.client_secret",
84    "oauth2.scopes",
85    "oauth2.auto_provision_users",
86    "oauth2.admin_group_claim",
87    "oauth2.admin_group_value",
88    "oauth2.read_only_group_claim",
89    "oauth2.read_only_group_value",
90    "oauth2.button_text",
91    // Toolchain settings
92    "toolchain.enabled",
93    "toolchain.max_size",
94];
95
96/// Maps setting keys (e.g., `"registry.data_dir"`) to their configuration source.
97pub type SourceMap = HashMap<String, ConfigSource>;
98
99/// Compute the environment variable name for a given setting key.
100/// E.g., `"registry.data_dir"` becomes `"KELLNR_REGISTRY__DATA_DIR"`.
101fn env_var_name(key: &str) -> String {
102    format!("KELLNR_{}", key.to_uppercase().replace('.', "__"))
103}
104
105/// Initialize a `SourceMap` with all settings set to Default.
106pub fn init_default_sources() -> SourceMap {
107    SETTING_KEYS
108        .iter()
109        .map(|k| (k.to_string(), ConfigSource::Default))
110        .collect()
111}
112
113/// Check which environment variables are set and mark them in the source map.
114pub fn track_env_sources(sources: &mut SourceMap) {
115    for key in SETTING_KEYS {
116        if std::env::var(env_var_name(key)).is_ok() {
117            sources.insert((*key).to_string(), ConfigSource::Env);
118        }
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn test_init_default_sources() {
128        let sources = init_default_sources();
129        assert!(sources.len() > 50); // We have many settings
130        assert_eq!(
131            sources.get("registry.data_dir"),
132            Some(&ConfigSource::Default)
133        );
134        assert_eq!(sources.get("local.port"), Some(&ConfigSource::Default));
135    }
136
137    #[test]
138    fn test_setting_keys_count() {
139        assert!(SETTING_KEYS.len() > 50);
140    }
141
142    #[test]
143    fn test_env_var_name() {
144        assert_eq!(
145            env_var_name("registry.data_dir"),
146            "KELLNR_REGISTRY__DATA_DIR"
147        );
148        assert_eq!(env_var_name("local.port"), "KELLNR_LOCAL__PORT");
149        assert_eq!(
150            env_var_name("oauth2.auto_provision_users"),
151            "KELLNR_OAUTH2__AUTO_PROVISION_USERS"
152        );
153    }
154
155    #[test]
156    fn test_config_source_serialization() {
157        assert_eq!(
158            serde_json::to_string(&ConfigSource::Default).unwrap(),
159            "\"default\""
160        );
161        assert_eq!(
162            serde_json::to_string(&ConfigSource::Toml).unwrap(),
163            "\"toml\""
164        );
165        assert_eq!(
166            serde_json::to_string(&ConfigSource::Env).unwrap(),
167            "\"env\""
168        );
169        assert_eq!(
170            serde_json::to_string(&ConfigSource::Cli).unwrap(),
171            "\"cli\""
172        );
173    }
174}