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
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
#![cfg_attr(nightly, feature(doc_cfg))]
#![deny(missing_docs)]

//! Semi-hierarchical configuration so con-free, it's unreal.
//!
//! ```rust
//! use serde::Deserialize;
//! use figment::{Figment, providers::{Format, Toml, Json, Env}};
//!
//! #[derive(Deserialize)]
//! struct Package {
//!     name: String,
//!     description: Option<String>,
//!     authors: Vec<String>,
//!     publish: Option<bool>,
//!     // ... and so on ...
//! }
//!
//! #[derive(Deserialize)]
//! struct Config {
//!     package: Package,
//!     rustc: Option<String>,
//!     rustdoc: Option<String>,
//!     // ... and so on ...
//! }
//!
//! # figment::Jail::expect_with(|jail| {
//! # jail.create_file("Cargo.toml", r#"
//! #   [package]
//! #   name = "test"
//! #   authors = ["bob"]
//! #   publish = false
//! # "#)?;
//! let config: Config = Figment::new()
//!     .merge(Toml::file("Cargo.toml"))
//!     .merge(Env::prefixed("CARGO_"))
//!     .merge(Env::raw().only(&["RUSTC", "RUSTDOC"]))
//!     .join(Json::file("Cargo.json"))
//!     .extract()?;
//! # Ok(())
//! # });
//! ```
//!
//! # Table of Contents
//!
//!   * [Overview](#overview) - A brief overview of the entire crate.
//!   * [Metadata](#metadata) - Figment's value metadata tracking.
//!   * [Extracting and Profiles](#extracting-and-profiles) - Semi-hierarchical
//!     "profiles", profile selection, nesting, and extraction.
//!   * [Crate Feature Flags](#crate-feature-flags) - Feature flags and what
//!     they enable.
//!   * [Built-In Providers](#built-in-providers) - Table of providers provided
//!     by this crate.
//!   * [For `Provider` Authors](#for-provider-authors) - Tips for writing
//!     [`Provider`]s.
//!   * [For Library Authors](#for-library-authors) - Brief guide for authors
//!     wishing to use Figment in their libraries or frameworks.
//!   * [For Application Authors](#for-application-authors) - Brief guide for
//!     authors of applications that use libraries that use Figment.
//!   * [Tips](#tips) - Things to remember when working with Figment.
//!   * [Type Index](#modules) - The real rustdocs.
//!
//! # Overview
//!
//! Figment is a library for declaring and combining configuration sources and
//! extracting typed values from the combined sources. There are two prevailing
//! concepts:
//!
//!   * **Providers:** Types implementing the [`Provider`] trait, which
//!     implement a configuration source.
//!   * **Figments:** The [`Figment`] type, which combines providers via
//!     [`merge`](Figment::merge()) or [`join`](Figment::join) and allows
//!     typed [`extraction`](Figment::extract()). Figments are also providers
//!     themselves.
//!
//! Defining a configuration consists of constructing a `Figment` and merging or
//! joining any number of [`Provider`]s. Values for duplicate keys from a
//! _merged_ provider replace those from previous providers, while no
//! replacement occurs for _joined_ providers. Sources are read eagerly,
//! immediately upon merging and joining.
//!
//! The simplest useful figment has one provider. The figment below will use all
//! environment variables prefixed with `MY_APP_` as configuration values, after
//! removing the prefix:
//!
//! ```
//! use figment::{Figment, providers::Env};
//!
//! let figment = Figment::from(Env::prefixed("MY_APP_"));
//! ```
//!
//! Most figments will use more than one provider, merging and joining as
//! necessary. The figment below reads `App.toml`, environment variables
//! prefixed with `APP_` and fills any holes (but does replace existing values)
//! with values from `App.json`:
//!
//! ```
//! use figment::{Figment, providers::{Format, Toml, Json, Env}};
//!
//! let figment = Figment::new()
//!     .merge(Toml::file("App.toml"))
//!     .merge(Env::prefixed("APP_"))
//!     .join(Json::file("App.json"));
//! ```
//!
//! Values can be [`extracted`](Figment::extract()) into any value that
//! implements [`Deserialize`](serde::Deserialize). The [`Jail`] type allows for
//! semi-sandboxed configuration testing. The example below showcases
//! extraction and testing:
//!
//! ```rust
//! use serde::Deserialize;
//! use figment::{Figment, providers::{Format, Toml, Json, Env}};
//!
//! #[derive(Debug, PartialEq, Deserialize)]
//! struct AppConfig {
//!     name: String,
//!     count: usize,
//!     authors: Vec<String>,
//! }
//!
//! figment::Jail::expect_with(|jail| {
//!     jail.create_file("App.toml", r#"
//!         name = "Just a TOML App!"
//!         count = 100
//!     "#)?;
//!
//!     jail.create_file("App.json", r#"
//!         {
//!             "name": "Just a JSON App",
//!             "authors": ["figment", "developers"]
//!         }
//!     "#)?;
//!
//!     jail.set_env("APP_COUNT", 250);
//!
//!     // Sources are read _eagerly_: sources are read as soon as they are
//!     // merged/joined into a figment.
//!     let figment = Figment::new()
//!         .merge(Toml::file("App.toml"))
//!         .merge(Env::prefixed("APP_"))
//!         .join(Json::file("App.json"));
//!
//!     let config: AppConfig = figment.extract()?;
//!     assert_eq!(config, AppConfig {
//!         name: "Just a TOML App!".into(),
//!         count: 250,
//!         authors: vec!["figment".into(), "developers".into()],
//!     });
//!
//!     Ok(())
//! });
//! ```
//!
//! # Metadata
//!
//! Figment takes _great_ care to propagate as much information as possible
//! about configuration sources. All values extracted from a figment are
//! [tagged](crate::value::Tag) with the originating [`Metadata`] and
//! [`Profile`]. The tag is preserved across merges, joins, and errors, which
//! also include the [`path`](Error::path) of the offending key. Precise
//! tracking allows for rich error messages as well as ["magic"] values like
//! [`RelativePathBuf`], which automatically creates a path relative to the
//! configuration file in which it was declared.
//!
//! A [`Metadata`] consists of:
//!
//!   * The name of the configuration source.
//!   * An ["interpolater"](Metadata::interpolate()) that takes a path to a key
//!     and converts it into a provider-native key.
//!   * A [`Source`] specifying where the value was sourced from.
//!   * A code source [`Location`] where the value's provider was added to a
//!   [`Figment`].
//!
//! Along with the information in an [`Error`], this means figment can produce
//! rich error values and messages:
//!
//! ```text
//! error: invalid type: found string "hi", expected u16
//!  --> key `debug.port` in TOML file App.toml
//! ```
//!
//! [`RelativePathBuf`]: value::magic::RelativePathBuf
//! ["magic"]: value::magic
//! [`Location`]: std::panic::Location
//!
//! # Extracting and Profiles
//!
//! Providers _always_ [produce](Provider::data()) [`Dict`](value::Dict)s nested
//! in [`Profile`]s. A profile is [`selected`](Figment::select()) when
//! extracting, and the dictionary corresponding to that profile is deserialized
//! into the requested type. If no profile is selected, the
//! [`Default`](Profile::Default) profile is used.
//!
//! There are two built-in profiles: the aforementioned default profile and the
//! [`Global`](Profile::Global) profile. As the name implies, the default
//! profile contains default values for all profiles. The global profile _also_
//! contains values that correspond to all profiles, but those values supersede
//! values of any other profile _except_ the global profile, even when another
//! source is merged.
//!
//! Some providers can be configured as `nested`, which allows top-level keys in
//! dictionaries produced by the source to be treated as profiles. The following
//! example showcases profiles and nesting:
//!
//! ```rust
//! use serde::Deserialize;
//! use figment::{Figment, providers::{Format, Toml, Json, Env}};
//!
//! #[derive(Debug, PartialEq, Deserialize)]
//! struct Config {
//!     name: String,
//! }
//!
//! impl Config {
//!     // Note the `nested` option on both `file` providers. This makes each
//!     // top-level dictionary act as a profile.
//!     fn figment() -> Figment {
//!         Figment::new()
//!             .merge(Toml::file("Base.toml").nested())
//!             .merge(Toml::file("App.toml").nested())
//!     }
//! }
//!
//! figment::Jail::expect_with(|jail| {
//!     jail.create_file("Base.toml", r#"
//!         [default]
//!         name = "Base-Default"
//!
//!         [debug]
//!         name = "Base-Debug"
//!     "#)?;
//!
//!     // The default profile is used...by default.
//!     let config: Config = Config::figment().extract()?;
//!     assert_eq!(config, Config { name: "Base-Default".into(), });
//!
//!     // A different profile can be selected with `select`.
//!     let config: Config = Config::figment().select("debug").extract()?;
//!     assert_eq!(config, Config { name: "Base-Debug".into(), });
//!
//!     // Selecting non-existent profiles is okay as long as we have defaults.
//!     let config: Config = Config::figment().select("undefined").extract()?;
//!     assert_eq!(config, Config { name: "Base-Default".into(), });
//!
//!     // Replace the previous `Base.toml`. This one has a `global` profile.
//!     jail.create_file("Base.toml", r#"
//!         [default]
//!         name = "Base-Default"
//!
//!         [debug]
//!         name = "Base-Debug"
//!
//!         [global]
//!         name = "Base-Global"
//!     "#)?;
//!
//!     // Global values override all profile values.
//!     let config_def: Config = Config::figment().extract()?;
//!     let config_deb: Config = Config::figment().select("debug").extract()?;
//!     assert_eq!(config_def, Config { name: "Base-Global".into(), });
//!     assert_eq!(config_deb, Config { name: "Base-Global".into(), });
//!
//!     // Merges from succeeding providers take precedence, even for globals.
//!     jail.create_file("App.toml", r#"
//!         [debug]
//!         name = "App-Debug"
//!
//!         [global]
//!         name = "App-Global"
//!     "#)?;
//!
//!     let config_def: Config = Config::figment().extract()?;
//!     let config_deb: Config = Config::figment().select("debug").extract()?;
//!     assert_eq!(config_def, Config { name: "App-Global".into(), });
//!     assert_eq!(config_deb, Config { name: "App-Global".into(), });
//!
//!     Ok(())
//! });
//! ```
//!
//! # Crate Feature Flags
//!
//! To help with compilation times, types, modules, and providers are gated by
//! features. They are:
//!
//! | feature | gated namespace     | description                             |
//! |---------|---------------------|-----------------------------------------|
//! | `test`  | [`Jail`]            | Semi-sandboxed environment for testing. |
//! | `env`   | [`providers::Env`]  | Environment variable [`Provider`].      |
//! | `toml`  | [`providers::Toml`] | TOML file/string [`Provider`].          |
//! | `json`  | [`providers::Json`] | JSON file/string [`Provider`].          |
//! | `yaml`  | [`providers::Yaml`] | YAML file/string [`Provider`].          |
//!
//! # Built-In Providers
//!
//! In addition to the four gated providers, figment provides the following
//! providers out-of-the-box:
//!
//! | provider                              | description                            |
//! |---------------------------------------|----------------------------------------|
//! | [`providers::Serialized`]             | Source from any [`Serialize`] type.    |
//! | [`(impl AsRef<str>, impl Serialize)`] | Global source from a `("key", value)`. |
//! | [`&T` _where_ `T: Provider`]          | Source from `T` as a reference.        |
//!
//! [`Serialize`]: serde::Serialize
//! [`(impl AsRef<str>, impl Serialize)`]: Provider#impl-Provider-for-(K%2C%20V)
//! [`&T` _where_ `T: Provider`]: Provider#impl-Provider-for-%26%27_%20T
//!
//! # For Provider Authors
//!
//! The [`Provider`] trait documentation details extensively how to implement a
//! provider for Figment. For data format based providers, the [`Format`] trait
//! allows for even simpler implementations.
//!
//! [`Format`]: providers::Format
//!
//! # For Library Authors
//!
//! For libraries and frameworks that wish to expose customizable configuration,
//! we encourage the following structure:
//!
//! ```rust
//! use serde::{Serialize, Deserialize};
//!
//! use figment::{Figment, Provider, Error, Metadata, Profile};
//!
//! // The library's required configuration.
//! #[derive(Debug, Deserialize, Serialize)]
//! struct Config { /* the library's required/expected values */ }
//!
//! // The default configuration.
//! impl Default for Config {
//!     fn default() -> Self {
//!         Config { /* default values */ }
//!     }
//! }
//!
//! impl Config {
//!     // Allow the configuration to be extracted from any `Provider`.
//!     fn from<T: Provider>(provider: T) -> Result<Config, Error> {
//!         Figment::from(provider).extract()
//!     }
//!
//!     // Provide a default provider, a `Figment`.
//!     fn figment() -> Figment {
//!         use figment::providers::Env;
//!
//!         // In reality, whatever the library desires.
//!         Figment::from(Config::default()).merge(Env::prefixed("APP_"))
//!     }
//! }
//!
//! use figment::value::{Map, Dict};
//!
//! // Make `Config` a provider itself for composability.
//! impl Provider for Config {
//!     fn metadata(&self) -> Metadata {
//!         Metadata::named("Library Config")
//!     }
//!
//!     fn data(&self) -> Result<Map<Profile, Dict>, Error>  {
//!         figment::providers::Serialized::defaults(Config::default()).data()
//!     }
//!
//!     fn profile(&self) -> Option<Profile> {
//!         // Optionally, a profile that's selected by default.
//!         # None
//!     }
//! }
//! ```
//!
//! This structure has the following properties:
//!
//!   * The library provides a `Config` structure that clearly indicates which
//!     values the library requires.
//!   * Users can completely customize configuration via their own [`Provider`].
//!   * The library's `Config` is itself a [`Provider`] for composability.
//!   * The library provides a `Figment` which it will use as the default
//!     configuration provider.
//!
//! `Config::from(Config::figment())` can be used as the library default while
//! allowing complete customization of the configuration sources. Developers
//! building on the library can base their figments on `Config::default()`,
//! `Config::figment()`, both or neither.
//!
//! For frameworks, a top-level structure should expose the `Figment` that was
//! used to extract the `Config`, allowing other libraries making use of the
//! framework to also extract values from the same `Figment`:
//!
//! ```rust,no_run
//! use figment::{Figment, Provider, Error};
//! # struct Config;
//! # impl Config {
//! #     fn figment() -> Figment { panic!() }
//! #     fn from<T: Provider>(_: T) -> Result<Config, Error> { panic!() }
//! # }
//!
//! struct App {
//!     /// The configuration.
//!     pub config: Config,
//!     /// The figment used to extract the configuration.
//!     pub figment: Figment,
//! }
//!
//! impl App {
//!     pub fn new() -> Result<App, Error> {
//!         App::custom(Config::figment())
//!     }
//!
//!     pub fn custom<T: Provider>(provider: T) -> Result<App, Error> {
//!         let figment = Figment::from(provider);
//!         Ok(App { config: Config::from(&figment)?, figment })
//!     }
//! }
//! ```
//!
//! # For Application Authors
//!
//! As an application author, you'll need to make at least the following
//! decisions:
//!
//!   1. The sources you'll accept configuration from.
//!   2. The precedence you'll apply to each source.
//!   3. Whether you'll use profiles or not.
//!
//! For special sources, you may find yourself needing to implement a custom
//! [`Provider`]. As with libraries, you'll likely want to provide default
//! values where possible either by providing it to the figment or by using
//! [serde's defaults](https://serde.rs/attr-default.html). Then, it's simply a
//! matter of declaring a figment and extracting the configuration from it.
//!
//! A reasonable starting point might be:
//!
//! ```rust
//! use serde::{Serialize, Deserialize};
//! use figment::{Figment, providers::{Env, Format, Toml, Serialized}};
//!
//! #[derive(Deserialize, Serialize)]
//! struct Config {
//!     key: String,
//!     another: u32
//! }
//!
//! impl Default for Config {
//!     fn default() -> Config {
//!         Config {
//!             key: "default".into(),
//!             another: 100,
//!         }
//!     }
//! }
//!
//! Figment::from(Serialized::defaults(Config::default()))
//!     .merge(Toml::file("App.toml"))
//!     .merge(Env::prefixed("APP_"));
//! ```
//!
//! # Tips
//!
//! Some things to remember when working with Figment:
//!
//!   * Merging and joining are _eager_: sources are read immediately. It's
//!     useful to define a function that returns a `Figment`.
//!   * The [`util`] modules contains helpful serialize and deserialize
//!     implementations for defining `Config` structures.
//!   * The [`Format`] trait makes implementing data-format based [`Provider`]s
//!     straight-forward.
//!   * [`Magic`](value::magic) values can significantly reduce the need to
//!     inspect a `Figment` directly.
//!   * [`Jail`] makes testing configurations straight-forward and much less
//!     error-prone.
//!   * [`Error`] may contain more than one error: iterate over it to retrieve
//!     all errors.

pub mod value;
pub mod providers;
pub mod error;
pub mod util;
mod figment;
mod profile;
mod coalesce;
mod metadata;
mod provider;

#[cfg(any(test, feature = "test"))] mod jail;
#[cfg(any(test, feature = "test"))] pub use jail::Jail;

#[doc(inline)]
pub use error::Error;
pub use self::figment::Figment;
pub use profile::Profile;
pub use provider::*;
pub use metadata::*;