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}