Add more documentation to Config trait and derive

This commit is contained in:
Lukas Kalbertodt
2021-07-27 16:15:05 +02:00
parent 78b2db26e1
commit a86ab50588

View File

@@ -20,7 +20,6 @@ pub mod yaml;
pub use serde;
pub use confique_macro::Config;
pub use self::{
builder::Builder,
error::Error,
@@ -28,20 +27,147 @@ pub use self::{
};
/// Derives (automatically implements) [`Config`] for a struct.
///
/// This only works for structs with named fields, but not for tuple structs,
/// unit structs, enums, or unions.
///
/// # Quick example
///
/// ```
/// use confique::Config;
/// use std::net::IpAddr;
///
/// #[derive(Config)]
/// struct Conf {
/// color: Option<String>,
///
/// #[config(nested)]
/// http: HttpConf,
/// }
///
/// #[derive(Config)]
/// struct HttpConf {
/// #[config(env = "APP_PORT")]
/// port: u16,
///
/// #[config(default = "127.0.0.1")]
/// bind: IpAddr,
/// }
/// ```
///
/// This derives `Config` for the two structs.
///
/// - `HttpConf::port` can be loaded from the environment variable `APP_PORT`.
/// - `HttpConf::bind` has a default value of `127.0.0.1` (the string is turned
/// into the `IpAddr` via its `Deserialize` impl). Thus a value for this
/// field does not need to be present when loading configuration.
/// - `Conf::color` is optional and does not need to be present when loading the
/// configuration.
///
///
/// # How to use
///
/// There are two types of fields distinguished by this macro: nested and leaf
/// fields.
///
/// - **Nested fields**: they have to be annotated with `#[config(nested)]` and
/// contain a nested configuration object. The type of this field must
/// implement `Config`. As implied by the previous statement, `Option<_>` as
/// type for nested fields is not allowed.
///
/// - **Leaf fields**: all fields *not* annotated with `#[config
/// (nested)]`, these contain your actual values. The type of such a field
/// has to implement `serde::Deserialize`.
///
/// Doc comments on the struct and the individual fields are interpreted and
/// stored in [`Meta`]. They are used in the formatting functions
/// (e.g. `toml::format`).
///
///
/// ## Attributes
///
/// This macro currently recognizes the following attributes for leaf fields:
///
/// - **`#[config(default = ...)]`**: sets a default value for this field. This
/// is returned by [`Partial::default_values`] and, in most circumstances,
/// used as a last "layer" to pull values from that have not been set in a
/// layer of higher-priority. Currently, Boolean, float, integer and string
/// values are allowed.
///
/// - **`#[config(env = "KEY")]`**: assigns an environment variable to this
/// field. In [`Partial::from_env`], the variable is checked and
/// deserialized into the field if present.
///
///
/// ## Special types for leaf fields
///
/// These types give a different meaning/semantic to the field. Please note that
/// due to the limitations of derive macros, the type is checked *literally*.
/// So it won't work if you rename symbols or used full paths.
///
/// - **`Option<T>`**: this marks the field as an optional field. All other
/// fields are non-optional and will raise an error if while loading the
/// configuration, no value has been set for them.
///
///
///
/// # What the macro generates
///
/// This macro emits one `impl confique::Config for … { … }` block. But in order
/// to implement that trait, a *partial type* of your struct is also generated.
/// That partial type lives in its own module and derives
/// `serde::Deserialize`.
///
/// The example in the "Quick example" section above would expand to something
/// like this:
///
/// ```ignore
/// impl confique::Config for Conf {
/// type Partial = confique_partial_conf::PartialConf;
/// ...
/// }
/// mod confique_partial_conf {
/// #[derive(serde::Deserialize)]
/// pub(super) struct PartialConf {
/// pub(super) color: Option<String>,
///
/// #[serde(default = "confique::Partial::empty")]
/// pub(super) http: <HttpConf as confique::Config>::Partial,
/// }
///
/// impl confique::Partial for PartialConf { ... }
/// }
///
/// impl confique::Config for HttpConf {
/// type Partial = confique_partial_http_conf::PartialHttpConf;
/// ...
/// }
/// mod confique_partial_http_conf {
/// #[derive(serde::Deserialize)]
/// pub(super) struct PartialHttpConf {
/// pub(super) port: Option<u16>,
/// pub(super) bind: Option<IpAddr>,
/// }
///
/// impl confique::Partial for PartialHttpConf { ... }
/// }
/// ```
pub use confique_macro::Config;
/// A configuration object that can be deserialized in layers via `serde`.
///
/// You would usually derive this trait for your own type and then load the
/// configuration with one of the provided methods, like
/// [`from_sources`][Self::from_sources].
/// [`from_file`][Self::from_file] or [`builder`](Self::builder).
///
/// # Deriving
///
/// This trait is usually derived as implementing it manually usually entails
/// writing some boilerplate code, that goes against the "don't repeat yourself"
/// principle.
///
/// TODO
/// writing some repetitive boilerplate code, that goes against the "don't
/// repeat yourself" principle. See [the documentation of the derive
/// macro][macro@Config] for more information!
pub trait Config: Sized {
/// A version of `Self` that represents a potetially partial configuration.
///
@@ -71,11 +197,33 @@ pub trait Config: Sized {
/// default values (usually specified with `#[default = ...]`) are merged
/// (with the lowest priority).
///
/// TODO: Example
/// # Example
///
/// In the following example, configuration is first loaded from environment
/// variables, then from `app.toml`, then from `/etc/app/config.toml` and
/// finally from the configured default values. Values found earlier in
/// this list have precedence.
///
/// ```
/// use confique::Config;
///
/// #[derive(Config)]
/// struct Conf {
/// #[config(env = "APP_PORT", default = 8080)]
/// port: u16,
/// }
///
/// let conf = Conf::builder()
/// .env()
/// .file("app.toml")
/// .file("/etc/app/config.toml")
/// .load();
/// ```
fn builder() -> Builder<Self> {
Builder::new()
}
/// Load the configuration from a single file.
///
/// If you rather want to load from multiple sources, use
@@ -86,7 +234,18 @@ pub trait Config: Sized {
/// - Loading the file fails.
/// - The file does not specify all required configuration values.
///
/// TODO: Example
/// # Example
///
/// ```
/// use confique::Config;
///
/// #[derive(Config)]
/// struct Conf {
/// port: u16,
/// }
///
/// let conf = Conf::from_file("config.toml");
/// ```
fn from_file(path: impl Into<PathBuf>) -> Result<Self, Error> {
let default_values = Self::Partial::default_values();
let mut file = File::new(path)?;