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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
//! Helpers for integrating common configuration patterns.
//!
//! There are some common patterns of integrating pieces of configuration into an application and
//! make them active. Many of these patterns require registering in multiple callbacks at once to
//! work correctly. Doing it manually is tedious and error prone.
//!
//! The traits in this module allow registering all the callbacks in one go, making it easier for
//! other crates to integrate such patterns.
use std::any::TypeId;
use std::borrow::Borrow;
use std::fmt::{Debug, Display};
use std::sync::Arc;

use arc_swap::ArcSwap;
use serde::de::DeserializeOwned;
use structopt::StructOpt;

use super::Builder;

/// The basic helper trait.
///
/// It allows being plugged into a builder and modifying it in an arbitrary way.
///
/// It is more common to apply the helper by the
/// [`Builder::with`](../struct.Builder.html#method.with) method than directly.
///
/// There's an implementation of `Helper` for `FnOnce(Builder) -> Builder`, so helpers can be
/// either custom types or just closures (which are often more convenient than defining an empty
/// type and the implementation).
///
/// ```rust
/// use spirit::{Builder, Empty, Spirit};
/// use spirit::helpers::Helper;
///
/// struct CfgPrint;
///
/// impl Helper<Empty, Empty> for CfgPrint {
///     fn apply(self, builder: Builder<Empty, Empty>) -> Builder<Empty, Empty> {
///         builder.on_config(|_opts, _config| println!("Config changed"))
///     }
/// }
///
/// Spirit::<Empty, Empty>::new()
///     .with(CfgPrint)
///     .run(|_spirit| {
///         println!("Running...");
///         Ok(())
///     })
/// ```
///
/// ```rust
/// use spirit::{Builder, Empty, Spirit};
///
/// fn cfg_print(builder: Builder<Empty, Empty>) -> Builder<Empty, Empty> {
///     builder.on_config(|_opts, _config| println!("Config changed"))
/// }
///
/// Spirit::<Empty, Empty>::new()
///     .with(cfg_print)
///     .run(|_spirit| {
///         println!("Running...");
///         Ok(())
///     })
/// ```
pub trait Helper<O, C> {
    /// Perform the transformation on the given builder.
    ///
    /// And yes, it is possible to do multiple primitive transformations inside one helper (this is
    /// what makes helpers useful for 3rd party crates, they can integrate with just one call of
    /// [`with`](../struct.Builder.html#method.with)).
    fn apply(self, builder: Builder<O, C>) -> Builder<O, C>;
}

impl<O, C, F> Helper<O, C> for F
where
    F: FnOnce(Builder<O, C>) -> Builder<O, C>,
{
    fn apply(self, builder: Builder<O, C>) -> Builder<O, C> {
        self(builder)
    }
}

/// A specialized version of [`Helper`](trait.Helper.html) for a piece of extracted configuration.
///
/// This traits works in tandem with an extractor function and action. The extractor is supposed to
/// extract a specific piece of configuration. The trait is defined on the type returned by the
/// extractor and produces some kind of resource. The action is then performed with the resource.
///
/// As an example, the type implementing the trait could be a configuration for a TCP socket. The
/// extractor just pulls out the instance of the type out of the configuration. The action could be
/// whatever the application needs to do with the TCP socket. The helper then bridges these
/// together by making the socket out of the configuration.
///
/// The trait often delegates to the basic version of `Helper` under the hood, by connecting the
/// extractor with the „active“ part of the helper.
///
/// You can use the [`Builder::config_helper`](../struct.Builder.html#method.config_helper) to
/// apply a `CfgHelper`.
///
/// # TODO
///
/// This calls for an example.
///
/// # Future plans
///
/// It is planned to eventually have a custom derive for these kinds of helpers to compose a helper
/// of a bigger piece of configuration. The extractor would then be auto-generated.
pub trait CfgHelper<O, C, Action> {
    /// Perform the creation and application of the helper.
    ///
    /// # Params
    ///
    /// * `extractor`: Function that pulls out a bit of configuration out of the complete
    ///   configuration type.
    /// * `action`: Something application-specific performed with the resource built of the
    ///   relevant piece of configuration.
    /// * `name`: Named used in logs to reference the specific instance of the type in logs. It is
    ///   more useful to have „heartbeat connection“ instead of „tcp socket“ in there (often,
    ///   application has many different kinds of tcp sockets around).
    /// * `builder`: The builder to modify by this helper.
    fn apply<Extractor, Name>(
        extractor: Extractor,
        action: Action,
        name: Name,
        builder: Builder<O, C>,
    ) -> Builder<O, C>
    where
        Extractor: FnMut(&C) -> Self + Send + 'static,
        Name: Clone + Display + Send + Sync + 'static;
}

/// A variant of the [`CfgHelper`](trait.CfgHelper.html) for resources that come in groups.
///
/// If an application should (for example) listen for incoming connections, it is often desirable
/// to be able to configure multiple listening endpoints at once.
///
/// In simple words, if the `IteratedCfgHelper` is implemented for a type, a `CfgHelper` is
/// implemented for a container of the type (eg. `Vec`). The extractor then extracts the vector and
/// the helper takes care of managing multiple instances of the resource.
///
/// # Single instance
///
/// If a helper is implemented in terms of `IteratedCfgHelper` and your application configuration
/// contains exactly one instance, it is possible to return `iter::once` from the extractor, which
/// will pretend the configuration contains a container of exactly one thing.
///
/// Some helper crates may already provide both implementations on the same type in this manner.
pub trait IteratedCfgHelper<O, C, Action> {
    /// Perform the transformation of the builder.
    ///
    /// It works the same way as [`CfgHelper::apply`](trait.CfgHelper.html#method.apply), only with
    /// slightly different types around the extractor.
    fn apply<Extractor, ExtractedIter, Name>(
        extractor: Extractor,
        action: Action,
        name: Name,
        builder: Builder<O, C>,
    ) -> Builder<O, C>
    where
        Self: Sized, // TODO: Why does rustc insist on this one?
        Extractor: FnMut(&C) -> ExtractedIter + Send + 'static,
        ExtractedIter: IntoIterator<Item = Self>,
        Name: Clone + Display + Send + Sync + 'static;
}

impl<O, C, Action, Iter, Target> CfgHelper<O, C, Action> for Iter
where
    Iter: IntoIterator<Item = Target>,
    Target: IteratedCfgHelper<O, C, Action>,
{
    fn apply<Extractor, Name>(
        extractor: Extractor,
        action: Action,
        name: Name,
        builder: Builder<O, C>,
    ) -> Builder<O, C>
    where
        Extractor: FnMut(&C) -> Self + Send + 'static,
        Name: Clone + Display + Send + Sync + 'static,
    {
        <Target as IteratedCfgHelper<O, C, Action>>::apply(extractor, action, name, builder)
    }
}

/// A helper to store configuration to some global-ish storage.
///
/// This makes sure every time a new config is loaded, it is made available inside the passed
/// parameter. Therefore, places without direct access to the `Spirit` itself can look into the
/// configuration.
///
/// The parameter can be a lot of things, but usually:
///
/// * `Arc<ArcSwap<C>>`.
/// * A reference to global `ArcSwap<C>` (for example inside `lazy_static` or `once_cell`).
///
/// # Examples
///
/// ```rust
/// #[macro_use]
/// extern crate lazy_static;
/// extern crate spirit;
///
/// use std::sync::Arc;
///
/// use spirit::{ArcSwap, Empty, Spirit};
/// use spirit::helpers;
///
/// lazy_static! {
///     static ref CFG: ArcSwap<Empty> = ArcSwap::from(Arc::new(Empty {}));
/// }
///
/// # fn main() {
/// # let _ =
/// Spirit::<Empty, Empty>::new()
///     // Will make sure CFG contains the newest config
///     .with(helpers::cfg_store(&*CFG))
///     .build(false);
/// # }
/// ```
pub fn cfg_store<S, O, C>(storage: S) -> impl Helper<O, C>
where
    S: Borrow<ArcSwap<C>> + Send + Sync + 'static,
    C: DeserializeOwned + Send + Sync + 'static,
    O: Debug + StructOpt + Sync + Send + 'static,
{
    |builder: Builder<O, C>| {
        builder.on_config(move |_o: &O, c: &Arc<C>| storage.borrow().store(Arc::clone(c)))
    }
}

/// A helper for one-time initial configuration.
///
/// Sometimes, some configuration values can't be reasonably updated at runtime (libraries don't
/// support reconfiguration, there's no time to do that, ...). This callback tries to improve the
/// situation around these configurations.
///
/// The `extractor` extracts a fragment of configuration every time a configuration is loaded. The
/// first time this happens, `init` is called with this extracted configuration. Upon any future
/// configuration reloads, a warning is issued (with the given `name`) if the configuration
/// contains a different value than the one it was originally initialized.
///
/// # Examples
///
/// ```
/// #[macro_use]
/// extern crate serde_derive;
/// extern crate spirit;
///
/// use spirit::{Empty, Spirit};
/// use spirit::helpers;
///
/// #[derive(Clone, Debug, Default, Deserialize)]
/// struct Cfg {
///     #[serde(default)]
///     msg: String,
/// }
///
/// impl Cfg {
///     fn msg(&self) -> &String {
///         &self.msg
///     }
/// }
///
/// fn print_msg(msg: &String) {
///     println!("{}", msg);
/// }
///
/// fn main() {
///     Spirit::<Empty, Cfg>::new()
///         // The first version of `msg` is printed at the initial configuration load. If however
///         // the configuration changes into some other message, a warning is printed (because
///         // there's no way to modify the already printed message
///         .with(helpers::immutable_cfg_init(Cfg::msg, print_msg, "message"))
///         .run(|_| Ok(()));
/// }
/// ```
pub fn immutable_cfg_init<O, C, R, E, F, N>(extractor: E, init: F, name: N) -> impl Helper<O, C>
where
    E: for<'a> Fn(&'a C) -> &R + Send + 'static,
    F: FnOnce(&R) + Send + 'static,
    R: Clone + PartialEq + Send + 'static,
    C: DeserializeOwned + Send + Sync + 'static,
    O: Debug + StructOpt + Sync + Send + 'static,
    N: Display + Send + 'static,
{
    let mut first = None;
    let mut init = Some(init);
    let on_cfg = move |_o: &O, c: &Arc<C>| {
        let extracted = extractor(&c);
        if first.is_none() {
            first = Some(extracted.clone());
            (init.take().expect("Init called multiple times"))(extracted);
        } else if first.as_ref() != Some(extracted) {
            warn!("Configuration {} can't be changed at runtime", name);
        }
    };
    |builder: Builder<O, C>| builder.on_config(on_cfg)
}

/// A helper to warn about changes to configuration that can't be updated at runtime.
///
/// This is similar to [`immutable_cfg_init`](fn.immutable_cfg_init.html), except that there's no
/// callback called at the first load.
///
/// # Examples
///
/// ```
/// #[macro_use]
/// extern crate serde_derive;
/// extern crate spirit;
///
/// use spirit::{Empty, Spirit};
/// use spirit::helpers;
///
/// #[derive(Clone, Debug, Default, Deserialize)]
/// struct Cfg {
///     #[serde(default)]
///     msg: String,
/// }
///
/// impl Cfg {
///     fn msg(&self) -> &String {
///         &self.msg
///     }
/// }
///
/// fn main() {
///     Spirit::<Empty, Cfg>::new()
///         // This prints a warning if the message is ever changed during runtime ‒ we can't take
///         // it back and change it after it got printed in the body.
///         .with(helpers::immutable_cfg(Cfg::msg, "message"))
///         .run(|spirit| {
///             println!("{}", spirit.config().msg);
///             Ok(())
///         });
/// }
/// ```
pub fn immutable_cfg<O, C, R, E, N>(extractor: E, name: N) -> impl Helper<O, C>
where
    E: for<'a> Fn(&'a C) -> &R + Send + 'static,
    R: Clone + PartialEq + Send + 'static,
    C: DeserializeOwned + Send + Sync + 'static,
    O: Debug + StructOpt + Sync + Send + 'static,
    N: Display + Send + 'static,
{
    immutable_cfg_init(extractor, |_| (), name)
}

impl<O, C> Builder<O, C>
where
    C: DeserializeOwned + Send + Sync + 'static,
    O: Debug + StructOpt + Sync + Send + 'static,
{
    /// Apply a config helper to the builder.
    ///
    /// For more information see [`CfgHelper`](helpers/trait.CfgHelper.html).
    pub fn config_helper<Cfg, Extractor, Action, Name>(
        self,
        extractor: Extractor,
        action: Action,
        name: Name,
    ) -> Self
    where
        Extractor: FnMut(&C) -> Cfg + Send + 'static,
        Cfg: CfgHelper<O, C, Action>,
        Name: Clone + Display + Send + Sync + 'static,
    {
        trace!("Adding config helper for {}", name);
        CfgHelper::apply(extractor, action, name, self)
    }

    /// Check if this is the first call with the given type.
    ///
    /// Some helpers share common part. This common part makes sense to register just once, so this
    /// can be used to check that. The first call with given type returns `true`, any future ones
    /// with the same type return `false`.
    ///
    /// The method has no direct effect on the future spirit constructed from the builder and
    /// works only as a note for future helpers that want to manipulate the builder.
    ///
    /// A higher-level interface is the [`with_singleton`](#method.with_singleton) method.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use spirit::{Empty, Spirit};
    ///
    /// let mut builder = Spirit::<Empty, Empty>::new();
    ///
    /// struct X;
    /// struct Y;
    ///
    /// assert!(builder.singleton::<X>());
    /// assert!(!builder.singleton::<X>());
    /// assert!(builder.singleton::<Y>());
    /// ```
    pub fn singleton<T: 'static>(&mut self) -> bool {
        self.singletons.insert(TypeId::of::<T>())
    }

    /// Apply a ['Helper`](helpers/trait.Helper.html) to the builder.
    pub fn with<H: Helper<O, C>>(self, helper: H) -> Self {
        trace!("Adding a helper");
        helper.apply(self)
    }

    /// Apply the first [`Helper`](helpers.trait.Helper.html) of the type.
    ///
    /// This applies the passed helper, but only if a helper with the same hasn't yet been applied
    /// (or the [`singleton`](#method.singleton) called manually).
    ///
    /// Note that different instances of the same type of a helper can act differently, but are
    /// still considered the same type. This means the first instance wins. This is considered a
    /// feature ‒ many other helpers need some environment to run in (like `tokio`). The helpers
    /// try to apply a default configuration, but the user can apply a specific configuration
    /// first.
    pub fn with_singleton<T: Helper<O, C> + 'static>(mut self, singleton: T) -> Self {
        if self.singleton::<T>() {
            self.with(singleton)
        } else {
            trace!("Singleton already exists");
            self
        }
    }
}