diff --git a/Cargo.lock b/Cargo.lock index b5a997e..5b8296c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.68" @@ -37,6 +46,7 @@ dependencies = [ "impl_cast", "paste", "pollster", + "regex", "reqwest", "rumqttc", "serde", @@ -784,6 +794,8 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] diff --git a/Cargo.toml b/Cargo.toml index 88c4aaa..21ac022 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ tracing = "0.1.37" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } bytes = "1.3.0" pollster = "0.2.5" +regex = "1.7.0" [profile.release] lto=true diff --git a/config/zeus.dev.toml b/config/zeus.dev.toml index 58fa33f..223b402 100644 --- a/config/zeus.dev.toml +++ b/config/zeus.dev.toml @@ -5,6 +5,10 @@ base_url = "https://login.huizinga.dev/api/oidc" host="olympus.lan.huizinga.dev" port=8883 username="mqtt" +password="${MQTT_PASSWORD}" + +[ntfy] +topic = "${NTFY_TOPIC}" [presence] topic = "automation_dev/presence" diff --git a/src/config.rs b/src/config.rs index 36c7180..14ebfed 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,7 @@ use std::{fs, error::Error, collections::HashMap, net::{Ipv4Addr, SocketAddr}}; -use tracing::{debug, trace}; +use regex::{Regex, Captures}; +use tracing::{debug, trace, warn}; use rumqttc::AsyncClient; use serde::Deserialize; @@ -14,7 +15,6 @@ pub struct Config { pub mqtt: MqttConfig, #[serde(default)] pub fullfillment: FullfillmentConfig, - #[serde(default)] pub ntfy: NtfyConfig, pub presence: MqttDeviceConfig, pub light_sensor: LightSensorConfig, @@ -33,7 +33,7 @@ pub struct MqttConfig { pub host: String, pub port: u16, pub username: String, - pub password: Option, + pub password: String, } #[derive(Debug, Deserialize)] @@ -68,19 +68,13 @@ fn default_fullfillment_port() -> u16 { pub struct NtfyConfig { #[serde(default = "default_ntfy_url")] pub url: String, - pub topic: Option, + pub topic: String, } fn default_ntfy_url() -> String { "https://ntfy.sh".into() } -impl Default for NtfyConfig { - fn default() -> Self { - Self { url: default_ntfy_url(), topic: None } - } -} - #[derive(Debug, Deserialize)] pub struct LightSensorConfig { #[serde(flatten)] @@ -161,11 +155,23 @@ impl Config { pub fn build(filename: &str) -> Result> { debug!("Loading config: {filename}"); let file = fs::read_to_string(filename)?; - let mut config: Self = toml::from_str(&file)?; - config.mqtt.password = Some(std::env::var("MQTT_PASSWORD").or(config.mqtt.password.ok_or("MQTT password needs to be set in either config [mqtt.password] or the environment [MQTT_PASSWORD]"))?); - config.ntfy.topic = Some(std::env::var("NTFY_TOPIC").or(config.ntfy.topic.ok_or("ntfy.sh topic needs to be set in either config [ntfy.url] or the environment [NTFY_TOPIC]!"))?); + // Substitute in environment variables + let re = Regex::new(r"\$\{(.*)\}").unwrap(); + let file = re.replace_all(&file, |caps: &Captures| { + let key = caps.get(1).unwrap().as_str(); + debug!("Substituting '{key}' in config"); + match std::env::var(key) { + Ok(value) => value, + Err(_) => { + // @TODO Would be nice if we could propagate this error upwards + warn!("Environment variable '{key}' is not set, using empty string as default"); + "".to_string() + } + } + }); + let config = toml::from_str(&file)?; Ok(config) } } diff --git a/src/main.rs b/src/main.rs index 4118cb8..539c035 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ #![feature(async_closure)] -use std::{time::Duration, sync::{Arc, RwLock}, process, net::SocketAddr}; +use std::{time::Duration, sync::{Arc, RwLock}, process}; use axum::{Router, Json, routing::post, http::StatusCode, extract::FromRef}; @@ -45,7 +45,7 @@ async fn main() { // Configure MQTT let mut mqttoptions = MqttOptions::new("rust-test", config.mqtt.host, config.mqtt.port); - mqttoptions.set_credentials(config.mqtt.username, config.mqtt.password.unwrap()); + mqttoptions.set_credentials(config.mqtt.username, config.mqtt.password); mqttoptions.set_keep_alive(Duration::from_secs(5)); mqttoptions.set_transport(Transport::tls_with_default_config()); diff --git a/src/ntfy.rs b/src/ntfy.rs index 88fca77..de2b36f 100644 --- a/src/ntfy.rs +++ b/src/ntfy.rs @@ -89,7 +89,7 @@ impl Notification { impl Ntfy { pub fn new(config: NtfyConfig) -> Self { - Self { base_url: config.url, topic: config.topic.unwrap() } + Self { base_url: config.url, topic: config.topic } } }