use std::fs::File; use std::io::{ErrorKind, Read}; use std::path::Path; use anyhow::{Context, Result}; use serde::Deserialize; use validator::Validate; use crate::modules; #[derive(Deserialize, Validate)] pub struct Mqtt { #[validate(length(min = 1))] pub host: String, pub port: u16, #[serde(default)] #[validate] pub credentials: Option, } #[derive(Deserialize, Validate)] pub struct MqttCredentials { pub user: String, pub password: String, } #[derive(Deserialize, Validate)] pub struct Internal { #[validate(length(min = 2))] pub stable_id: String, } #[derive(Deserialize, Validate)] pub struct Config { #[validate(custom = "crate::util::validate_hass_id")] pub friendly_id: String, #[validate(length(min = 1))] pub display_name: String, #[validate] pub mqtt: Mqtt, #[validate] pub modules: modules::Config, } pub fn load(config_file_path: &Path) -> Result> { match File::open(config_file_path) { Ok(mut file) => { log::info!("Reading configuration file: {}", config_file_path.to_string_lossy()); let mut string_content = String::new(); file.read_to_string(&mut string_content).context("while reading the configuration file")?; let parsed = toml::from_str::(string_content.as_str()).context("while parsing the configuration file")?; parsed.validate().context("while validating the configuration file")?; Ok(Some(parsed)) } Err(error) if error.kind() == ErrorKind::NotFound => { log::error!("The configuration file does not exist: {}", config_file_path.to_string_lossy()); log::info!("See the documentation: {}", env!("CARGO_PKG_HOMEPAGE")); Ok(None) } Err(error) => Err(error).context("while opening the configuration file"), } }