use super::sections::LogLevel;
use crate::values::UpDuration;
use std::path::PathBuf;
#[non_exhaustive]
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct DaemonOverrides {
pub log_json: Option<bool>,
pub log_level: Option<LogLevel>,
pub socket: Option<PathBuf>,
pub max_cron_sleep: Option<UpDuration>,
}
#[must_use]
pub fn parse_daemon_bool(value: &str) -> Option<bool> {
match value {
"1" | "true" => Some(true),
"0" | "false" => Some(false),
_ => None,
}
}
impl DaemonOverrides {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn log_json(mut self, value: Option<bool>) -> Self {
self.log_json = value;
self
}
#[must_use]
pub fn log_level(mut self, value: Option<LogLevel>) -> Self {
self.log_level = value;
self
}
#[must_use]
pub fn socket(mut self, value: Option<PathBuf>) -> Self {
self.socket = value;
self
}
#[must_use]
pub fn max_cron_sleep(mut self, value: Option<UpDuration>) -> Self {
self.max_cron_sleep = value;
self
}
}
#[cfg(test)]
mod tests {
use super::super::config::DaemonConfig;
use super::super::error::DaemonConfigError;
use super::super::sections::{LogLevel, MIN_CRON_SLEEP};
use super::super::testing::*;
use super::*;
use crate::values::UpDuration;
#[test]
fn below_minimum_display_is_exact() {
let err = DaemonConfigError::BelowMinimum {
key: "max_cron_sleep",
value: UpDuration::from_millis(999),
min: UpDuration::from_millis(1_000),
};
assert_eq!(
err.to_string(),
"invalid value `999` for max_cron_sleep: must be at least 1s"
);
}
#[test]
fn file_sets_values_and_keeps_dog_sections_raw() {
let src = r#"
[daemon]
log_json = true
enabled_dogs = ["metrics"]
[dog.metrics]
port = 9615
"#;
let cfg = DaemonConfig::load(Some(src), &no_env).unwrap();
assert!(cfg.daemon.log_json);
assert_eq!(cfg.daemon.enabled_dogs, vec!["metrics"]);
assert_eq!(cfg.dog["metrics"]["port"].as_integer(), Some(9615));
}
#[test]
fn a_flag_rescues_a_below_floor_file_value() {
let cfg = DaemonConfig::load_layered(
Some("[daemon]\nmax_cron_sleep = \"500\"\n"),
&no_env,
&DaemonOverrides::new().max_cron_sleep(Some(UpDuration::from_millis(300_000))),
)
.unwrap();
assert_eq!(
cfg.daemon.max_cron_sleep,
Some(UpDuration::from_millis(300_000))
);
}
#[test]
fn a_below_floor_flag_is_refused_naming_the_flag() {
let err = DaemonConfig::load_layered(
None,
&no_env,
&DaemonOverrides::new().max_cron_sleep(Some(UpDuration::from_millis(500))),
)
.unwrap_err();
assert_eq!(
err,
DaemonConfigError::BelowMinimum {
key: "--max-cron-sleep",
value: UpDuration::from_millis(500),
min: MIN_CRON_SLEEP,
}
);
assert!(err.to_string().contains("--max-cron-sleep"), "got: {err}");
}
#[test]
fn a_flag_beats_the_environment() {
let env = |k: &str| (k == "SHEP_LOG_LEVEL").then(|| "trace".to_string());
let cfg = DaemonConfig::load_layered(
Some("[daemon]\nlog_level = \"error\"\n"),
&env,
&DaemonOverrides::new().log_level(Some(LogLevel::Info)),
)
.unwrap();
assert_eq!(cfg.daemon.log_level, LogLevel::Info);
}
#[test]
fn the_bool_grammar_is_exactly_four_spellings() {
assert_eq!(parse_daemon_bool("1"), Some(true));
assert_eq!(parse_daemon_bool("0"), Some(false));
assert_eq!(parse_daemon_bool("true"), Some(true));
assert_eq!(parse_daemon_bool("false"), Some(false));
for wider in ["yes", "no", "on", "off", "TRUE", "y"] {
assert_eq!(
parse_daemon_bool(wider),
None,
"{wider} must not be a boolean here"
);
}
}
}