Skip to content

Commit

Permalink
add some debug logging to track config variable loads
Browse files Browse the repository at this point in the history
  • Loading branch information
lmaotrigine committed Oct 31, 2023
1 parent 5940991 commit cc5d7f7
Showing 1 changed file with 74 additions and 49 deletions.
123 changes: 74 additions & 49 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ use clap::Parser;
use erased_debug::Erased;
use serde::Deserialize;
use std::{
fmt::Debug,
fs::read_to_string,
io::Error as IoError,
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
path::{Path, PathBuf},
};
use toml::{self, de::Error as TomlDeError};
use tracing::info;
use tracing::{debug, info};

#[derive(Debug, Parser)]
#[clap(about, author, version = crate::VERSION)]
Expand Down Expand Up @@ -160,62 +161,36 @@ impl From<TomlDeError> for Error {
}
}

// this is a bit of a cluster fuck
// but this handles all the lookup bits in the right order
// this handles all the lookup bits in the right order
// so it goes CLI -> env vars -> profile-specific overrides -> bare values in TOML -> hardcoded fallback
macro_rules! config_field {
($first:ident.$second:ident, $field:ident, $type:ty$(, $default:expr)?) => {
pub fn $field(&self) -> Result<$type, Error> {
fn $field(&self) -> Result<$type, Error> {
let value: Result<_, Error> = if let Some(ref $field) = self.cli.$field {
debug!($field = ?$field, "Read field from CLI (or env var)");
Ok($field.to_owned())
} else {
if let Some(profile) = self.profile_toml {
if let Some(ref outer) = profile.get(stringify!($first)) {
if let Some(inner) = outer.get(stringify!($second)) {
return Ok(<$type>::deserialize(inner.clone())?);
}
}
}
if let Some(ref outer) = self.default_toml.get(stringify!($first)) {
let value = outer.get(stringify!($second)).ok_or_else(|| {
Error::MissingField(concat!(stringify!($first), ".", stringify!($second)))
})?;
let as_type = <$type>::deserialize(value.clone())?;
Ok(as_type)
} else {
Err(Error::MissingField(stringify!($first)))?
}
let field = concat!(stringify!($first), ".", stringify!($second));
self.toml_value_nested(stringify!($first), stringify!($second)).ok_or_else(|| Error::MissingField(field))
};
value$(.or_else(|_| Ok($default)))?
value$(.or_else(|_| { debug!($field = ?$default, "Using default value {:?} for field", $default);Ok($default)}))?
}
};
($field:ident, $type:ty$(, $default:expr)?) => {
pub fn $field(&self) -> Result<$type, Error> {
fn $field(&self) -> Result<$type, Error> {
let value = if let Some(ref $field) = self.cli.$field {
Ok::<_, Error>($field.to_owned())
} else {
if let Some(profile) = self.profile_toml {
if let Some($field) = profile.get(stringify!($field)) {
return Ok(<$type>::deserialize($field.clone())?);
}
}

if let Some($field) = self.default_toml.get(stringify!($field)) {
let value = <$type>::deserialize($field.clone())?;
Ok(value)
} else {
Err(Error::MissingField(stringify!($field)))
}
self.toml_value(stringify!($field))
};
value$(.or_else(|_| Ok($default)))?
value$(.or_else(|_| {debug!($field = ?$default, "Using default value {:?} for field", $default);Ok($default)}))?
}
};
}

struct Merge<'a> {
cli: Cli,
default_toml: &'a toml::Value,
profile_toml: Option<&'a toml::Value>,
toml: &'a toml::Value,
}

#[inline]
Expand All @@ -226,11 +201,67 @@ fn is_docker() -> bool {
}

impl<'a> Merge<'a> {
#[cfg(debug_assertions)]
const PROFILE: &'static str = "debug";
#[cfg(not(debug_assertions))]
const PROFILE: &'static str = "release";

fn profile_value<T: Debug + Deserialize<'a>>(&self, field: &'a str) -> Option<T> {
let value = self
.toml
.get(Self::PROFILE)
.and_then(|v| v.get(field))
.and_then(|v| T::deserialize(v.clone()).ok());
if let Some(value) = &value {
debug!(field = field, value = ?value, "Read field from `{}` TOML table", Self::PROFILE);
}
value
}

fn toml_value<T: Debug + Deserialize<'a>>(&self, field: &'static str) -> Result<T, Error> {
self.profile_value(field)
.or_else(|| {
let value = self.toml.get(field).and_then(|v| T::deserialize(v.clone()).ok());
if let Some(value) = &value {
debug!(field = field, value = ?value, "Read field from top-level TOML table");
}
value
})
.ok_or_else(|| Error::MissingField(field))
}

fn profile_value_nested<T: Debug + Deserialize<'a>>(&self, outer: &'a str, inner: &'a str) -> Option<T> {
let value = self
.toml
.get(Self::PROFILE)
.and_then(|v| v.get(outer))
.and_then(|v| v.get(inner))
.and_then(|v| T::deserialize(v.clone()).ok());
if let Some(value) = &value {
debug!(outer = outer, inner = inner, value = ?value, "Read field from `{}` TOML table", Self::PROFILE);
}
value
}

fn toml_value_nested<T: Debug + Deserialize<'a>>(&self, outer: &'static str, inner: &'a str) -> Option<T> {
self.profile_value_nested(outer, inner).or_else(|| {
let value = self
.toml
.get(outer)
.and_then(|v| v.get(inner))
.and_then(|v| T::deserialize(v.clone()).ok());
if let Some(value) = &value {
debug!(outer = outer, inner = inner, value = ?value, "Read field from top-level TOML table");
}
value
})
}

config_field!(database.dsn, database_dsn, String, {
if is_docker() {
"postgres://heartbeat@db/heartbeat".into()
String::from("postgres://heartbeat@db/heartbeat")
} else {
"postgres://postgres@localhost/postgres".into()
String::from("postgres://postgres@localhost/postgres")
}
});

Expand All @@ -242,11 +273,11 @@ impl<'a> Merge<'a> {

config_field!(secret_key, String, String::new());

config_field!(repo, String, "https://github.com/lmaotrigine/heartbeat".into());
config_field!(repo, String, String::from("https://github.com/lmaotrigine/heartbeat"));

config_field!(server_name, String, "Some person's heartbeat".into());
config_field!(server_name, String, String::from("Some person's heartbeat"));

config_field!(live_url, String, "http://127.0.0.1:6060".into());
config_field!(live_url, String, String::from("http://127.0.0.1:6060"));

config_field!(
bind,
Expand Down Expand Up @@ -308,15 +339,9 @@ impl Config {
// just an empty table
toml::Value::Table(toml::map::Map::new())
};
let profile_override = if cfg!(debug_assertions) {
toml_config.get("debug")
} else {
toml_config.get("release")
};
let config = Merge {
cli,
default_toml: &toml_config,
profile_toml: profile_override,
toml: &toml_config,
};
config.try_into()
}
Expand Down

0 comments on commit cc5d7f7

Please sign in to comment.