kellnr_settings/
settings.rs1use std::path::{Path, PathBuf};
2
3pub use provcfg::Error as SettingsError;
8use provcfg::{ClapArgs, Configurable, Provenance};
9use serde::{Deserialize, Serialize};
10
11use crate::config_source::SourceMap;
12use crate::docs::{Docs, DocsArgs, DocsPartial, DocsProv};
13use crate::gcs::{Gcs, GcsArgs, GcsPartial, GcsProv};
14use crate::local::{Local, LocalArgs, LocalPartial, LocalProv};
15use crate::log::{Log, LogArgs, LogPartial, LogProv};
16use crate::oauth2::{OAuth2, OAuth2Args, OAuth2Partial, OAuth2Prov};
17use crate::origin::{Origin, OriginArgs, OriginPartial, OriginProv};
18use crate::postgresql::{Postgresql, PostgresqlArgs, PostgresqlPartial, PostgresqlProv};
19use crate::proxy::{Proxy, ProxyArgs, ProxyPartial, ProxyProv};
20use crate::registry::{Registry, RegistryArgs, RegistryPartial, RegistryProv};
21use crate::s3::{S3, S3Args, S3Partial, S3Prov};
22use crate::setup::{Setup, SetupArgs, SetupPartial, SetupProv};
23use crate::toolchain::{Toolchain, ToolchainArgs, ToolchainPartial, ToolchainProv};
24
25#[derive(Debug, Default, Deserialize, Serialize, Eq, PartialEq, Clone, Configurable, ClapArgs)]
30#[serde(default)]
31pub struct Settings {
32 #[configurable(nested)]
33 pub setup: Setup,
34 #[configurable(nested)]
35 pub registry: Registry,
36 #[configurable(nested)]
37 pub docs: Docs,
38 #[configurable(nested)]
39 pub proxy: Proxy,
40 #[configurable(nested)]
41 pub log: Log,
42 #[configurable(nested)]
43 pub local: Local,
44 #[configurable(nested)]
45 pub origin: Origin,
46 #[configurable(nested)]
47 pub postgresql: Postgresql,
48 #[configurable(nested)]
49 pub s3: S3,
50 #[configurable(nested)]
51 pub gcs: Gcs,
52 #[configurable(nested)]
53 pub oauth2: OAuth2,
54 #[configurable(nested)]
55 pub toolchain: Toolchain,
56}
57
58pub fn build_prov_with_cli(
67 config_file: Option<&Path>,
68 cli_partial: Option<SettingsPartial>,
69) -> Result<SettingsProv, SettingsError> {
70 let mut cfg = provcfg::Config::new();
71 if let Some(path) = config_file {
72 cfg = cfg.add_toml_file(path)?;
73 }
74 cfg = cfg.add_env_with_list_keys("KELLNR", env_list_keys().iter().copied());
75 if let Some(partial) = cli_partial {
76 cfg = cfg.add_cli(partial);
77 }
78 cfg.build::<SettingsProv>()
79}
80
81pub(crate) fn env_list_keys() -> &'static [&'static str] {
85 &["registry.required_crate_fields", "oauth2.scopes"]
86}
87
88pub fn sources_from_prov(prov: &SettingsProv) -> SourceMap {
91 prov.sources_map()
92 .into_iter()
93 .map(|(key, category)| (key, category.into()))
94 .collect()
95}
96
97impl Settings {
98 pub fn bin_path(&self) -> PathBuf {
99 PathBuf::from(&self.registry.data_dir).join("crates")
100 }
101
102 pub fn doc_queue_path(&self) -> PathBuf {
103 PathBuf::from(&self.registry.data_dir).join("doc_queue")
104 }
105
106 pub fn sqlite_path(&self) -> PathBuf {
107 PathBuf::from(&self.registry.data_dir).join("db.sqlite")
108 }
109
110 pub fn docs_path(&self) -> PathBuf {
111 PathBuf::from(&self.registry.data_dir).join("docs")
112 }
113
114 pub fn base_path(&self) -> PathBuf {
115 PathBuf::from(&self.registry.data_dir).join("git")
116 }
117
118 pub fn crates_io_bin_path(&self) -> PathBuf {
119 PathBuf::from(&self.registry.data_dir).join("cratesio")
120 }
121
122 pub fn crates_io_path(&self) -> String {
123 format!("{}/cratesio", self.registry.data_dir)
124 }
125
126 pub fn crates_path(&self) -> String {
127 format!("{}/crates", self.registry.data_dir)
128 }
129
130 pub fn crates_path_or_bucket(&self) -> String {
131 if self.s3.enabled {
132 self.s3.crates_bucket.clone()
133 } else if self.gcs.enabled {
134 self.gcs.crates_bucket.clone()
135 } else {
136 self.crates_path()
137 }
138 }
139
140 pub fn crates_io_path_or_bucket(&self) -> String {
141 if self.s3.enabled {
142 self.s3.cratesio_bucket.clone()
143 } else if self.gcs.enabled {
144 self.gcs.cratesio_bucket.clone()
145 } else {
146 self.crates_io_path()
147 }
148 }
149
150 pub fn toolchain_path(&self) -> String {
151 format!("{}/toolchains", self.registry.data_dir)
152 }
153
154 pub fn toolchain_path_or_bucket(&self) -> String {
155 if self.s3.enabled {
156 self.s3.toolchain_bucket.clone()
157 } else if self.gcs.enabled {
158 self.gcs.toolchain_bucket.clone()
159 } else {
160 self.toolchain_path()
161 }
162 }
163}
164
165pub fn test_settings() -> Settings {
167 Settings {
168 registry: Registry {
169 data_dir: "/tmp/kdata_test".to_string(),
170 ..Registry::default()
171 },
172 ..Settings::default()
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use provcfg::Config;
179
180 use super::*;
181 use crate::config_source::ConfigSource;
182
183 #[test]
184 fn sources_from_prov_with_no_sources_marks_every_leaf_default() {
185 let prov = Config::new().build::<SettingsProv>().unwrap();
186 let map = sources_from_prov(&prov);
187
188 assert_eq!(map.get("registry.data_dir"), Some(&ConfigSource::Default));
190 assert_eq!(map.get("docs.enabled"), Some(&ConfigSource::Default));
191 assert_eq!(map.get("s3.enabled"), Some(&ConfigSource::Default));
192 assert!(
193 map.values().all(|s| *s == ConfigSource::Default),
194 "every entry must report Default when no sources were added"
195 );
196 }
197
198 #[test]
199 fn sources_from_prov_attributes_toml_leaves_to_toml_source() {
200 let prov = Config::new()
201 .add_toml_str(
202 "test.toml",
203 "[registry]\ndata_dir = \"/tmp/x\"\n[docs]\nenabled = true\n",
204 )
205 .build::<SettingsProv>()
206 .unwrap();
207 let map = sources_from_prov(&prov);
208
209 assert_eq!(map.get("registry.data_dir"), Some(&ConfigSource::Toml));
210 assert_eq!(map.get("docs.enabled"), Some(&ConfigSource::Toml));
211 assert_eq!(map.get("registry.cache_size"), Some(&ConfigSource::Default));
214 assert_eq!(map.get("s3.enabled"), Some(&ConfigSource::Default));
215 }
216}