soph_config/support/
config.rs

1use crate::{error::Error, Config, ConfigResult, Source};
2use soph_core::support::base_path;
3use std::path::PathBuf;
4
5impl Config {
6    // load package config file (not workspace config file)
7    pub fn load() -> Self {
8        let mut paths = Vec::new();
9
10        // from env var
11        if let Ok(config) = std::env::var("SOPH_CONFIG") {
12            paths.push(PathBuf::from(config));
13        }
14
15        // from cli arg
16        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    /// get toml config and deserialize to type
52    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}