1use crate::{PrayError, PrayResult};
2use serde::Deserialize;
3use std::collections::BTreeMap;
4use std::fs;
5use std::path::{Path, PathBuf};
6
7#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq)]
8pub struct PrayConfig {
9 #[serde(default)]
10 pub local: PrayLocalConfig,
11}
12
13#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq)]
14pub struct PrayLocalConfig {
15 #[serde(default)]
16 pub package: BTreeMap<String, String>,
17 #[serde(default)]
18 pub source: BTreeMap<String, String>,
19}
20
21pub fn load_user_config() -> PrayResult<PrayConfig> {
22 let Some(path) = user_config_path() else {
23 return Ok(PrayConfig::default());
24 };
25 if !path.is_file() {
26 return Ok(PrayConfig::default());
27 }
28 let text = fs::read_to_string(&path)?;
29 toml::from_str(&text).map_err(|error| PrayError::Parse {
30 kind: "config",
31 message: format!("{}: {error}", path.display()),
32 })
33}
34
35pub fn user_config_path() -> Option<PathBuf> {
36 if let Ok(path) = std::env::var("PRAY_CONFIG") {
37 return Some(PathBuf::from(path));
38 }
39 if let Ok(home) = std::env::var("PRAY_HOME") {
40 let path = Path::new(&home).join("config.toml");
41 if path.is_file() {
42 return Some(path);
43 }
44 }
45 home_directory().map(|home| home.join(".config").join("pray").join("config.toml"))
46}
47
48fn home_directory() -> Option<PathBuf> {
49 std::env::var_os("HOME").map(PathBuf::from)
50}
51
52#[cfg(test)]
53mod tests {
54 use super::PrayConfig;
55
56 #[test]
57 fn parses_local_override_tables() {
58 let config: PrayConfig = toml::from_str(
59 r#"
60[local.package]
61"sample/base" = "../fork"
62
63[local.source]
64dist = "../distribution/prayers"
65"#,
66 )
67 .expect("config");
68 assert_eq!(
69 config.local.package.get("sample/base").map(String::as_str),
70 Some("../fork")
71 );
72 assert_eq!(
73 config.local.source.get("dist").map(String::as_str),
74 Some("../distribution/prayers")
75 );
76 }
77}