confique/
lib.rs

1//! Confique is a type-safe, layered, light-weight, `serde`-based configuration library.
2//!
3//! The core of the library is the [`Config`] trait and [its derive-macro][macro@Config].
4//! You define your configuration value as one or more structs, each of which has
5//! to `#[derive(Config)]`. Then you can use different ways of loading an instance
6//! of your root configuration struct.
7//!
8//!
9//! # How to use
10//!
11//! Add `confique` as dependency to your `Cargo.toml` and remember to enable the
12//! crate features for file formats you are interested in. For example:
13//! `cargo add confique --features=toml`.
14//!
15//! ## Defining your configuration with structs
16//!
17//! First, define some structs that describe all your configuration values. Use
18//! the types you want to use in your code. For example, if you have a `port`
19//! config and your code needs that value, it should be of type `u16`,
20//! and *not* `Option<u16>` or `String`. That way, the code using that value is
21//! cleanest.
22//!
23//! Small example:
24//!
25//! ```
26//! use confique::Config;
27//!
28//! #[derive(Config)]
29//! struct Conf {
30//!     // A required value. Since it's not `Option<_>`, it has to be specified when
31//!     // loading the configuration, or else loading returns an error.
32//!     username: String,
33//!
34//!     // An optional value.
35//!     welcome_message: Option<String>,
36//!
37//!     // A required value with default value. If no other value is specified
38//!     // (e.g. in a config file), the default value is used.
39//!     #[config(default = 8080)]
40//!     port: u16,
41//! }
42//! # fn main() {}
43//! ```
44//!
45//! As your application grows, oftentimes you want to split the configuration
46//! into multiple structs. This has the added benefit that your config files
47//! are somewhat structured or have sections. You can do that by including
48//! other types that implement `Config` with `#[config(nested)]`.
49//!
50//! ```
51//! use std::path::PathBuf;
52//! use confique::Config;
53//!
54//! #[derive(Config)]
55//! struct Conf {
56//!     username: String,
57//!
58//!     #[config(nested)]
59//!     log: LogConf,
60//!
61//!     #[config(nested)]
62//!     db: DbConf,
63//! }
64//!
65//! #[derive(Config)]
66//! struct LogConf {
67//!     #[config(default = true)]
68//!     stdout: bool,
69//!
70//!     file: Option<PathBuf>,
71//! }
72//!
73//! #[derive(Config)]
74//! struct DbConf {
75//!     // ...
76//! }
77//! # fn main() {}
78//! ```
79//!
80//! You can also attach some other attributes to fields. For example, with
81//! `#[config(env = "KEY")]`, you can load a value from an environment variable.
82//! With `#[config(validate = ...)]` you can add validation checks. For more
83//! information, see the [docs for the derive macro][macro@Config].
84//!
85//! Note: if a field hast `#[config(nested)]`, its type must implement
86//! [`Config`], otherwise it must implement `serde::Deserialize`.
87//!
88//!
89//! ## Loading the configuration
90//!
91//! Here, you have multiple options. Most of the time, you can probably use the
92//! provided high-level methods of [`Config`], like [`Config::from_file`] and
93//! [`Config::builder`].
94//!
95//! ```
96//! use confique::Config;
97//!
98//! # #[derive(Config)]
99//! # struct Conf {}
100//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
101//! // Load from a single file only.
102//! # #[cfg(feature = "toml")]
103//! let config = Conf::from_file("config.toml")?;
104//!
105//! // Or load from multiple sources (higher priority sources are listed first).
106//! # #[cfg(feature = "toml")]
107//! let config = Conf::builder()
108//!     .env()
109//!     .file("config.toml")
110//!     .file("/etc/myapp/config.toml")
111//!     .load()?;
112//! # Ok(())
113//! # }
114//! ```
115//!
116//! But you can also assemble your configuration yourself. That's what
117//! the *layer* types are for (i.e. [`Config::Layer`]). These implement
118//! `serde::Deserialize` and can thus be loaded from a vast number of sources.
119//! One of those sources is the built-in [`File`] which gives you a bit more
120//! control when loading configuration from files. And you can always simply
121//! create an instance of the layer type by writing all values in Rust code
122//! with struct initializer syntax!
123//!
124//! Once you have all your layers collected, you have to combine
125//! them via [`Layer::with_fallback`] and convert them to the actual config
126//! type via [`Config::from_layer`]. And you probably also want to use
127//! [`Layer::default_values`] as the last layer.
128//!
129//! ```no_run
130//! # #[cfg(not(feature = "toml"))]
131//! # fn main() {}
132//! # #[cfg(feature = "toml")]
133//! # fn main() -> Result<(), confique::Error> {
134//! use confique::{Config, File, FileFormat, Layer};
135//!
136//! #[derive(Config)]
137//! struct Conf {
138//!     foo: f32,
139//! }
140//!
141//! type ConfLayer = <Conf as Config>::Layer;
142//! let from_file: ConfLayer = File::with_format("/etc/foo/config", FileFormat::Toml)
143//!     .required()
144//!     .load()?;
145//! let manual = ConfLayer {
146//!     // Remember: all fields in the layer types are `Option`s!
147//!     foo: Some(3.14),
148//! };
149//! let defaults = ConfLayer::default_values();
150//!
151//! let merged = from_file.with_fallback(manual).with_fallback(defaults);
152//! let config = Conf::from_layer(merged)?;
153//! # Ok(())
154//! # }
155//! ```
156//!
157//! ## Using your configuration
158//!
159//! Well, this is the simple part: the loaded configuration is just an instance
160//! of your struct. And you already know how to access fields of structs!
161//!
162//!
163//! # Other notes
164//!
165//! - To use CLI as a layer when loading your configuration, see the `clap`
166//!   example!
167//!
168//! # Cargo features
169//!
170//! This crate has a Cargo feature for each supported file format. These are not
171//! enabled by default, so you have to specify which file formats you are
172//! interested in.
173//!
174//! ```toml
175//! confique = { version = "...", features = ["toml"] }
176//! ```
177//!
178//! All crate features:
179//!
180//! - `toml`: enables TOML support and adds the `toml` dependency.
181//! - `yaml`: enables YAML support and adds the `serde_yaml` dependency.
182//! - `json5`: enables JSON5 support and adds the `json5` dependency.
183
184use serde::Deserialize;
185
186#[doc(hidden)]
187pub mod internal;
188
189mod builder;
190pub mod env;
191mod error;
192pub mod meta;
193
194#[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))]
195mod file;
196
197#[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))]
198mod template;
199
200#[cfg(feature = "json5")]
201pub mod json5;
202
203#[cfg(feature = "toml")]
204pub mod toml;
205
206#[cfg(feature = "yaml")]
207pub mod yaml;
208
209#[cfg(test)]
210mod test_utils;
211
212
213pub use serde;
214pub use self::{
215    builder::Builder,
216    error::Error,
217};
218
219#[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))]
220pub use crate::{
221    file::{File, FileFormat},
222    template::FormatOptions,
223};
224
225
226/// Derives (automatically implements) [`Config`] for a struct.
227///
228/// This only works for structs with named fields (i.e. not for tuple structs,
229/// unit structs, enums, or unions). This macro only works sometimes inside of
230/// functions (as it generates a module and symbol resolution is weird in that
231/// case); if you get weird errors "symbol not found", just move the struct
232/// definition outside of the function.
233///
234/// # Quick example
235///
236/// ```
237/// use confique::Config;
238/// use std::net::IpAddr;
239///
240/// #[derive(Config)]
241/// struct Conf {
242///     color: Option<String>,
243///
244///     #[config(nested)]
245///     http: HttpConf,
246/// }
247///
248/// #[derive(Config)]
249/// struct HttpConf {
250///     #[config(env = "APP_PORT")]
251///     port: u16,
252///
253///     #[config(default = "127.0.0.1")]
254///     bind: IpAddr,
255///
256///     #[config(default = ["x-user", "x-password"])]
257///     headers: Vec<String>,
258/// }
259/// # fn main() {}
260/// ```
261///
262/// This derives `Config` for the two structs.
263///
264/// - `HttpConf::port` can be loaded from the environment variable `APP_PORT`.
265/// - `HttpConf::bind` has a default value of `127.0.0.1` (the string is turned
266///   into the `IpAddr` via its `Deserialize` impl). Thus a value for this
267///   field does not need to be present when loading configuration.
268/// - `Conf::color` is optional and does not need to be present when loading the
269///   configuration.
270///
271///
272/// # How to use
273///
274/// There are two types of fields distinguished by this macro: nested and leaf
275/// fields.
276///
277/// - **Nested fields**: they have to be annotated with `#[config(nested)]` and
278///   contain a nested configuration object. The type of this field must
279///   implement `Config`. As implied by the previous statement, `Option<_>` as
280///   type for nested fields is not allowed.
281///
282/// - **Leaf fields**: all fields *not* annotated with `#[config(nested)]`,
283///   these contain your actual values. The type of such a field has to
284///   implement `serde::Deserialize` or you have to add a `deserialize_with`
285///   attribute.
286///
287/// Doc comments on the struct and the individual fields are interpreted and
288/// stored in [`Meta`][meta::Meta]. They are used in the formatting functions
289/// (e.g. `toml::format`).
290///
291/// ## Special types for leaf fields
292///
293/// These types give a different meaning/semantic to the field. Please note that
294/// due to the limitations of derive macros, the type is checked *literally*.
295/// So it won't work if you rename symbols or use full paths.
296///
297/// - **`Option<T>`**: this marks the field as an optional field. All other
298///   fields are non-optional and will raise an error if while loading the
299///   configuration, no value has been set for them. Optional fields cannot have
300///   a `#[config(default = ...)]` attribute as that would not make sense. Note:
301///   the (unqualified) symbol `Option` must be in scope and refer to
302///   `std::option::Option`.
303///
304///
305/// ## Field Attributes
306///
307/// The following attributes can be attached to struct fields.
308///
309/// ### `default`
310///
311/// ```ignore
312/// #[config(default = ...)]
313/// ```
314///
315/// Sets a default value for this field. This is returned by
316/// [`Layer::default_values`] and, in most circumstances, used as a
317/// last "layer" to pull values from that have not been set in a layer of
318/// higher-priority. Currently, the following expressions are allowed:
319///
320/// - Booleans, e.g. `default = true`
321/// - Integers, e.g. `default = 900`
322/// - Floats, e.g. `default = 3.14`
323/// - Strings, e.g. `default = "fox"`
324/// - Arrays, e.g. `default = ["foo", "bar"]`
325/// - Key value maps, e.g. `default = { "cat": 3.14, "bear": 9.0 }`
326///
327/// Map keys can be Booleans, integers, floats, and strings. For array and map
328/// values, you can use any of the expressions in the list above (i.e. you
329/// can nest arrays/maps).
330///
331/// The field value is deserialized from the specified default value
332/// (via `serde::de::IntoDeserializer`). So the expression after `default =`
333/// is often not the same Rust type as your field. For example, you can have
334/// `#[config(default = "/foo/bar")]` on the field `path: PathBuf`. This
335/// works fine as `PathBuf` can be deserialized from a string. (Also see the
336/// `IpAddr` field in the example above.)
337///
338/// If you use an integer or float literal without type suffix, `confique` has
339/// to infer the exact type from the type of the field. This should work in
340/// most cases (`u8`, `f32`, `Vec<i16>`, `[f64; 3]`, ...), but this type
341/// inference is very basic, not even close to what Rust can do. If confique
342/// cannot figure out the type, it defaults to `i32` for integers and `f64`
343/// for floats (like Rust does). If that causes problems for you, just add a
344/// type suffix, e.g. `default = 800u32`.
345///
346/// ### `env`
347///
348/// ```ignore
349/// #[config(env = "KEY")]
350/// ```
351///
352/// Assigns an environment variable to this field. In [`Layer::from_env`], the
353/// variable is checked and deserialized into the field if present.
354///
355/// If the env var is set to an empty string and if the field fails to
356/// parse/deserialize/validate, it is treated as unset.
357///
358/// ### `parse_env`
359///
360/// ```ignore
361/// #[config(parse_env = path::to::function)]
362/// ```
363///
364/// Function used to parse environment variables. Mostly useful if you need to
365/// parse lists or other complex objects from env vars. Function needs
366/// signature `fn(&str) -> Result<T, impl std::error::Error>` where `T` is the
367/// type of the field. Can only be present if the `env` attribute is present.
368/// Also see [`env::parse`].
369///
370/// #### `deserialize_with`
371///
372/// ```ignore
373/// #[config(deserialize_with = path::to::function)]
374/// ```
375///
376/// Like [serde's `deserialize_with` attribute][serde-deser].
377///
378/// [serde-deser]: https://serde.rs/field-attrs.html#deserialize_with
379///
380/// #### `validate`
381///
382/// ```ignore
383/// #[config(validate = path::to::function)]
384/// // or
385/// #[config(validate(<expr>, "msg"))]
386/// ```
387///
388/// Adds a validation to the field, i.e. a check that must succeed to be able to
389/// load the configuration. The validator is called as part of the
390/// deserialization, and is thus executed for all layers, not just for the
391/// merged configuration. The attribute can be specified multiple times.
392///
393/// > *Note*: remember ["Parse, don't validate"][parse-not-validate]! If you can
394///    reasonably represent your validation logic as a type, you should use
395///    that type instead of validating a weakly-typed field. Example: if your
396///    config value is an IP-address, use the dedicated `std::net::IpAddr` as
397///    field type (can be deserialized from strings) instead of a `String`
398///    field with a `validate` function making sure it's a valid IP-address.
399/// >
400/// > ```ignore
401/// > // GOOD
402/// > addr: std::net::IpAddr,
403/// >
404/// > // BAD
405/// > #[config(validate(addr.parse::<std::net::IpAddr>().is_ok(), "not a valid IP-address"))]
406/// > addr: String,
407/// > ```
408///
409/// [parse-not-validate]: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
410///
411/// The `validate = path::to::function` syntax expects a function that is
412/// callable as `Fn(&T) -> Result<(), E>` where `T` is the (non-optional) type
413/// of the field and `E` can be any type implementing `fmt::Display` (e.g. just
414/// `&str`). Example:
415///
416/// ```
417/// use confique::Config;
418///
419/// #[derive(Config)]
420/// struct Conf {
421///     #[config(validate = is_valid_user)]
422///     user: Option<String>,
423/// }
424///
425/// fn is_valid_user(user: &String) -> Result<(), &'static str> {
426///     if user == "root" {
427///         return Err("user 'root' is not allowed");
428///     }
429///     if !user.is_ascii() {
430///         return Err("user must be an ASCII string");
431///     }
432///     Ok(())
433/// }
434/// # fn main() {}
435/// ```
436///
437/// The `validate(<expr>, "msg")` syntax is only for convenience and intended
438/// for simple cases. It works similar to the `assert!` macro as it expects an
439/// expression validating to `bool` and a string error message. The expression
440/// can access the field value by reference via the field's name. If the
441/// expression validates to `false`, this is treated as a validation error.
442/// Examples:
443///
444/// ```
445/// use confique::Config;
446///
447/// #[derive(Config)]
448/// struct Conf {
449///     #[config(
450///         validate(!name.is_empty(), "name must not be empty"),
451///         validate(name.is_ascii(), "name must be ASCII"),
452///     )]
453///     name: String,
454///
455///     #[config(validate(*port >= 1024, "cannot use ports < 1024 as non-root user"))]
456///     port: Option<u16>,
457/// }
458/// ```
459///
460///
461/// ## Struct attributes
462///
463/// The following attributes can be attached to the struct itself.
464///
465/// #### `validate`
466///
467/// ```ignore
468/// #[config(validate = path::to::function)]
469/// ```
470///
471/// Adds a validation to the config struct, i.e. a check that must suceed to be
472/// able to load the configuration. The validator is called inside
473/// `Config::from_layer`, i.e. only after all layers have been merged.
474///
475/// The given`path::to::function` is expected to be a function callable as
476/// `Fn(&T) -> Result<(), E>` where `T` is the struct type (`Self`) and `E` can
477/// be any type implementing `fmt::Display` (e.g. just `&str`). Example:
478///
479/// ```
480/// use confique::Config;
481///
482/// #[derive(Config)]
483/// #[config(validate = Self::validate)]
484/// struct ColorMixConfig {
485///     source_weight: f32,
486///     target_weight: f32,
487/// }
488///
489/// impl ColorMixConfig {
490///     fn validate(&self) -> Result<(), &'static str> {
491///         if self.source_weight + self.target_weight > 1.0 {
492///             return Err("sum of weights must not exceed 1");
493///         }
494///         Ok(())
495///     }
496/// }
497/// # fn main() {}
498/// ```
499///
500/// ### `layer_attr`
501///
502/// ```ignore
503/// #[config(layer_attr(...))]
504/// ```
505///
506/// Specify attributes that should be attached to the layer struct definition.
507/// For example, `#[config(layer_attr(derive(Clone)))]` can be used to make
508/// the layer type implement `Clone`.
509///
510/// This attribute can also be applied to struct fields.
511///
512///
513/// # What the macro generates
514///
515/// This macro emits one `impl confique::Config for … { … }` block. But in order
516/// to implement that trait, a *layer type* of your struct is also generated.
517/// That layer type lives in its own module and derives `serde::Deserialize`.
518///
519/// The example in the "Quick example" section above would expand to something
520/// like this:
521///
522/// ```ignore
523/// // ----- Generated for `Conf` -----
524/// impl confique::Config for Conf {
525///     type Layer = confique_conf_layer::ConfLayer;
526///     ...
527/// }
528/// mod confique_conf_layer {
529///     #[derive(serde::Deserialize)]
530///     pub(super) struct ConfLayer {
531///         pub(super) color: Option<String>,
532///
533///         #[serde(default = "confique::Layer::empty")]
534///         pub(super) http: <HttpConf as confique::Config>::Layer,
535///     }
536///
537///     impl confique::Layer for ConfLayer { ... }
538/// }
539///
540/// // ----- Generated for `HttpConf` -----
541/// impl confique::Config for HttpConf {
542///     type Layer = confique_http_conf_layer::HttpConfLayer;
543///     ...
544/// }
545/// mod confique_http_conf_layer {
546///     #[derive(serde::Deserialize)]
547///     pub(super) struct HttpConfLayer {
548///         pub(super) port: Option<u16>,
549///         pub(super) bind: Option<IpAddr>,
550///         pub(super) headers: Option<Vec<String>>,
551///     }
552///
553///     impl confique::Layer for HttpConfLayer { ... }
554/// }
555/// ```
556pub use confique_macro::Config;
557
558
559/// A configuration object that can be deserialized in layers via `serde`.
560///
561/// You would usually derive this trait for your own type and then load the
562/// configuration with one of the provided methods, like
563/// [`from_file`][Self::from_file] or [`builder`](Self::builder).
564///
565/// # Deriving
566///
567/// This trait is usually derived as implementing it manually usually entails
568/// writing some repetitive boilerplate code, that goes against the "don't
569/// repeat yourself" principle. See [the documentation of the derive
570/// macro][macro@Config] for more information!
571pub trait Config: Sized {
572    /// A layer of `Self` (a potentially partial configuration).
573    ///
574    /// This type is supposed to have the exact same fields as this one, but
575    /// with every field being optional. Its main use is to have a layered
576    /// configuration from multiple sources where each layer might not contain
577    /// all required values. The only thing that matters is that combining all
578    /// layers will result in a configuration object that has all required
579    /// values defined.
580    type Layer: Layer;
581
582    /// A description of this configuration.
583    ///
584    /// This is a runtime representation from the struct definition of your
585    /// configuration type.
586    const META: meta::Meta;
587
588    /// Tries to create `Self` from a layer and validates itself.
589    ///
590    /// An [`Error`] is returned if:
591    /// - any required values are not defined in `layer`, or
592    /// - the struct validation fails (see `validate` attribute on derive macro)
593    fn from_layer(layer: Self::Layer) -> Result<Self, Error>;
594
595    /// Convenience builder to configure, load and merge multiple configuration
596    /// sources. **Sources specified earlier have a higher priority**; later
597    /// sources only fill in the gaps. After all sources have been loaded, the
598    /// default values (usually specified with `#[default = ...]`) are merged
599    /// (with the lowest priority).
600    ///
601    /// # Example
602    ///
603    /// In the following example, configuration is first loaded from environment
604    /// variables, then from `app.toml`, then from `/etc/app/config.toml` and
605    /// finally from the configured default values. Values found earlier in
606    /// this list have precedence.
607    ///
608    /// ```
609    /// use confique::Config;
610    ///
611    /// #[derive(Config)]
612    /// struct Conf {
613    ///     #[config(env = "APP_PORT", default = 8080)]
614    ///     port: u16,
615    /// }
616    ///
617    /// #[cfg(feature = "toml")]
618    /// let conf = Conf::builder()
619    ///     .env()
620    ///     .file("app.toml")
621    ///     .file("/etc/app/config.toml")
622    ///     .load();
623    /// ```
624    fn builder() -> Builder<Self> {
625        Builder::new()
626    }
627
628
629    /// Load the configuration from a single file.
630    ///
631    /// If you rather want to load from multiple sources, use
632    /// [`Config::builder`]. Infers the file format from the file extension.
633    /// Returns an error in these cases:
634    ///
635    /// - The path does not have a known file extension.
636    /// - Loading the file fails.
637    /// - The file does not specify all required configuration values.
638    ///
639    /// # Example
640    ///
641    /// ```
642    /// use confique::Config;
643    ///
644    /// #[derive(Config)]
645    /// struct Conf {
646    ///     port: u16,
647    /// }
648    ///
649    /// let conf = Conf::from_file("config.toml");
650    /// ```
651    #[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))]
652    fn from_file(path: impl Into<std::path::PathBuf>) -> Result<Self, Error> {
653        let default_values = Self::Layer::default_values();
654        let mut file = File::new(path)?;
655        if !default_values.is_complete() {
656            file = file.required();
657        }
658
659        Self::from_layer(file.load::<Self::Layer>()?.with_fallback(default_values))
660    }
661}
662
663/// A configuration layer: all fields are optional. Can be directly deserialized
664/// via `serde`.
665pub trait Layer: for<'de> Deserialize<'de> {
666    /// Returns `Self` where all fields/values are `None` or empty.
667    fn empty() -> Self;
668
669    /// Returns an object containing all default values (i.e. set via
670    /// `#[config(default = ...)]` when deriving `Config`) with all remaining
671    /// values/fields set to `None`/being empty.
672    fn default_values() -> Self;
673
674    /// Loads values from environment variables. This is only relevant for
675    /// fields annotated with `#[config(env = "...")]`: all fields not
676    /// annotated `env` will be `None`.
677    ///
678    /// If the env variable corresponding to a field is not set, that field is
679    /// `None`. If it is set and non-empty, but it failed to deserialize into
680    /// the target type, an error is returned. If set to an empty string *and*
681    /// if it fails to deserialize, it's treated as not set.
682    fn from_env() -> Result<Self, Error>;
683
684    /// Combines two layers. `self` has a higher priority; missing values in
685    /// `self` are filled with values in `fallback`, if they exist. The
686    /// semantics of this method is basically like in [`Option::or`].
687    fn with_fallback(self, fallback: Self) -> Self;
688
689    /// Returns `true` if all values are unspecified/`None`.
690    fn is_empty(&self) -> bool;
691
692    /// Returns `true` if all required (non-optional) values in this
693    /// configuration are set. If this returns `true`, [`Config::from_layer`]
694    /// will not return an error.
695    fn is_complete(&self) -> bool;
696}