use std::sync::LazyLock; use anyhow::Result; use confique::Config; use opentelemetry_otlp::Protocol; use crate::s_log::*; #[derive(Config)] pub struct CspyConfig { #[config(env = "CSPY_DOCKER_SOCKET")] pub docker_socket: Option, #[config(env = "CSPY_OTLP_PROTO", default = "httpbinary", deserialize_with = crate::config::deser_protocol)] pub otlp_protocol: Protocol, #[config(env = "CSPY_OTLP_ENDPOINT")] pub otlp_endpoint: Option, #[config(env = "CSPY_OTLP_INTERVAL")] pub otlp_export_interval: Option, } pub static CONFIG: LazyLock = LazyLock::new(|| { let cfg_loc = std::env::var("CSPY_CONFIG"); let cfg_loc = cfg_loc .as_deref() .ok() .unwrap_or("/etc/containerspy/config.json"); let cfg = CspyConfig::builder().env().file(cfg_loc).load().unwrap(); info("Loaded config at startup", [ ("docker_socket", &*format!("{:?}", cfg.docker_socket)), ("otlp_protocol", &*format!("{:?}", cfg.otlp_protocol)), ("otlp_endpoint", &*format!("{:?}", cfg.otlp_endpoint)), ("otlp_export_interval", &*format!("{:?}", cfg.otlp_export_interval)), ]); cfg }); /// deserialization boilerplate struct ProtoDeserVisitor; /// deserialization boilerplate impl confique::serde::de::Visitor<'_> for ProtoDeserVisitor { type Value = Protocol; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str(r#""httpbinary", "httpjson", or "grpc"."#) } fn visit_str(self, v: &str) -> std::result::Result where E: confique::serde::de::Error, { Ok(match v { "httpbinary" => Protocol::HttpBinary, "httpjson" => Protocol::HttpJson, "grpc" => Protocol::Grpc, &_ => { return Err(E::custom(format!( "{v} is not a valid OTLP protocol, valid options are httpbinary, httpjson, or grpc." ))) } }) } } /// deserialization boilerplate fn deser_protocol<'de, D: confique::serde::Deserializer<'de>>(d: D) -> Result { d.deserialize_str(ProtoDeserVisitor) }