soph_config/support/
config.rs1use crate::{error::Error, Config, ConfigResult, Source};
2use soph_core::support::base_path;
3use std::path::PathBuf;
4
5impl Config {
6 pub fn load() -> Self {
8 let mut paths = Vec::new();
9
10 if let Ok(config) = std::env::var("SOPH_CONFIG") {
12 paths.push(PathBuf::from(config));
13 }
14
15 let source = std::env::args()
17 .filter(|arg| arg.starts_with("--config="))
18 .map(|arg| arg.replace("--config=", ""))
19 .collect::<Vec<_>>()
20 .first()
21 .map(|arg| arg.to_owned())
22 .unwrap_or(".config.toml".to_owned());
23
24 paths.extend([base_path(&source), base_path(".env")]);
25
26 let source = match paths.iter().find(|path| path.exists()) {
27 None => Source::Default,
28 Some(path) => Source::File(path.to_string_lossy().to_string()),
29 };
30
31 let inner = Self::load_from_source(&source).unwrap_or_else(|err| panic!("{err}"));
32
33 Self { inner, source }
34 }
35
36 pub fn load_from_source(source: &Source) -> ConfigResult<toml::Table> {
37 let config = match source.clone() {
38 Source::Default => toml::Table::default(),
39 Source::File(path) => std::fs::read_to_string(path)
40 .map_err(|err| Error::Message(format!("load config from `{source}` {err}")))?
41 .parse::<toml::Table>()?,
42 };
43
44 Ok(config)
45 }
46
47 pub fn has(&self, key: &str) -> bool {
48 self.inner.contains_key(key)
49 }
50
51 pub fn parse<T: serde::de::DeserializeOwned>(&self) -> ConfigResult<T> {
53 let key = std::any::type_name::<T>()
54 .split("::")
55 .last()
56 .expect("Cannot get type name")
57 .to_string();
58
59 match self.get(&key.to_lowercase()) {
60 Some(value) => Ok(value.to_owned().try_into::<T>()?),
61 None => super::default(),
62 }
63 }
64
65 pub fn all(self) -> toml::Table {
66 self.inner
67 }
68
69 pub fn source(&self) -> &Source {
70 &self.source
71 }
72}
73
74impl std::ops::Deref for Config {
75 type Target = toml::Table;
76
77 fn deref(&self) -> &Self::Target {
78 &self.inner
79 }
80}