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/// figment::{value::*, *},
99/// impl_figment_convert, merge_impl_figment_convert, Config,
100/// };
101/// use std::path::PathBuf;
102///
103/// #[derive(Default)]
104/// struct MyArgs {
105/// root: Option<PathBuf>,
106/// }
107///
108/// impl Provider for MyArgs {
109/// fn metadata(&self) -> Metadata {
110/// Metadata::default()
111/// }
112///
113/// fn data(&self) -> std::result::Result<Map<Profile, Dict>, Error> {
114/// todo!()
115/// }
116/// }
117///
118/// impl_figment_convert!(MyArgs);
119///
120/// #[derive(Default)]
121/// struct OuterArgs {
122/// value: u64,
123/// inner: MyArgs,
124/// }
125///
126/// impl Provider for OuterArgs {
127/// fn metadata(&self) -> Metadata {
128/// Metadata::default()
129/// }
130///
131/// fn data(&self) -> std::result::Result<Map<Profile, Dict>, Error> {
132/// todo!()
133/// }
134/// }
135///
136/// merge_impl_figment_convert!(OuterArgs, inner);
137/// ```
138#[macro_export]
139macro_rules! merge_impl_figment_convert {
140 ($name:ty, $start:ident $(, $more:ident)*) => {
141 impl<'a> From<&'a $name> for $crate::figment::Figment {
142 fn from(args: &'a $name) -> Self {
143 let mut figment: $crate::figment::Figment = From::from(&args.$start);
144 $ (
145 figment = figment.merge(&args.$more);
146 )*
147 figment = figment.merge(args);
148 figment
149 }
150 }
151 };
152}
153
154/// A macro to implement converters from a type to [`Config`](crate::Config) and
155/// [`figment::Figment`].
156///
157/// Via [Config::to_figment](crate::Config::to_figment) and the
158/// [Cast](crate::FigmentProviders::Cast) profile.
159#[macro_export]
160macro_rules! impl_figment_convert_cast {
161 ($name:ty) => {
162 impl<'a> From<&'a $name> for $crate::figment::Figment {
163 fn from(args: &'a $name) -> Self {
164 let root =
165 $crate::find_project_root(None).expect("could not determine project root");
166 $crate::Config::with_root(&root)
167 .to_figment($crate::FigmentProviders::Cast)
168 .merge(args)
169 }
170 }
171 };
172}
173
174/// Same as `impl_figment_convert` but also implies `Provider` for the given `Serialize` type for
175/// convenience. The `Provider` only provides the "root" value for the current profile
176#[macro_export]
177macro_rules! impl_figment_convert_basic {
178 ($name:ty) => {
179 $crate::impl_figment_convert!($name);
180
181 impl $crate::figment::Provider for $name {
182 fn metadata(&self) -> $crate::figment::Metadata {
183 $crate::figment::Metadata::named(stringify!($name))
184 }
185
186 fn data(
187 &self,
188 ) -> Result<
189 $crate::figment::value::Map<$crate::figment::Profile, $crate::figment::value::Dict>,
190 $crate::figment::Error,
191 > {
192 let mut dict = $crate::figment::value::Dict::new();
193 if let Some(root) = self.root.as_ref() {
194 dict.insert(
195 "root".to_string(),
196 $crate::figment::value::Value::serialize(root)?,
197 );
198 }
199 Ok($crate::figment::value::Map::from([($crate::Config::selected_profile(), dict)]))
200 }
201 }
202 };
203}