steid

@jamesgill /

2.5 KBCode·Blame·Raw
1use std::path::PathBuf;
2
3use serde::Deserialize;
4
5/// Application configuration, read from `STEID_`-prefixed environment variables.
6///
7/// The bind address is deliberately absent: Topcoat owns it via `HOST` and `PORT`.
8#[derive(Debug, Clone, Deserialize)]
9pub struct AppConfig {
10 /// SQLite connection string. Needs `mode=rwc` to create the file on first boot.
11 #[serde(default = "default_database_url")]
12 pub database_url: String,
13
14 /// Root directory for bare git repositories, laid out as `{data_dir}/{org}/{repo}.git`.
15 ///
16 /// Unread until Milestone 2 introduces `GitStorage`; carried now so the config
17 /// surface matches the documented environment.
18 #[allow(dead_code)]
19 #[serde(default = "default_data_dir")]
20 pub data_dir: PathBuf,
21}
22
23fn default_database_url() -> String {
24 "sqlite:steid.db?mode=rwc".to_owned()
25}
26
27fn default_data_dir() -> PathBuf {
28 PathBuf::from("./data")
29}
30
31impl AppConfig {
32 /// Loads configuration from the environment, applying defaults for anything unset.
33 pub fn from_env() -> Result<Self, envy::Error> {
34 envy::prefixed("STEID_").from_env()
35 }
36}
37
38#[cfg(test)]
39mod tests {
40 use super::*;
41
42 /// Builds a config from an explicit iterator rather than the process environment,
43 /// so tests don't race on shared global state.
44 fn from_pairs(pairs: &[(&str, &str)]) -> Result<AppConfig, envy::Error> {
45 envy::prefixed("STEID_").from_iter(
46 pairs
47 .iter()
48 .map(|(k, v)| ((*k).to_owned(), (*v).to_owned())),
49 )
50 }
51
52 #[test]
53 fn applies_defaults_when_environment_is_empty() {
54 let config = from_pairs(&[]).expect("defaults should satisfy every field");
55
56 assert_eq!(config.database_url, "sqlite:steid.db?mode=rwc");
57 assert_eq!(config.data_dir, PathBuf::from("./data"));
58 }
59
60 #[test]
61 fn reads_prefixed_variables() {
62 let config = from_pairs(&[
63 ("STEID_DATABASE_URL", "sqlite:custom.db?mode=rwc"),
64 ("STEID_DATA_DIR", "/srv/steid/repos"),
65 ])
66 .expect("explicit values should parse");
67
68 assert_eq!(config.database_url, "sqlite:custom.db?mode=rwc");
69 assert_eq!(config.data_dir, PathBuf::from("/srv/steid/repos"));
70 }
71
72 #[test]
73 fn ignores_unprefixed_variables() {
74 let config = from_pairs(&[("DATA_DIR", "/should/be/ignored")])
75 .expect("unprefixed keys should not interfere");
76
77 assert_eq!(config.data_dir, PathBuf::from("./data"));
78 }
79}