Skip to main content

didbot_config/
precedence.rs

1//! The one place the file, command-line arguments and the environment are
2//! resolved against each other.
3//!
4//! **Command-line arguments win, then the file, then the environment.**
5//! Arguments are the leakiest tier — world-readable in `/proc/<pid>/cmdline`
6//! — so this order is a concession to how a developer actually runs the
7//! binary, not an endorsement: a secret's own *value* should never be an
8//! argument in a real deployment, only a path, and this
9//! function resolving arguments highest does not change that — see the
10//! crate root's doc.
11//!
12//! Every caller in this workspace is expected to route its own three-way
13//! merges through this function rather than reimplementing the order field
14//! by field, so the order is one fact instead of one per setting.
15
16/// Resolves one setting across the three tiers, first `Some` wins.
17///
18/// The caller's own built-in default is not a fourth argument here on
19/// purpose: `None` from every tier means "nothing configured it," and what
20/// that becomes is specific to the setting (a fixed constant, a computed
21/// value, or an outright refusal to start) in a way this function has no
22/// business deciding.
23pub fn precedence<T>(cli: Option<T>, file: Option<T>, env: Option<T>) -> Option<T> {
24    cli.or(file).or(env)
25}
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30
31    #[test]
32    fn cli_beats_file_and_env() {
33        assert_eq!(precedence(Some(1), Some(2), Some(3)), Some(1));
34    }
35
36    #[test]
37    fn file_beats_env_when_cli_is_silent() {
38        assert_eq!(precedence(None, Some(2), Some(3)), Some(2));
39    }
40
41    #[test]
42    fn env_is_last_resort() {
43        assert_eq!(precedence(None, None, Some(3)), Some(3));
44    }
45
46    #[test]
47    fn nothing_configured_is_none() {
48        assert_eq!(precedence::<u32>(None, None, None), None);
49    }
50}