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}