From ffae9608964a8ecce6e2c61ed790b8674e5e1a2b Mon Sep 17 00:00:00 2001 From: Jonni Liljamo Date: Sun, 19 Oct 2025 13:48:10 +0300 Subject: [PATCH] feat: config defaults --- src/config.rs | 95 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 27 deletions(-) diff --git a/src/config.rs b/src/config.rs index 0d57d35..037ab99 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,14 +5,16 @@ * more information. */ -use std::{collections::HashMap, net::IpAddr, str::FromStr}; +use std::{ + collections::HashMap, + net::{IpAddr, Ipv4Addr}, +}; use serde::Deserialize; use tokio::{fs::File, io::AsyncReadExt}; #[derive(Debug)] pub enum ConfigError { - InvalidInterface, NonExistentService(String), } @@ -21,7 +23,6 @@ impl std::error::Error for ConfigError {} impl std::fmt::Display for ConfigError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::InvalidInterface => write!(f, "invalid interface"), Self::NonExistentService(s) => write!(f, "service '{}' does not exist", s), } } @@ -58,11 +59,27 @@ pub struct NotifierConfig { pub services: Vec, } +fn default_interface() -> IpAddr { + IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)) +} + +fn default_port() -> u16 { + 3000 +} + +fn default_hashmap() -> HashMap { + HashMap::new() +} + #[derive(Deserialize)] pub struct Config { - pub interface: String, + #[serde(default = "default_interface")] + pub interface: IpAddr, + #[serde(default = "default_port")] pub port: u16, + #[serde(default = "default_hashmap")] pub services: HashMap>, + #[serde(default = "default_hashmap")] pub notifiers: HashMap, } @@ -70,11 +87,6 @@ impl Config { pub async fn from_str(s: &str) -> Result> { let config: Config = toml::from_str(s)?; - // Verify interface. - if IpAddr::from_str(&config.interface).is_err() { - return Err(Box::new(ConfigError::InvalidInterface)); - } - // Verify notifiers target services that exist. for notifier in config.notifiers.values() { for service in ¬ifier.services { @@ -96,39 +108,68 @@ impl Config { #[cfg(test)] mod tests { + use std::net::Ipv6Addr; + use super::*; #[tokio::test] - async fn basic_ok() { - assert!( - Config::from_str( - r#" + async fn defaults() { + let config = Config::from_str("").await.unwrap(); + + assert_eq!(config.interface, default_interface()); + assert_eq!(config.port, default_port()); + assert!(config.services.is_empty()); + assert!(config.notifiers.is_empty()); + } + + #[tokio::test] + async fn set_interface_ipv4() { + let config = Config::from_str( + r#" interface = "0.0.0.0" - port = 8080 + "#, + ) + .await + .unwrap(); - [services.m] - type = "matrix" - instance = "" - token = "" - room = "" + assert_eq!(config.interface, IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))); + } - [notifiers.a] - token = "42" - services = ["m"] - "# - ) - .await - .is_ok() + #[tokio::test] + async fn set_interface_ipv6() { + let config = Config::from_str( + r#" + interface = "2001:42::69" + "#, + ) + .await + .unwrap(); + + assert_eq!( + config.interface, + IpAddr::V6(Ipv6Addr::new(0x2001, 0x42, 0x0, 0x0, 0x0, 0x0, 0x0, 0x69)) ); } + #[tokio::test] + async fn set_port() { + let config = Config::from_str( + r#" + port = 42 + "#, + ) + .await + .unwrap(); + + assert_eq!(config.port, 42); + } + #[tokio::test] async fn invalid_interface() { assert!( Config::from_str( r#" interface = "0.0.0.a" - port = 8080 "# ) .await -- 2.44.1