1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
use once_cell::sync::Lazy; use serde::Deserialize; use std::fs; use std::io; use std::path::{Path, PathBuf}; use url::Url; #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize)] pub struct Config { pub etwin: EtwinConfig, pub db: DbConfig, } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize)] pub struct EtwinConfig { pub http_port: u16, pub external_uri: Url, } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize)] pub struct DbConfig { pub host: String, pub port: u16, pub name: String, pub user: String, pub password: String, } #[derive(Debug)] pub enum FindConfigFileError { NotFound(PathBuf), Other(PathBuf, io::Error), } #[derive(Debug)] pub enum FindConfigError { NotFound(PathBuf), ParseError(toml::de::Error), Other(PathBuf, io::Error), } fn find_config_file(dir: PathBuf) -> Result<(PathBuf, String), FindConfigFileError> { for d in dir.ancestors() { let config_path = d.join("etwin.toml"); match fs::read_to_string(&config_path) { Ok(toml) => return Ok((config_path, toml)), Err(e) if e.kind() == io::ErrorKind::NotFound => continue, Err(e) => return Err(FindConfigFileError::Other(dir, e)), } } Err(FindConfigFileError::NotFound(dir)) } pub fn parse_config(_file: &Path, config_toml: &str) -> Result<Config, toml::de::Error> { let raw: Config = toml::from_str(&config_toml)?; Ok(raw) } pub fn find_config(dir: PathBuf) -> Result<Config, FindConfigError> { match find_config_file(dir) { Ok((file, config_toml)) => match parse_config(&file, &config_toml) { Ok(config) => Ok(config), Err(e) => Err(FindConfigError::ParseError(e)), }, Err(FindConfigFileError::NotFound(dir)) => Err(FindConfigError::NotFound(dir)), Err(FindConfigFileError::Other(dir, cause)) => Err(FindConfigError::Other(dir, cause)), } } pub static DEFAULT: Lazy<Config> = Lazy::new(|| Config { etwin: EtwinConfig { http_port: 50320, external_uri: Url::parse("http://localhost:50320/").unwrap(), }, db: DbConfig { host: "localhost".to_string(), port: 5432, name: "etwindb".to_string(), user: "etwin".to_string(), password: "dev".to_string(), }, }); #[cfg(test)] mod test { use crate::{parse_config, DEFAULT}; #[test] fn test_default_config() { const INPUT: &str = r#" [etwin] http_port = 50320 external_uri = "http://localhost:50320" [db] host = "localhost" port = 5432 name = "etwindb" user = "etwin" password = "dev" "#; let path = std::env::current_dir().unwrap().join("etwin.toml"); let actual = parse_config(&path, INPUT); let expected = Ok(DEFAULT.clone()); assert_eq!(actual, expected); } }