1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
//! Why a snapshot was installed, and how the installs since have gone.
//!
//! Three questions an operator asks that the crate could always answer and
//! never did: *why did this change*, *did the last attempt work*, and *how
//! many have failed since one did*. [`ReloadReason`] is the first,
//! [`ReloadEvent`] carries it to a hook, and [`ConfigStatus`] is all three
//! in one cheap struct.
//!
//! Nothing here is on the read path. A reason is recorded by the same store
//! that publishes the snapshot, a failure by the same code that decided not
//! to publish one — so reading any of it is a load, never a computation,
//! and `current()` stays the single atomic load it has always been.
//!
//! **Values never appear here.** A reason names a *file*, a failure names a
//! *key path* and an [`ErrorKind`], and a status is counts and timestamps.
//! Every one of these types exists to be printed into a log, which is
//! exactly how a configured value escapes, so none of them can hold one.
use PathBuf;
use Arc;
use Instant;
use crateSnapshotMeta;
use crate;
/// What caused a snapshot to be installed.
///
/// Recorded at the call site that installs, because nothing downstream can
/// reconstruct it: by the time a hook runs, a file change and a manual
/// `reload()` have produced the identical swap.
///
/// `#[non_exhaustive]`: the set of things that can install a configuration
/// grows with the crate, and matching on it must keep compiling when it
/// does.
/// One install, as an event.
///
/// What [`on_reload_with`](crate::ConfigCell::on_reload_with) hands a hook,
/// and what the two-argument [`on_reload`](crate::ConfigCell::on_reload)
/// cannot say: *why* the snapshot moved, *which* generation it became, and
/// — through `previous` — that there was nothing before it.
///
/// The first install is an event with `previous: None`. The pair form has
/// nowhere to put that and so does not fire for it at all; this form does,
/// which is what makes [`ReloadReason::Initial`] reachable from a hook.
///
/// # A note on `Debug`
///
/// It prints the reason, the metadata and *whether* there was a previous
/// snapshot — never the snapshots themselves, however `T` renders. An event
/// is a diagnostic, a `{:?}` of one lands in a log, and a configuration
/// holds passwords. Reach for the [`current`](Self::current) field when you
/// want the values; that is a deliberate second step.
// Hand-written: `#[derive(Clone)]` would demand `T: Clone`, and an `Arc<T>`
// clones whatever `T` is.
/// A reload that did not install anything.
///
/// The category and the key path, and deliberately not the message: an
/// error's `Display` is value-free by policy and enforced by
/// `tests/security.rs`, but a struct that *stores* free text is one careless
/// `Error::new` away from carrying a value into every log that prints a
/// status. What an operator needs to act — when, what kind, which key — is
/// none of it free text.
/// What is true of a configuration right now, for an operator asking.
///
/// Every field is *recorded* where it happens rather than recomputed here,
/// so building one is a handful of atomic loads: no I/O, no source is
/// re-read, and nothing here can block. That is the constraint — an
/// exporter calling this per scrape must cost nothing.
///
/// It is assembled from several loads, so a reload landing mid-call can
/// leave one field an install ahead of another. The same trade
/// [`SnapshotMeta`] makes, for the same reason: for operators, not for
/// correctness.
///
/// # What it does not carry
///
/// **No values, by construction** — see [`FailureStatus`]. And no *source*
/// list: which sources would be read is a question about the next load, and
/// [`check`](crate::check) already answers it against the sources rather
/// than from a cache of them that could go stale. Nor is there a
/// `last_success`: an install *is* the success, so
/// [`loaded_at`](Self::loaded_at) is when the last one was.
///
/// [`loaded_at`]: Self::loaded_at