use anyhow::Result; use serde::Deserialize; use crate::config::RuntimeConfig; // This code is inspired by the Trunk crate (https://github.com/thedodd/trunk) // This file parses the options that can be declared in the environment. // runtime.rs applies business logic and builds RuntimeConfig structs. // - Note for the future - // There is no *need* to have a 'DIPLONAT_XXX_*' prefix for all config options. // If some config options are shared by several modules, a ConfigOptsBase could // contain them, and parse the 'DIPLONAT_*' prefix directly. // Only in runtime.rs would these options find their proper location in each // module's struct. /// Consul configuration options #[derive(Clone, Default, Deserialize)] pub struct ConfigOptsConsul { /// Consul's node name [default: None] pub node_name: Option, /// Consul's REST URL [default: "http://127.0.0.1:8500"] pub url: Option, } /// ACME configuration options #[derive(Clone, Default, Deserialize)] pub struct ConfigOptsAcme { /// Whether the ACME module is enabled [default: false] #[serde(default)] pub enable: bool, /// The default domain holder's e-mail [default: None] pub email: Option, } /// Firewall configuration options #[derive(Clone, Default, Deserialize)] pub struct ConfigOptsFirewall { /// Whether the firewall module is enabled [default: false] #[serde(default)] pub enable: bool, /// Refresh time for firewall rules [default: 300] pub refresh_time: Option, } /// IGD configuration options #[derive(Clone, Default, Deserialize)] pub struct ConfigOptsIgd { /// Whether the IGD module is enabled [default: false] #[serde(default)] pub enable: bool, /// This node's private IP address [default: None] pub private_ip: Option, /// Expiration time for IGD rules [default: 60] pub expiration_time: Option, /// Refresh time for IGD rules [default: 300] pub refresh_time: Option, } /// Model of all potential configuration options pub struct ConfigOpts { pub consul: ConfigOptsConsul, pub acme: ConfigOptsAcme, pub firewall: ConfigOptsFirewall, pub igd: ConfigOptsIgd, } impl ConfigOpts { pub fn from_env() -> Result { let consul: ConfigOptsConsul = envy::prefixed("DIPLONAT_CONSUL_").from_env()?; let acme: ConfigOptsAcme = envy::prefixed("DIPLONAT_ACME_").from_env()?; let firewall: ConfigOptsFirewall = envy::prefixed("DIPLONAT_FIREWALL_").from_env()?; let igd: ConfigOptsIgd = envy::prefixed("DIPLONAT_IGD_").from_env()?; RuntimeConfig::new(Self { consul, acme, firewall, igd, }) } // Currently only used in tests #[allow(dead_code)] pub fn from_iter(iter: Iter) -> Result where Iter: IntoIterator { let consul: ConfigOptsConsul = envy::prefixed("DIPLONAT_CONSUL_").from_iter(iter.clone())?; let acme: ConfigOptsAcme = envy::prefixed("DIPLONAT_ACME_").from_iter(iter.clone())?; let firewall: ConfigOptsFirewall = envy::prefixed("DIPLONAT_FIREWALL_").from_iter(iter.clone())?; let igd: ConfigOptsIgd = envy::prefixed("DIPLONAT_IGD_").from_iter(iter.clone())?; RuntimeConfig::new(Self { consul, acme, firewall, igd, }) } }