1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4use utoipa::ToSchema;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
8#[serde(rename_all = "lowercase")]
9pub enum ConfigSource {
10 Default,
12 Toml,
14 Env,
16 Cli,
18}
19
20const SETTING_KEYS: &[&str] = &[
22 "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.ip",
40 "local.port",
41 "origin.hostname",
43 "origin.port",
44 "origin.protocol",
45 "origin.path",
46 "log.level",
48 "log.format",
49 "log.level_web_server",
50 "docs.enabled",
52 "docs.max_size",
53 "proxy.enabled",
55 "proxy.num_threads",
56 "proxy.download_on_update",
57 "proxy.url",
58 "proxy.index",
59 "postgresql.enabled",
61 "postgresql.address",
62 "postgresql.port",
63 "postgresql.db",
64 "postgresql.user",
65 "postgresql.pwd",
66 "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.admin_pwd",
78 "setup.admin_token",
79 "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.enabled",
93 "toolchain.max_size",
94];
95
96pub type SourceMap = HashMap<String, ConfigSource>;
98
99fn env_var_name(key: &str) -> String {
102 format!("KELLNR_{}", key.to_uppercase().replace('.', "__"))
103}
104
105pub fn init_default_sources() -> SourceMap {
107 SETTING_KEYS
108 .iter()
109 .map(|k| (k.to_string(), ConfigSource::Default))
110 .collect()
111}
112
113pub 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); 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}