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}