mirror of
https://github.com/OMGeeky/confique.git
synced 2026-01-19 01:45:28 +01:00
Add more documentation to Config trait and derive
This commit is contained in:
175
src/lib.rs
175
src/lib.rs
@@ -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)?;
|
||||
|
||||
Reference in New Issue
Block a user