op_config/providers/
optional.rs

1use eyre::Result;
2use figment::{
3    value::{Dict, Map},
4    Error, Figment, Metadata, Profile, Provider,
5};
6
7use super::unwraps::UnwrapProfileProvider;
8
9/// Extracts the profile from the `profile` key and using the original key as backup, merging
10/// values where necessary
11///
12/// For example given:
13///
14/// ```toml
15/// [profile.cool]
16/// key = "value"
17///
18/// [cool]
19/// key2 = "value2"
20/// ```
21///
22/// OptionalStrictProfileProvider will output:
23///
24/// ```toml
25/// [cool]
26/// key = "value"
27/// key2 = "value2"
28/// ```
29///
30/// And emit a deprecation warning
31#[derive(Debug)]
32pub struct OptionalStrictProfileProvider<P> {
33    provider: P,
34    profiles: Vec<Profile>,
35}
36
37impl<P> OptionalStrictProfileProvider<P> {
38    pub(crate) const PROFILE_PROFILE: Profile = Profile::const_new("profile");
39
40    /// Creates a new `OptionalStrictProfileProvider` from the given provider and profiles
41    #[allow(dead_code)]
42    pub fn new(provider: P, profiles: impl IntoIterator<Item = impl Into<Profile>>) -> Self {
43        Self {
44            provider,
45            profiles: profiles.into_iter().map(|profile| profile.into()).collect(),
46        }
47    }
48}
49
50impl<P: Provider> Provider for OptionalStrictProfileProvider<P> {
51    fn metadata(&self) -> Metadata {
52        self.provider.metadata()
53    }
54
55    fn data(&self) -> Result<Map<Profile, Dict>, Error> {
56        let mut figment = Figment::from(&self.provider);
57        for profile in &self.profiles {
58            figment = figment.merge(UnwrapProfileProvider::new(
59                &self.provider,
60                Self::PROFILE_PROFILE,
61                profile.clone(),
62            ));
63        }
64        figment.data().map_err(|err| {
65            // figment does tag metadata and tries to map metadata to an error, since we use a new
66            // figment in this provider this new figment does not know about the metadata of the
67            // provider and can't map the metadata to the error. Therefor we return the root error
68            // if this error originated in the provider's data.
69            if let Err(root_err) = self.provider.data() {
70                return root_err;
71            }
72            err
73        })
74    }
75
76    fn profile(&self) -> Option<Profile> {
77        self.profiles.last().cloned()
78    }
79}