foundry_config/providers/
warnings.rs1use crate::{Config, DEPRECATIONS, Warning};
2use figment::{
3 Error, Figment, Metadata, Profile, Provider,
4 value::{Dict, Map, Value},
5};
6use heck::ToSnakeCase;
7use std::collections::BTreeMap;
8
9pub struct WarningsProvider<P> {
11 provider: P,
12 profile: Profile,
13 old_warnings: Result<Vec<Warning>, Error>,
14}
15
16impl<P: Provider> WarningsProvider<P> {
17 const WARNINGS_KEY: &'static str = "__warnings";
18
19 pub fn new(
21 provider: P,
22 profile: impl Into<Profile>,
23 old_warnings: Result<Vec<Warning>, Error>,
24 ) -> Self {
25 Self { provider, profile: profile.into(), old_warnings }
26 }
27
28 pub fn for_figment(provider: P, figment: &Figment) -> Self {
30 let old_warnings = {
31 let warnings_res = figment.extract_inner(Self::WARNINGS_KEY);
32 if warnings_res.as_ref().err().map(|err| err.missing()).unwrap_or(false) {
33 Ok(vec![])
34 } else {
35 warnings_res
36 }
37 };
38 Self::new(provider, figment.profile().clone(), old_warnings)
39 }
40
41 pub fn collect_warnings(&self) -> Result<Vec<Warning>, Error> {
43 let data = self.provider.data().unwrap_or_default();
44
45 let mut out = self.old_warnings.clone()?;
46
47 out.extend(data.keys().filter(|k| !Config::is_standalone_section(k.as_str())).map(
49 |unknown_section| {
50 let source = self.provider.metadata().source.map(|s| s.to_string());
51 Warning::UnknownSection { unknown_section: unknown_section.clone(), source }
52 },
53 ));
54
55 let deprecated_key_warning = |key| {
57 DEPRECATIONS.iter().find_map(|(deprecated_key, new_value)| {
58 if key == *deprecated_key {
59 Some(Warning::DeprecatedKey {
60 old: deprecated_key.to_string(),
61 new: new_value.to_string(),
62 })
63 } else {
64 None
65 }
66 })
67 };
68 let profiles = data
69 .iter()
70 .filter(|(profile, _)| **profile == Config::PROFILE_SECTION)
71 .map(|(_, dict)| dict);
72
73 out.extend(profiles.clone().flat_map(BTreeMap::keys).filter_map(deprecated_key_warning));
74 out.extend(
75 profiles
76 .clone()
77 .filter_map(|dict| dict.get(self.profile.as_str().as_str()))
78 .filter_map(Value::as_dict)
79 .flat_map(BTreeMap::keys)
80 .filter_map(deprecated_key_warning),
81 );
82
83 if let Ok(default_map) = figment::providers::Serialized::defaults(&Config::default()).data()
85 && let Some(default_dict) = default_map.get(&Config::DEFAULT_PROFILE)
86 {
87 let allowed_keys: std::collections::BTreeSet<String> =
88 default_dict.keys().cloned().collect();
89 for profile_map in profiles {
90 for (profile, value) in profile_map {
91 let Some(profile_dict) = value.as_dict() else {
92 continue;
93 };
94
95 let source = self
96 .provider
97 .metadata()
98 .source
99 .map(|s| s.to_string())
100 .unwrap_or(Config::FILE_NAME.to_string());
101 for key in profile_dict.keys() {
102 let is_not_deprecated =
103 !DEPRECATIONS.iter().any(|(deprecated_key, _)| *deprecated_key == key);
104 let is_not_allowed = !allowed_keys.contains(key)
105 && !allowed_keys.contains(&key.to_snake_case());
106 let is_not_reserved = key != "extends" && key != Self::WARNINGS_KEY;
107 let is_not_backward_compatible = key != "solc_version";
108
109 if is_not_deprecated
110 && is_not_allowed
111 && is_not_reserved
112 && is_not_backward_compatible
113 {
114 out.push(Warning::UnknownKey {
115 key: key.clone(),
116 profile: profile.clone(),
117 source: source.clone(),
118 });
119 }
120 }
121 }
122 }
123 }
124
125 Ok(out)
126 }
127}
128
129impl<P: Provider> Provider for WarningsProvider<P> {
130 fn metadata(&self) -> Metadata {
131 if let Some(source) = self.provider.metadata().source {
132 Metadata::from("Warnings", source)
133 } else {
134 Metadata::named("Warnings")
135 }
136 }
137
138 fn data(&self) -> Result<Map<Profile, Dict>, Error> {
139 let warnings = self.collect_warnings()?;
140 Ok(Map::from([(
141 self.profile.clone(),
142 Dict::from([(Self::WARNINGS_KEY.to_string(), Value::serialize(warnings)?)]),
143 )]))
144 }
145
146 fn profile(&self) -> Option<Profile> {
147 Some(self.profile.clone())
148 }
149}