agent_data_plane_config/
provenance.rs

1//! [`ConfigValue`]: a configuration value paired with the reason it holds that value.
2
3use serde::{Serialize, Serializer};
4
5/// Whether a configuration value was set explicitly, or merely defaulted.
6///
7/// A configuration source generally supplies every setting it knows about, including settings nobody
8/// configured, so a value on its own cannot say whether anything set it. A setting whose meaning
9/// depends on that question needs the distinction; a setting that only needs an effective value can
10/// ignore it.
11#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
12pub enum Provenance {
13    /// Nothing set the setting, so it holds a default.
14    ///
15    /// This is the provenance of a value the model defaulted itself and of a value a source supplied
16    /// from its own defaults.
17    #[default]
18    Default,
19
20    /// Some input set this value: a configuration file, an environment variable, remote
21    /// configuration, and so on.
22    Explicit,
23}
24
25/// A configuration value together with the reason it holds that value.
26///
27/// Most model fields are plain values, because their effective value is all a consumer needs. Use
28/// `ConfigValue<T>` for a setting whose behavior depends on whether the value was set explicitly,
29/// rather than on the value alone. The primary intake URL is the canonical example: the Core Agent
30/// supplies `dd_url` at its schema default even when the operator configured only `site`, so the URL
31/// alone cannot say whether it should override `site`.
32///
33/// The value and its provenance are independent, and both are always available. A defaulted setting
34/// holds its default value with [`Provenance::Default`], rather than holding no value, so a consumer
35/// never has to restate a default the configuration layer already resolved:
36///
37/// ```ignore
38/// if endpoints.dd_url.is_explicit() {
39///     resolve_verbatim(&endpoints.dd_url.value)
40/// } else {
41///     resolve_from_site(&endpoints.site.value)
42/// }
43/// ```
44///
45/// Equality covers both fields: a value that keeps its contents but becomes explicit is a change, and
46/// a [`Live`](crate::Live) view of it wakes.
47///
48/// Serialization emits the value alone, so the shape of a serialized
49/// [`SalukiConfiguration`](crate::SalukiConfiguration) does not depend on which fields track
50/// provenance.
51///
52/// Prefer `ConfigValue<T>` over `ConfigValue<Option<T>>`. Reaching for the inner `Option` usually
53/// means it is duplicating the provenance: a `T` with [`Provenance::Default`] already expresses "no
54/// one configured this", and the nested form leaves two different ways to say so.
55#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
56pub struct ConfigValue<T> {
57    /// The effective value.
58    pub value: T,
59
60    /// Whether an input set [`value`](Self::value), or it is a default.
61    pub provenance: Provenance,
62}
63
64impl<T> ConfigValue<T> {
65    /// Creates a value with the given provenance.
66    pub fn new(value: T, provenance: Provenance) -> Self {
67        Self { value, provenance }
68    }
69
70    /// Creates a value that an input set explicitly.
71    pub fn explicit(value: T) -> Self {
72        Self::new(value, Provenance::Explicit)
73    }
74
75    /// Creates a default value that nothing set.
76    pub fn defaulted(value: T) -> Self {
77        Self::new(value, Provenance::Default)
78    }
79
80    /// Returns whether an input set this value explicitly.
81    ///
82    /// A setting that acts as an override is in force only when this is true: a defaulted value
83    /// expresses no intent to override anything.
84    pub fn is_explicit(&self) -> bool {
85        self.provenance == Provenance::Explicit
86    }
87}
88
89impl<T: Serialize> Serialize for ConfigValue<T> {
90    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
91        self.value.serialize(serializer)
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::{ConfigValue, Provenance};
98
99    #[test]
100    fn a_defaulted_value_keeps_its_effective_value() {
101        let value = ConfigValue::defaulted("https://app.datadoghq.com".to_string());
102
103        assert!(!value.is_explicit());
104        // The effective value survives, so a consumer need not restate the default.
105        assert_eq!(value.value, "https://app.datadoghq.com");
106    }
107
108    #[test]
109    fn an_explicit_value_reports_itself_as_explicit() {
110        let value = ConfigValue::explicit("https://custom.example.com".to_string());
111
112        assert!(value.is_explicit());
113        assert_eq!(value.value, "https://custom.example.com");
114    }
115
116    #[test]
117    fn provenance_participates_in_equality() {
118        // A `Live` view projecting a `ConfigValue` must wake when a value becomes explicit even though
119        // its contents are unchanged, so provenance cannot be excluded from equality.
120        assert_ne!(ConfigValue::defaulted(0u64), ConfigValue::explicit(0u64));
121    }
122
123    #[test]
124    fn the_model_default_is_a_defaulted_value() {
125        assert_eq!(ConfigValue::<u64>::default(), ConfigValue::defaulted(0));
126        assert_eq!(Provenance::default(), Provenance::Default);
127    }
128}