diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 65923ced798a..1e27d06c1f08 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -509,6 +509,86 @@ impl Config { Ok(config) } + /// Returns the populated [Figment] using the requested [FigmentProviders] preset. + /// + /// This will merge various providers, such as env,toml,remappings into the figment. + pub fn to_figment(self, providers: FigmentProviders) -> Figment { + let mut c = self; + let profile = Config::selected_profile(); + let mut figment = Figment::default().merge(DappHardhatDirProvider(&c.__root.0)); + + // merge global foundry.toml file + if let Some(global_toml) = Config::foundry_dir_toml().filter(|p| p.exists()) { + figment = Config::merge_toml_provider( + figment, + TomlFileProvider::new(None, global_toml).cached(), + profile.clone(), + ); + } + // merge local foundry.toml file + figment = Config::merge_toml_provider( + figment, + TomlFileProvider::new(Some("FOUNDRY_CONFIG"), c.__root.0.join(Config::FILE_NAME)) + .cached(), + profile.clone(), + ); + + // merge environment variables + figment = figment + .merge( + Env::prefixed("DAPP_") + .ignore(&["REMAPPINGS", "LIBRARIES", "FFI", "FS_PERMISSIONS"]) + .global(), + ) + .merge( + Env::prefixed("DAPP_TEST_") + .ignore(&["CACHE", "FUZZ_RUNS", "DEPTH", "FFI", "FS_PERMISSIONS"]) + .global(), + ) + .merge(DappEnvCompatProvider) + .merge(EtherscanEnvProvider::default()) + .merge( + Env::prefixed("FOUNDRY_") + .ignore(&["PROFILE", "REMAPPINGS", "LIBRARIES", "FFI", "FS_PERMISSIONS"]) + .map(|key| { + let key = key.as_str(); + if Config::STANDALONE_SECTIONS.iter().any(|section| { + key.starts_with(&format!("{}_", section.to_ascii_uppercase())) + }) { + key.replacen('_', ".", 1).into() + } else { + key.into() + } + }) + .global(), + ) + .select(profile.clone()); + + // only resolve remappings if all providers are requested + if providers.is_all() { + // we try to merge remappings after we've merged all other providers, this prevents + // redundant fs lookups to determine the default remappings that are eventually updated + // by other providers, like the toml file + let remappings = RemappingsProvider { + auto_detect_remappings: figment + .extract_inner::("auto_detect_remappings") + .unwrap_or(true), + lib_paths: figment + .extract_inner::>("libs") + .map(Cow::Owned) + .unwrap_or_else(|_| Cow::Borrowed(&c.libs)), + root: &c.__root.0, + remappings: figment.extract_inner::>("remappings"), + }; + figment = figment.merge(remappings); + } + + // normalize defaults + figment = c.normalize_defaults(figment); + + Figment::from(c).merge(figment).select(profile) + } + /// The config supports relative paths and tracks the root path separately see /// `Config::with_root` /// @@ -1643,77 +1723,32 @@ impl Config { } impl From for Figment { - fn from(mut c: Config) -> Figment { - let profile = Config::selected_profile(); - let mut figment = Figment::default().merge(DappHardhatDirProvider(&c.__root.0)); - - // merge global foundry.toml file - if let Some(global_toml) = Config::foundry_dir_toml().filter(|p| p.exists()) { - figment = Config::merge_toml_provider( - figment, - TomlFileProvider::new(None, global_toml).cached(), - profile.clone(), - ); - } - // merge local foundry.toml file - figment = Config::merge_toml_provider( - figment, - TomlFileProvider::new(Some("FOUNDRY_CONFIG"), c.__root.0.join(Config::FILE_NAME)) - .cached(), - profile.clone(), - ); - - // merge environment variables - figment = figment - .merge( - Env::prefixed("DAPP_") - .ignore(&["REMAPPINGS", "LIBRARIES", "FFI", "FS_PERMISSIONS"]) - .global(), - ) - .merge( - Env::prefixed("DAPP_TEST_") - .ignore(&["CACHE", "FUZZ_RUNS", "DEPTH", "FFI", "FS_PERMISSIONS"]) - .global(), - ) - .merge(DappEnvCompatProvider) - .merge(EtherscanEnvProvider::default()) - .merge( - Env::prefixed("FOUNDRY_") - .ignore(&["PROFILE", "REMAPPINGS", "LIBRARIES", "FFI", "FS_PERMISSIONS"]) - .map(|key| { - let key = key.as_str(); - if Config::STANDALONE_SECTIONS.iter().any(|section| { - key.starts_with(&format!("{}_", section.to_ascii_uppercase())) - }) { - key.replacen('_', ".", 1).into() - } else { - key.into() - } - }) - .global(), - ) - .select(profile.clone()); + fn from(c: Config) -> Figment { + c.to_figment(FigmentProviders::All) + } +} - // we try to merge remappings after we've merged all other providers, this prevents - // redundant fs lookups to determine the default remappings that are eventually updated by - // other providers, like the toml file - let remappings = RemappingsProvider { - auto_detect_remappings: figment - .extract_inner::("auto_detect_remappings") - .unwrap_or(true), - lib_paths: figment - .extract_inner::>("libs") - .map(Cow::Owned) - .unwrap_or_else(|_| Cow::Borrowed(&c.libs)), - root: &c.__root.0, - remappings: figment.extract_inner::>("remappings"), - }; - let merge = figment.merge(remappings); +/// Determines what providers should be used when loading the [Figment] for a [Config] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum FigmentProviders { + /// Include all providers + #[default] + All, + /// Only include necessary providers that are useful for cast commands + /// + /// This will exclude more expensive providers such as remappings + Cast, +} - // normalize defaults - let merge = c.normalize_defaults(merge); +impl FigmentProviders { + /// Returns true if all providers should be included + pub const fn is_all(&self) -> bool { + matches!(self, Self::All) + } - Figment::from(c).merge(merge).select(profile) + /// Returns true if this is the cast preset + pub const fn is_cast(&self) -> bool { + matches!(self, Self::Cast) } } diff --git a/crates/config/src/macros.rs b/crates/config/src/macros.rs index b84876b8e571..d7e206a97825 100644 --- a/crates/config/src/macros.rs +++ b/crates/config/src/macros.rs @@ -184,12 +184,16 @@ macro_rules! merge_impl_figment_convert { } /// A macro to implement converters from a type to [`Config`] and [`figment::Figment`] +/// +/// Via [Config::to_figment](crate::Config::to_figment) and the +/// [Cast](crate::FigmentProviders::Cast) profile. #[macro_export] macro_rules! impl_figment_convert_cast { ($name:ty) => { impl<'a> From<&'a $name> for $crate::figment::Figment { fn from(args: &'a $name) -> Self { - $crate::Config::figment_with_root($crate::find_project_root_path(None).unwrap()) + $crate::Config::with_root($crate::find_project_root_path(None).unwrap()) + .to_figment($crate::FigmentProviders::Cast) .merge(args) } }