@@ 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<String>,
}
+fn default_interface() -> IpAddr {
+ IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))
+}
+
+fn default_port() -> u16 {
+ 3000
+}
+
+fn default_hashmap<K, V>() -> HashMap<K, V> {
+ 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<String, Box<dyn ServiceConfig>>,
+ #[serde(default = "default_hashmap")]
pub notifiers: HashMap<String, NotifierConfig>,
}
@@ 70,11 87,6 @@ impl Config {
pub async fn from_str(s: &str) -> Result<Self, Box<dyn std::error::Error>> {
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