winbrew_database\config/
mod.rs1use crate::core::{fs::atomic_write_toml_temp, paths};
2use anyhow::{Context, Result};
3use std::fs;
4use std::path::{Path, PathBuf};
5use tracing::warn;
6
7mod error;
8mod keys;
9mod lookup;
10mod registry;
11mod storage;
12mod types;
13mod validation;
14
15pub use error::{ConfigError, ConfigValidationError};
16pub use storage::{config_sections, config_set, config_unset, get_effective_value};
17pub use types::*;
18
19pub fn suggest_key(key: &str) -> Option<&'static str> {
20 registry::suggest_key(key)
21}
22
23impl Config {
24 pub fn load(path: &Path) -> Result<Self> {
25 match fs::read_to_string(path) {
26 Ok(contents) => {
27 if contents.trim().is_empty() {
28 Ok(Self::default())
29 } else {
30 toml::from_str(&contents).context("failed to parse config file")
31 }
32 }
33 Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(Self::default()),
34 Err(err) => Err(anyhow::Error::new(err).context("failed to read config file")),
35 }
36 }
37
38 pub fn load_at(root: &Path) -> Result<Self> {
39 let mut config = Self::load(&paths::config_file_at(root))?;
40 config.paths.root = root
41 .to_str()
42 .context("config root path is not valid UTF-8")?
43 .to_owned();
44
45 Ok(config.with_env(ConfigEnv::default()).with_config_root(root))
48 }
49
50 pub fn save(&self, path: &Path) -> Result<()> {
51 let contents = toml::to_string_pretty(self).context("failed to serialize config file")?;
52 Ok(atomic_write_toml_temp(path, &contents)?)
53 }
54
55 pub fn save_default(&self) -> Result<()> {
62 let root = self.config_storage_root();
63 let config_path = paths::config_file_at(&root);
64 self.save(&config_path)
65 }
66
67 pub fn load_current() -> Result<Self> {
68 let env = ConfigEnv::capture();
69 let root = Self::resolve_root_from_env(&env);
70 let config_path = paths::config_file_at(&root);
71 Ok(Self::load(&config_path)?
72 .with_env(env)
73 .with_config_root(&root))
74 }
75
76 pub fn resolved_paths(&self) -> paths::ResolvedPaths {
82 let root = self.runtime_root();
83 paths::resolved_paths(
84 &root,
85 &self.paths.packages,
86 &self.paths.data,
87 &self.paths.logs,
88 &self.paths.cache,
89 )
90 }
91
92 fn with_env(mut self, env: ConfigEnv) -> Self {
93 self.env = env;
94 self
95 }
96
97 fn with_config_root(mut self, root: &Path) -> Self {
98 self.config_root = Some(root.to_path_buf());
99 self
100 }
101
102 fn config_storage_root(&self) -> PathBuf {
103 self.config_root
104 .clone()
105 .unwrap_or_else(|| Self::resolve_root_from_env(&self.env))
106 }
107
108 fn runtime_root(&self) -> PathBuf {
109 self.effective_value("paths.root")
110 .map(|(value, _)| PathBuf::from(value))
111 .unwrap_or_else(|err| {
112 warn!(
113 error = %err,
114 "effective_value(\"paths.root\") failed, using raw config field as fallback"
115 );
116 PathBuf::from(&self.paths.root)
117 })
118 }
119
120 fn resolve_root_from_env(env: &ConfigEnv) -> PathBuf {
121 env.root_override()
122 .map(PathBuf::from)
123 .unwrap_or_else(|| PathBuf::from(default_root_path()))
124 }
125}