foundry_config/providers/
warnings.rs
1use crate::{Config, Warning, DEPRECATIONS};
2use figment::{
3 value::{Dict, Map, Value},
4 Error, Figment, Metadata, Profile, Provider,
5};
6use std::collections::BTreeMap;
7
8pub struct WarningsProvider<P> {
10 provider: P,
11 profile: Profile,
12 old_warnings: Result<Vec<Warning>, Error>,
13}
14
15impl<P: Provider> WarningsProvider<P> {
16 const WARNINGS_KEY: &'static str = "__warnings";
17
18 pub fn new(
20 provider: P,
21 profile: impl Into<Profile>,
22 old_warnings: Result<Vec<Warning>, Error>,
23 ) -> Self {
24 Self { provider, profile: profile.into(), old_warnings }
25 }
26
27 pub fn for_figment(provider: P, figment: &Figment) -> Self {
29 let old_warnings = {
30 let warnings_res = figment.extract_inner(Self::WARNINGS_KEY);
31 if warnings_res.as_ref().err().map(|err| err.missing()).unwrap_or(false) {
32 Ok(vec![])
33 } else {
34 warnings_res
35 }
36 };
37 Self::new(provider, figment.profile().clone(), old_warnings)
38 }
39
40 pub fn collect_warnings(&self) -> Result<Vec<Warning>, Error> {
42 let data = self.provider.data().unwrap_or_default();
43
44 let mut out = self.old_warnings.clone()?;
45
46 out.extend(
48 data.keys()
49 .filter(|k| {
50 **k != Config::PROFILE_SECTION &&
51 !Config::STANDALONE_SECTIONS.iter().any(|s| s == k)
52 })
53 .map(|unknown_section| {
54 let source = self.provider.metadata().source.map(|s| s.to_string());
55 Warning::UnknownSection { unknown_section: unknown_section.clone(), source }
56 }),
57 );
58
59 let deprecated_key_warning = |key| {
61 DEPRECATIONS.iter().find_map(|(deprecated_key, new_value)| {
62 if key == *deprecated_key {
63 Some(Warning::DeprecatedKey {
64 old: deprecated_key.to_string(),
65 new: new_value.to_string(),
66 })
67 } else {
68 None
69 }
70 })
71 };
72 let profiles = data
73 .iter()
74 .filter(|(profile, _)| **profile == Config::PROFILE_SECTION)
75 .map(|(_, dict)| dict);
76 out.extend(profiles.clone().flat_map(BTreeMap::keys).filter_map(deprecated_key_warning));
77 out.extend(
78 profiles
79 .filter_map(|dict| dict.get(self.profile.as_str().as_str()))
80 .filter_map(Value::as_dict)
81 .flat_map(BTreeMap::keys)
82 .filter_map(deprecated_key_warning),
83 );
84
85 Ok(out)
86 }
87}
88
89impl<P: Provider> Provider for WarningsProvider<P> {
90 fn metadata(&self) -> Metadata {
91 if let Some(source) = self.provider.metadata().source {
92 Metadata::from("Warnings", source)
93 } else {
94 Metadata::named("Warnings")
95 }
96 }
97
98 fn data(&self) -> Result<Map<Profile, Dict>, Error> {
99 let warnings = self.collect_warnings()?;
100 Ok(Map::from([(
101 self.profile.clone(),
102 Dict::from([(Self::WARNINGS_KEY.to_string(), Value::serialize(warnings)?)]),
103 )]))
104 }
105
106 fn profile(&self) -> Option<Profile> {
107 Some(self.profile.clone())
108 }
109}