kas_core/config/
factory.rs1#[cfg(feature = "serde")] use super::Format;
9use super::{Config, Error};
10#[cfg(feature = "serde")]
11use crate::util::warn_about_error_with_path;
12use std::cell::RefCell;
13#[cfg(feature = "serde")] use std::path::PathBuf;
14use std::rc::Rc;
15
16pub trait ConfigFactory {
18 fn read_config(&mut self) -> Result<Rc<RefCell<Config>>, Error>;
23
24 fn writer(self) -> Option<Box<dyn FnMut(&Config)>>;
26}
27
28#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
30pub struct DefaultFactory;
31
32impl ConfigFactory for DefaultFactory {
33 fn read_config(&mut self) -> Result<Rc<RefCell<Config>>, Error> {
34 Ok(Rc::new(RefCell::new(Config::default())))
35 }
36
37 fn writer(self) -> Option<Box<dyn FnMut(&Config)>> {
38 None
39 }
40}
41
42#[cfg(feature = "serde")]
46#[derive(Clone, Debug, PartialEq, Eq, Hash)]
47pub enum ConfigMode {
48 Auto,
50 Read,
52 ReadWrite,
56 WriteDefault,
60}
61
62#[cfg(feature = "serde")]
64#[derive(Clone, Debug, PartialEq, Eq, Hash)]
65pub struct ReadWriteFactory {
66 path: PathBuf,
67 mode: ConfigMode,
68 fail_on_error: bool,
69}
70
71#[cfg(feature = "serde")]
72impl ReadWriteFactory {
73 pub fn new(path: PathBuf, mode: ConfigMode) -> Self {
75 let fail_on_error = false;
76 ReadWriteFactory {
77 path,
78 mode,
79 fail_on_error,
80 }
81 }
82
83 pub fn fail_on_error(mut self) -> Self {
87 self.fail_on_error = true;
88 self
89 }
90
91 pub fn from_env() -> Self {
118 use std::env::var;
119
120 let mut path = PathBuf::new();
121 let mut mode = ConfigMode::Auto;
122 let mut fail_on_error = false;
123
124 if let Ok(v) = var("KAS_CONFIG") {
125 path = v.into();
126 }
127
128 if let Ok(mut v) = var("KAS_CONFIG_MODE") {
129 v.make_ascii_uppercase();
130 mode = match v.as_str() {
131 "READ" => ConfigMode::Read,
132 "READWRITE" => ConfigMode::ReadWrite,
133 "WRITEDEFAULT" => ConfigMode::WriteDefault,
134 other => {
135 log::error!("from_env: bad var KAS_CONFIG_MODE={other}");
136 log::error!("from_env: supported config modes: READ, READWRITE, WRITEDEFAULT");
137 mode
138 }
139 };
140 }
141
142 if let Ok(v) = var("KAS_CONFIG_FAIL_ON_ERROR") {
143 fail_on_error = match v.parse() {
144 Ok(b) => b,
145 _ => {
146 log::error!("from_env: bad var KAS_CONFIG_FAIL_ON_ERROR={v}");
147 true
148 }
149 };
150 }
151
152 ReadWriteFactory {
153 path,
154 mode,
155 fail_on_error,
156 }
157 }
158}
159
160#[cfg(feature = "serde")]
161impl ConfigFactory for ReadWriteFactory {
162 fn read_config(&mut self) -> Result<Rc<RefCell<Config>>, Error> {
163 let config = match self.mode {
164 _ if self.path.as_os_str().is_empty() => Config::default(),
165 ConfigMode::Auto | ConfigMode::Read | ConfigMode::ReadWrite => {
166 match Format::guess_and_read_path(&self.path) {
167 Ok(config) => {
168 self.mode = match std::fs::metadata(&self.path) {
169 Ok(meta) if meta.is_file() && !meta.permissions().readonly() => {
170 ConfigMode::ReadWrite
171 }
172 _ => ConfigMode::Read,
173 };
174
175 config
176 }
177 Err(error) => {
178 if matches!(&error, Error::IoError(e) if e.kind() == std::io::ErrorKind::NotFound)
179 {
180 self.mode = ConfigMode::WriteDefault;
181 } else {
182 warn_about_error_with_path("failed to read config", &error, &self.path);
183 if self.fail_on_error {
184 return Err(error);
185 }
186 }
187 Config::default()
188 }
189 }
190 }
191 ConfigMode::WriteDefault => Default::default(),
192 };
193
194 if self.mode == ConfigMode::WriteDefault
195 && let Err(error) = Format::guess_and_write_path(&self.path, &config)
196 {
197 self.mode = ConfigMode::Read;
198 warn_about_error_with_path("failed to write default config: ", &error, &self.path);
199 }
200
201 Ok(Rc::new(RefCell::new(config)))
202 }
203
204 fn writer(self) -> Option<Box<dyn FnMut(&Config)>> {
205 if self.path.as_os_str().is_empty()
206 || matches!(self.mode, ConfigMode::Read | ConfigMode::ReadWrite)
207 {
208 return None;
209 }
210
211 let path = self.path;
212 Some(Box::new(move |config| {
213 if let Err(error) = Format::guess_and_write_path(&path, config) {
214 warn_about_error_with_path("failed to write config: ", &error, &path);
215 }
216 }))
217 }
218}
219
220#[cfg(not(feature = "serde"))]
228#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
229pub struct AutoFactory(DefaultFactory);
230
231#[cfg(feature = "serde")]
239#[derive(Clone, Debug, PartialEq, Eq, Hash)]
240pub struct AutoFactory(ReadWriteFactory);
241
242#[cfg(feature = "serde")]
243impl Default for AutoFactory {
244 fn default() -> Self {
245 AutoFactory(ReadWriteFactory::from_env())
246 }
247}
248
249impl ConfigFactory for AutoFactory {
250 #[inline]
251 fn read_config(&mut self) -> Result<Rc<RefCell<Config>>, Error> {
252 self.0.read_config()
253 }
254
255 #[inline]
256 fn writer(self) -> Option<Box<dyn FnMut(&Config)>> {
257 self.0.writer()
258 }
259}