foundry_config/
macros.rs

1/// A macro to implement converters from a type to [`Config`](crate::Config) and
2/// [`figment::Figment`].
3///
4/// This can be used to remove some boilerplate code that's necessary to add additional layer(s) to
5/// the `Config`'s default `Figment`.
6///
7/// `impl_figment` takes the default `Config` and merges additional `Provider`, therefore the
8/// targeted type, requires an implementation of `figment::Profile`.
9///
10/// # Example
11///
12/// Use `impl_figment` on a type with a `root: Option<PathBuf>` field, which will be used for
13/// [`Config::figment_with_root()`](crate::Config::figment_with_root).
14///
15/// ```rust
16/// use std::path::PathBuf;
17/// use serde::Serialize;
18/// use foundry_config::{Config, impl_figment_convert};
19/// use foundry_config::figment::*;
20/// use foundry_config::figment::error::Kind::InvalidType;
21/// use foundry_config::figment::value::*;
22/// #[derive(Default, Serialize)]
23/// struct MyArgs {
24///     #[serde(skip_serializing_if = "Option::is_none")]
25///     root: Option<PathBuf>,
26/// }
27/// impl_figment_convert!(MyArgs);
28///
29/// impl Provider for MyArgs {
30///     fn metadata(&self) -> Metadata {
31///         Metadata::default()
32///     }
33///
34///     fn data(&self) -> std::result::Result<Map<Profile, Dict>, Error> {
35///         let value = Value::serialize(self)?;
36///         let error = InvalidType(value.to_actual(), "map".into());
37///         let mut dict = value.into_dict().ok_or(error)?;
38///         Ok(Map::from([(Config::selected_profile(), dict)]))
39///     }
40/// }
41///
42/// let figment: Figment = From::from(&MyArgs::default());
43///
44///  // Use `impl_figment` on a type that has several nested `Provider` as fields but is _not_ a `Provider` itself
45///
46/// #[derive(Default)]
47/// struct Outer {
48///     start: MyArgs,
49///     second: MyArgs,
50///     third: MyArgs,
51/// }
52/// impl_figment_convert!(Outer, start, second, third);
53///
54/// let figment: Figment = From::from(&Outer::default());
55/// ```
56#[macro_export]
57macro_rules! impl_figment_convert {
58    ($name:ty) => {
59        impl<'a> From<&'a $name> for $crate::figment::Figment {
60            fn from(args: &'a $name) -> Self {
61                $crate::Config::figment_with_root_opt(args.root.as_deref()).merge(args)
62            }
63        }
64    };
65    ($name:ty, $start:ident $(, $more:ident)*) => {
66        impl<'a> From<&'a $name> for $crate::figment::Figment {
67            fn from(args: &'a $name) -> Self {
68                let mut figment: $crate::figment::Figment = From::from(&args.$start);
69                $(
70                    figment = figment.merge(&args.$more);
71                )*
72                figment
73            }
74        }
75    };
76    ($name:ty, self, $start:ident $(, $more:ident)*) => {
77        impl<'a> From<&'a $name> for $crate::figment::Figment {
78            fn from(args: &'a $name) -> Self {
79                let mut figment: $crate::figment::Figment = From::from(&args.$start);
80                $(
81                    figment = figment.merge(&args.$more);
82                )*
83                figment = figment.merge(args);
84                figment
85            }
86        }
87    };
88}
89
90/// Same as `impl_figment_convert` but also merges the type itself into the figment
91///
92/// # Example
93///
94/// Merge several nested `Provider` together with the type itself
95///
96/// ```rust
97/// use foundry_config::{
98///     Config,
99///     figment::{value::*, *},
100///     impl_figment_convert, merge_impl_figment_convert,
101/// };
102/// use std::path::PathBuf;
103///
104/// #[derive(Default)]
105/// struct MyArgs {
106///     root: Option<PathBuf>,
107/// }
108///
109/// impl Provider for MyArgs {
110///     fn metadata(&self) -> Metadata {
111///         Metadata::default()
112///     }
113///
114///     fn data(&self) -> std::result::Result<Map<Profile, Dict>, Error> {
115///         todo!()
116///     }
117/// }
118///
119/// impl_figment_convert!(MyArgs);
120///
121/// #[derive(Default)]
122/// struct OuterArgs {
123///     value: u64,
124///     inner: MyArgs,
125/// }
126///
127/// impl Provider for OuterArgs {
128///     fn metadata(&self) -> Metadata {
129///         Metadata::default()
130///     }
131///
132///     fn data(&self) -> std::result::Result<Map<Profile, Dict>, Error> {
133///         todo!()
134///     }
135/// }
136///
137/// merge_impl_figment_convert!(OuterArgs, inner);
138/// ```
139#[macro_export]
140macro_rules! merge_impl_figment_convert {
141    ($name:ty, $start:ident $(, $more:ident)*) => {
142        impl<'a> From<&'a $name> for $crate::figment::Figment {
143            fn from(args: &'a $name) -> Self {
144                let mut figment: $crate::figment::Figment = From::from(&args.$start);
145                $ (
146                  figment =  figment.merge(&args.$more);
147                )*
148                figment = figment.merge(args);
149                figment
150            }
151        }
152    };
153}
154
155/// A macro to implement converters from a type to [`Config`](crate::Config) and
156/// [`figment::Figment`].
157///
158/// Via [Config::to_figment](crate::Config::to_figment) and the
159/// [Cast](crate::FigmentProviders::Cast) profile.
160#[macro_export]
161macro_rules! impl_figment_convert_cast {
162    ($name:ty) => {
163        impl<'a> From<&'a $name> for $crate::figment::Figment {
164            fn from(args: &'a $name) -> Self {
165                let root =
166                    $crate::find_project_root(None).expect("could not determine project root");
167                $crate::Config::with_root(&root)
168                    .to_figment($crate::FigmentProviders::Cast)
169                    .merge(args)
170            }
171        }
172    };
173}
174
175/// Same as `impl_figment_convert` but also implies `Provider` for the given `Serialize` type for
176/// convenience. The `Provider` only provides the "root" value for the current profile
177#[macro_export]
178macro_rules! impl_figment_convert_basic {
179    ($name:ty) => {
180        $crate::impl_figment_convert!($name);
181
182        impl $crate::figment::Provider for $name {
183            fn metadata(&self) -> $crate::figment::Metadata {
184                $crate::figment::Metadata::named(stringify!($name))
185            }
186
187            fn data(
188                &self,
189            ) -> Result<
190                $crate::figment::value::Map<$crate::figment::Profile, $crate::figment::value::Dict>,
191                $crate::figment::Error,
192            > {
193                let mut dict = $crate::figment::value::Dict::new();
194                if let Some(root) = self.root.as_ref() {
195                    dict.insert(
196                        "root".to_string(),
197                        $crate::figment::value::Value::serialize(root)?,
198                    );
199                }
200                Ok($crate::figment::value::Map::from([($crate::Config::selected_profile(), dict)]))
201            }
202        }
203    };
204}