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}