Skip to main content

kellnr_settings/
settings.rs

1use std::convert::TryFrom;
2use std::path::{Path, PathBuf};
3
4use config::{Config, ConfigError, Environment, File};
5use serde::{Deserialize, Serialize};
6
7use crate::config_source::{SourceMap, init_default_sources};
8use crate::docs::Docs;
9use crate::local::Local;
10use crate::log::Log;
11use crate::oauth2::OAuth2;
12use crate::origin::Origin;
13use crate::postgresql::Postgresql;
14use crate::proxy::Proxy;
15use crate::registry::Registry;
16use crate::s3::S3;
17use crate::setup::Setup;
18use crate::toolchain::Toolchain;
19
20#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)]
21#[serde(default)]
22pub struct Settings {
23    pub setup: Setup,
24    pub registry: Registry,
25    pub docs: Docs,
26    pub proxy: Proxy,
27    pub log: Log,
28    pub local: Local,
29    pub origin: Origin,
30    pub postgresql: Postgresql,
31    pub s3: S3,
32    pub oauth2: OAuth2,
33    pub toolchain: Toolchain,
34    /// Tracks the source (default, toml, env, cli) for each setting.
35    /// Skipped in serde serialization - use `SettingsResponse` for API responses.
36    #[serde(skip)]
37    pub sources: SourceMap,
38}
39
40impl Default for Settings {
41    fn default() -> Self {
42        Self {
43            setup: Setup::default(),
44            registry: Registry::default(),
45            docs: Docs::default(),
46            proxy: Proxy::default(),
47            log: Log::default(),
48            local: Local::default(),
49            origin: Origin::default(),
50            postgresql: Postgresql::default(),
51            s3: S3::default(),
52            oauth2: OAuth2::default(),
53            toolchain: Toolchain::default(),
54            sources: init_default_sources(),
55        }
56    }
57}
58
59impl TryFrom<Option<&Path>> for Settings {
60    type Error = ConfigError;
61
62    fn try_from(config_file: Option<&Path>) -> Result<Self, Self::Error> {
63        let mut builder = Config::builder();
64
65        // Add configuration from file if provided
66        if let Some(path) = config_file {
67            builder = builder.add_source(File::from(path).required(true));
68        }
69
70        // Add settings from environment variables (with prefix KELLNR)
71        builder = builder.add_source(
72            Environment::with_prefix("KELLNR")
73                .list_separator(",")
74                .with_list_parse_key("registry.required_crate_fields")
75                .with_list_parse_key("oauth2.scopes")
76                .try_parsing(true)
77                .prefix_separator("_")
78                .separator("__"),
79        );
80
81        builder.build()?.try_deserialize()
82    }
83}
84
85impl Settings {
86    pub fn bin_path(&self) -> PathBuf {
87        PathBuf::from(&self.registry.data_dir).join("crates")
88    }
89
90    pub fn doc_queue_path(&self) -> PathBuf {
91        PathBuf::from(&self.registry.data_dir).join("doc_queue")
92    }
93
94    pub fn sqlite_path(&self) -> PathBuf {
95        PathBuf::from(&self.registry.data_dir).join("db.sqlite")
96    }
97
98    pub fn docs_path(&self) -> PathBuf {
99        PathBuf::from(&self.registry.data_dir).join("docs")
100    }
101
102    pub fn base_path(&self) -> PathBuf {
103        PathBuf::from(&self.registry.data_dir).join("git")
104    }
105
106    pub fn crates_io_bin_path(&self) -> PathBuf {
107        PathBuf::from(&self.registry.data_dir).join("cratesio")
108    }
109
110    pub fn crates_io_path(&self) -> String {
111        format!("{}/cratesio", self.registry.data_dir)
112    }
113
114    pub fn crates_path(&self) -> String {
115        format!("{}/crates", self.registry.data_dir)
116    }
117
118    pub fn crates_path_or_bucket(&self) -> String {
119        if self.s3.enabled {
120            self.s3.crates_bucket.clone()
121        } else {
122            self.crates_path()
123        }
124    }
125
126    pub fn crates_io_path_or_bucket(&self) -> String {
127        if self.s3.enabled {
128            self.s3.cratesio_bucket.clone()
129        } else {
130            self.crates_io_path()
131        }
132    }
133
134    pub fn toolchain_path(&self) -> String {
135        format!("{}/toolchains", self.registry.data_dir)
136    }
137
138    pub fn toolchain_path_or_bucket(&self) -> String {
139        if self.s3.enabled {
140            self.s3.toolchain_bucket.clone()
141        } else {
142            self.toolchain_path()
143        }
144    }
145}
146
147pub fn get_settings() -> Result<Settings, ConfigError> {
148    let path = crate::compile_time_config::KELLNR_COMPTIME__CONFIG_FILE.map(Path::new);
149    Settings::try_from(path)
150}
151
152/// Used in Unit and Integration tests to provide test settings
153pub fn test_settings() -> Settings {
154    Settings {
155        registry: Registry {
156            data_dir: "/tmp/kdata_test".to_string(),
157            ..Registry::default()
158        },
159        sources: init_default_sources(),
160        ..Settings::default()
161    }
162}