use std::time::Duration; use anyhow::{Result, anyhow}; use crate::config::{ConfigOpts, ConfigOptsAcme, ConfigOptsBase, ConfigOptsConsul}; // This code is inspired by the Trunk crate (https://github.com/thedodd/trunk) // In this file, we take ConfigOpts and transform them into ready-to-use RuntimeConfig. // We apply default values and business logic. #[derive(Debug)] pub struct RuntimeConfigAcme { pub email: String, } #[derive(Debug)] pub struct RuntimeConfigConsul { pub node_name: String, pub url: String, } #[derive(Debug)] pub struct RuntimeConfigFirewall { pub refresh_time: Duration, } #[derive(Debug)] pub struct RuntimeConfigIgd { pub private_ip: String, pub expiration_time: Duration, pub refresh_time: Duration, } #[derive(Debug)] pub struct RuntimeConfig { pub acme: Option, pub consul: RuntimeConfigConsul, pub firewall: RuntimeConfigFirewall, pub igd: RuntimeConfigIgd, } impl RuntimeConfig { pub fn new(opts: ConfigOpts) -> Result { let acme = RuntimeConfigAcme::new(opts.acme.clone())?; let consul = RuntimeConfigConsul::new(opts.consul.clone())?; let firewall = RuntimeConfigFirewall::new(opts.base.clone())?; let igd = RuntimeConfigIgd::new(opts.base.clone())?; Ok(Self { acme, consul, firewall, igd, }) } } impl RuntimeConfigAcme { pub fn new(opts: ConfigOptsAcme) -> Result> { if !opts.enable { return Ok(None); } let email = opts.email.expect( "'DIPLONAT_ACME_EMAIL' environment variable is required \ if 'DIPLONAT_ACME_ENABLE' == 'true'"); Ok(Some(Self { email, })) } } impl RuntimeConfigConsul { pub(super) fn new(opts: ConfigOptsConsul) -> Result { let node_name = opts.node_name.expect( "'DIPLONAT_CONSUL_NODE_NAME' environment variable is required"); let url = opts.url.unwrap_or(super::CONSUL_URL.to_string()); Ok(Self { node_name, url, }) } } impl RuntimeConfigFirewall { pub(super) fn new(opts: ConfigOptsBase) -> Result { let refresh_time = Duration::from_secs( opts.refresh_time.unwrap_or(super::REFRESH_TIME).into()); Ok(Self { refresh_time, }) } } impl RuntimeConfigIgd { pub(super) fn new(opts: ConfigOptsBase) -> Result { let private_ip = opts.private_ip.expect( "'DIPLONAT_PRIVATE_IP' environment variable is required"); let expiration_time = Duration::from_secs( opts.expiration_time.unwrap_or(super::EXPIRATION_TIME).into()); let refresh_time = Duration::from_secs( opts.refresh_time.unwrap_or(super::REFRESH_TIME).into()); if refresh_time.as_secs() * 2 > expiration_time.as_secs() { return Err(anyhow!( "IGD expiration time (currently: {}s) must be at least twice bigger than refresh time (currently: {}s)", expiration_time.as_secs(), refresh_time.as_secs())); } Ok(Self { private_ip, expiration_time, refresh_time, }) } }