use anyhow::Context; use serde::{Deserialize, Serialize, Deserializer, de::{Visitor, Error, SeqAccess, value}}; use std::{collections::HashMap, fmt, fs::read_to_string, net::SocketAddr, path::PathBuf}; #[derive(Debug, Serialize, Deserialize)] pub struct Config { pub http: Option, pub https: Option, #[serde(default)] pub limits: Limits, #[serde(default)] pub hosts: HashMap, } #[derive(Debug, Serialize, Deserialize)] #[serde(default)] pub struct Limits { pub max_incoming_connections: usize, pub max_outgoing_connections: usize, } #[derive(Debug, Serialize, Deserialize)] pub struct HttpConfig { #[serde(deserialize_with = "string_or_seq")] pub bind: Vec, } #[derive(Debug, Serialize, Deserialize)] pub struct HttpsConfig { #[serde(deserialize_with = "string_or_seq")] pub bind: Vec, pub tls_cert: PathBuf, pub tls_key: PathBuf, } #[derive(Debug, Serialize, Deserialize)] #[serde(untagged)] pub enum HostConfig { Backend { backend: SocketAddr }, Files { files: FileserverConfig }, } #[derive(Debug, Serialize, Deserialize)] pub struct FileserverConfig { pub root: PathBuf, #[serde(default)] pub index: bool, } // fall back to expecting a single string and putting that in a 1-length vector fn string_or_seq<'de, D>(des: D) -> Result, D::Error> where D: Deserializer<'de> { struct StringOrList; impl<'de> Visitor<'de> for StringOrList { type Value = Vec; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("sequence or list") } fn visit_str(self, val: &str) -> Result, E> where E: Error { let addr = SocketAddr::deserialize(value::StrDeserializer::new(val))?; Ok(vec![addr]) } fn visit_seq(self, val: A) -> Result, A::Error> where A: SeqAccess<'de> { Vec::::deserialize(value::SeqAccessDeserializer::new(val)) } } des.deserialize_any(StringOrList) } impl Config { pub fn load(path: &str) -> anyhow::Result { let raw = read_to_string(path).context("reading config file")?; let config: Config = toml::from_str(&raw).context("parsing config")?; Ok(config) } } impl Default for Limits { fn default() -> Self { Self { max_incoming_connections: 1024, max_outgoing_connections: usize::MAX, } } }