figment/
provider.rs

1use crate::{Profile, Error, Metadata};
2use crate::value::{Tag, Map, Dict};
3
4/// Trait implemented by configuration source providers.
5///
6/// For an overview of built-in providers, see the [top-level
7/// docs](crate#built-in-providers).
8///
9/// # Overview
10///
11/// A [`Provider`] reads from a source to provide configuration data for
12/// [`Figment`]s ([`Provider::data()`]). A `Provider` also provides [`Metadata`]
13/// to identify the source of its configuration data ([`Provider::metadata()`]).
14/// A provider may also optionally set a `Profile` for the `Figment` it is
15/// merged (but not joined) into by implementing [`Provider::profile()`].
16///
17/// # Nesting
18///
19/// A [`Provider`] meant to be consumed externally should allow for optional
20/// [nesting](crate#extracting-and-profiles) when sensible. The general pattern
21/// is to allow a `Profile` to be specified. If one is not, read the
22/// configuration data as a `Map<Profile, Dict>`, thus using the top-level keys
23/// as profiles. If one _is_ specified, read the data as `Dict` and
24/// [`Profile::collect()`] into the specified profile.
25///
26/// # Example
27///
28/// Implementing a `Provider` requires implementing methods that provide both of
29/// these pieces of data. The first, [`Provider::metadata()`] identifies the
30/// provider's configuration sources, if any, and allows the provider to
31/// customize how paths to keys are interpolated. The second,
32/// [`Provider::data()`], actually reads the configuration and returns the data.
33///
34/// As an example, consider a provider that reads configuration from a
35/// networked store at some `Url`. A `Provider` implementation for such a
36/// provider may resemble the following:
37///
38/// ```rust,no_run
39/// # use serde::Deserialize;
40/// use figment::{Provider, Metadata, Profile, Error, value::{Map, Dict}};
41///
42/// # type Url = String;
43/// /// A provider that fetches its data from a given URL.
44/// struct NetProvider {
45///     /// The profile to emit data to if nesting is disabled.
46///     profile: Option<Profile>,
47///     /// The url to fetch data from.
48///     url: Url
49/// };
50///
51/// impl Provider for NetProvider {
52///     /// Returns metadata with kind `Network`, custom source `self.url`,
53///     /// and interpolator that returns a URL of `url/a/b/c` for key `a.b.c`.
54///     fn metadata(&self) -> Metadata {
55///         let url = self.url.clone();
56///         Metadata::named("Network")
57///             .source(self.url.as_str())
58///             .interpolater(move |profile, keys| match profile.is_custom() {
59///                 true => format!("{}/{}/{}", url, profile, keys.join("/")),
60///                 false => format!("{}/{}", url, keys.join("/")),
61///             })
62///     }
63///
64///     /// Fetches the data from `self.url`. Note that `Dict`, `Map`, and
65///     /// `Profile` are `Deserialize`, so we can deserialized to them.
66///     fn data(&self) -> Result<Map<Profile, Dict>, Error> {
67///         fn fetch<'a, T: Deserialize<'a>>(url: &Url) -> Result<T, Error> {
68///             /* fetch from the network, deserialize into `T` */
69///             # todo!()
70///         }
71///
72///         match &self.profile {
73///             // Don't nest: `fetch` into a `Dict`.
74///             Some(profile) => Ok(profile.collect(fetch(&self.url)?)),
75///             // Nest: `fetch` into a `Map<Profile, Dict>`.
76///             None => fetch(&self.url),
77///         }
78///     }
79/// }
80/// ```
81///
82/// [`Figment`]: crate::Figment
83pub trait Provider {
84    /// Returns the [`Metadata`] for this provider, identifying itself and its
85    /// configuration sources.
86    fn metadata(&self) -> Metadata;
87
88    /// Returns the configuration data.
89    fn data(&self) -> Result<Map<Profile, Dict>, Error>;
90
91    /// Optionally returns a profile to set on the [`Figment`](crate::Figment)
92    /// this provider is merged into. The profile is only set if `self` is
93    /// _merged_.
94    fn profile(&self) -> Option<Profile> {
95        None
96    }
97
98    /// This is used internally! Please, please don't use this externally. If
99    /// you have a good usecase for this, let me know!
100    #[doc(hidden)]
101    fn __metadata_map(&self) -> Option<Map<Tag, Metadata>> { None }
102}
103
104/// This is exactly `<T as Provider>`.
105impl<T: Provider> Provider for &T {
106    fn metadata(&self) -> Metadata { T::metadata(self) }
107
108    fn data(&self) -> Result<Map<Profile, Dict>, Error> { T::data(self) }
109
110    fn profile(&self) -> Option<Profile> {
111        T::profile(self)
112    }
113
114    #[doc(hidden)]
115    fn __metadata_map(&self) -> Option<Map<Tag, Metadata>> {
116        T::__metadata_map(self)
117    }
118}
119
120/// This is exactly equivalent to [`Serialized::global(K, V)`].
121///
122/// [`Serialized::global(K, V)`]: crate::providers::Serialized::global()
123impl<K: AsRef<str>, V: serde::Serialize> Provider for (K, V) {
124    fn metadata(&self) -> Metadata {
125        use std::any::type_name;
126        Metadata::named(format!("({}, {})", type_name::<K>(), type_name::<V>()))
127    }
128
129    fn data(&self) -> Result<Map<Profile, Dict>, Error> {
130        use crate::providers::Serialized;
131        Serialized::global(self.0.as_ref(), &self.1).data()
132    }
133}