diff --git a/src/dfx-core/src/config/model/dfinity.rs b/src/dfx-core/src/config/model/dfinity.rs index ad5f40cba7..db396ead79 100644 --- a/src/dfx-core/src/config/model/dfinity.rs +++ b/src/dfx-core/src/config/model/dfinity.rs @@ -29,11 +29,13 @@ use crate::error::socket_addr_conversion::SocketAddrConversionError; use crate::error::socket_addr_conversion::SocketAddrConversionError::{ EmptyIterator, ParseSocketAddrFailed, }; -use crate::error::structured_file::StructuredFileError; use crate::error::structured_file::StructuredFileError::{ DeserializeJsonFileFailed, ReadJsonFileFailed, }; -use crate::extension::manifest::custom_canister_type; +use crate::error::structured_file::{ + ReadConfigurationError, StructuredFileError, TransformConfigurationError, +}; +use crate::extension::manifest::custom_canister_type::TransformConfiguration; use crate::json::save_json_file; use crate::json::structure::{PossiblyStr, SerdeVec}; use byte_unit::Byte; @@ -952,44 +954,45 @@ impl Config { Ok(None) } - fn from_file( + fn from_file( path: &Path, - dfx_version: &semver::Version, - ) -> Result { + transformer: &mut T, + ) -> Result { let content = crate::fs::read(path).map_err(ReadJsonFileFailed)?; - Config::from_slice(path.to_path_buf(), &content, dfx_version) + Config::from_slice(path.to_path_buf(), &content, transformer) } - pub fn from_dir( + pub fn from_dir( working_dir: &Path, - dfx_version: &semver::Version, + transformer: &mut T, ) -> Result, LoadDfxConfigError> { let path = Config::resolve_config_path(working_dir)?; - path.map(|path| Config::from_file(&path, dfx_version)) + path.map(|path| Config::from_file(&path, transformer)) .transpose() .map_err(LoadFromFileFailed) } - pub fn from_current_dir( - dfx_version: &semver::Version, + pub fn from_current_dir( + transformer: &mut T, ) -> Result, LoadDfxConfigError> { Config::from_dir( &std::env::current_dir().map_err(DetermineCurrentWorkingDirFailed)?, - dfx_version, + transformer, ) } - fn from_slice( + fn from_slice( path: PathBuf, content: &[u8], - dfx_version: &semver::Version, - ) -> Result { + transformer: &mut T, + ) -> Result { let mut json: serde_json::Value = serde_json::from_slice(content) .map_err(|e| DeserializeJsonFileFailed(Box::new(path.clone()), e))?; - let extension_manager = - crate::extension::manager::ExtensionManager::new(dfx_version).unwrap(); - custom_canister_type::transform_dfx_json_via_extension(&mut json, extension_manager) - .unwrap(); // TODO: error handling + + transformer + .transform(&mut json) + .map_err(TransformConfigurationError::from)?; + let config = serde_json::from_value(json.clone()) .map_err(|e| DeserializeJsonFileFailed(Box::new(path.clone()), e))?; Ok(Config { path, json, config }) @@ -997,20 +1000,24 @@ impl Config { /// Create a configuration from a string. #[cfg(test)] - pub(crate) fn from_str( - content: &str, - dfx_version: &semver::Version, - ) -> Result { - Config::from_slice(PathBuf::from("-"), content.as_bytes(), dfx_version) + pub(crate) fn from_str(content: &str) -> Result { + let mut no_op_transformer = + crate::extension::manifest::custom_canister_type::NoopTransformConfiguration; + Config::from_slice( + PathBuf::from("-"), + content.as_bytes(), + &mut no_op_transformer, + ) } #[cfg(test)] pub(crate) fn from_str_and_path( path: PathBuf, content: &str, - dfx_version: &semver::Version, - ) -> Result { - Config::from_slice(path, content.as_bytes(), dfx_version) + ) -> Result { + let mut no_op_transformer = + crate::extension::manifest::custom_canister_type::NoopTransformConfiguration; + Config::from_slice(path, content.as_bytes(), &mut no_op_transformer) } pub fn get_path(&self) -> &PathBuf { @@ -1257,7 +1264,6 @@ mod tests { } } }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); @@ -1280,7 +1286,6 @@ mod tests { } } }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); @@ -1302,7 +1307,6 @@ mod tests { } } }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); @@ -1325,7 +1329,6 @@ mod tests { } } }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); @@ -1359,7 +1362,6 @@ mod tests { } } }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); @@ -1383,7 +1385,6 @@ mod tests { } } }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); let config_interface = config_no_values.get_config(); diff --git a/src/dfx-core/src/error/load_dfx_config.rs b/src/dfx-core/src/error/load_dfx_config.rs index 674d93954e..92c39e484b 100644 --- a/src/dfx-core/src/error/load_dfx_config.rs +++ b/src/dfx-core/src/error/load_dfx_config.rs @@ -1,5 +1,5 @@ use crate::error::fs::FsError; -use crate::error::structured_file::StructuredFileError; +use crate::error::structured_file::ReadConfigurationError; use thiserror::Error; #[derive(Error, Debug)] @@ -7,9 +7,9 @@ pub enum LoadDfxConfigError { #[error("Failed to resolve config path: {0}")] ResolveConfigPathFailed(FsError), - #[error("Failed to load dfx configuration: {0}")] - LoadFromFileFailed(StructuredFileError), - #[error("Failed to determine current working dir: {0}")] DetermineCurrentWorkingDirFailed(std::io::Error), + + #[error("Failed to load dfx configuration: {0}")] + LoadFromFileFailed(ReadConfigurationError), } diff --git a/src/dfx-core/src/error/structured_file.rs b/src/dfx-core/src/error/structured_file.rs index 375c19be63..b5c9e71954 100644 --- a/src/dfx-core/src/error/structured_file.rs +++ b/src/dfx-core/src/error/structured_file.rs @@ -2,6 +2,8 @@ use crate::error::fs::FsError; use std::path::PathBuf; use thiserror::Error; +use super::extension::ExtensionError; + #[derive(Error, Debug)] pub enum StructuredFileError { #[error("Failed to parse contents of {0} as json: {1}")] @@ -16,3 +18,19 @@ pub enum StructuredFileError { #[error("Failed to write JSON file: {0}")] WriteJsonFileFailed(FsError), } + +#[derive(Error, Debug)] +pub enum ReadConfigurationError { + #[error(transparent)] + StructuredFile(#[from] StructuredFileError), + #[error(transparent)] + TransformConfiguration(#[from] TransformConfigurationError), +} + +#[derive(Error, Debug)] +pub enum TransformConfigurationError { + #[error("Configuration transformation failed: {0}")] + ConfigurationTransformationFailed(String), // Or another error type if necessary + #[error("Extension error: {0}")] + ExtensionError(#[from] ExtensionError), // Note that `from` here allows automatic conversion +} diff --git a/src/dfx-core/src/extension/manifest/custom_canister_type.rs b/src/dfx-core/src/extension/manifest/custom_canister_type.rs index 6714b54060..1d16e8992b 100644 --- a/src/dfx-core/src/extension/manifest/custom_canister_type.rs +++ b/src/dfx-core/src/extension/manifest/custom_canister_type.rs @@ -7,30 +7,34 @@ use serde::{Deserialize, Serialize}; use serde_json::{Map as JsonMap, Value as JsonValue}; use std::collections::BTreeMap; -pub(crate) fn transform_dfx_json_via_extension( - json: &mut JsonValue, - extension_manager: ExtensionManager, -) -> Result<(), ExtensionError> { - let canisters = match json.get_mut("canisters").and_then(|v| v.as_object_mut()) { - Some(canisters) => canisters, - None => return Ok(()), - }; - for (canister_name, canister_declaration) in canisters.iter_mut() { - if let Some(canister_type) = get_valid_canister_type(canister_declaration) { - let canister_declaration = canister_declaration.as_object_mut().unwrap(); - let (extension_name, canister_type) = - get_extension_name_and_custom_canister_type(&canister_type); - let extension_manifest = ExtensionManifest::get(extension_name, &extension_manager)?; - *canister_declaration = process_canister_declaration( - canister_declaration, - extension_name, - &extension_manifest, - canister_name, - canister_type, - )?; +pub trait TransformConfiguration { + fn transform(&mut self, json: &mut serde_json::Value) -> Result<(), ExtensionError>; +} + +impl TransformConfiguration for ExtensionManager { + fn transform(&mut self, json: &mut JsonValue) -> Result<(), ExtensionError> { + let canisters = match json.get_mut("canisters").and_then(|v| v.as_object_mut()) { + Some(canisters) => canisters, + None => return Ok(()), + }; + for (canister_name, canister_declaration) in canisters.iter_mut() { + if let Some(canister_type) = get_valid_canister_type(canister_declaration) { + let canister_declaration = canister_declaration.as_object_mut().unwrap(); + let (extension_name, canister_type) = + get_extension_name_and_custom_canister_type(&canister_type); + let extension_manifest = + ExtensionManifest::get_by_extension_name(extension_name, self)?; + *canister_declaration = process_canister_declaration( + canister_declaration, + extension_name, + &extension_manifest, + canister_name, + canister_type, + )?; + } } + Ok(()) } - Ok(()) } fn get_valid_canister_type(canister_declaration: &mut JsonValue) -> Option { @@ -202,7 +206,7 @@ impl CustomCanisterTypeDeclaration { } #[cfg(test)] -mod tests { +mod custom_canister_type_declaration_tests { use super::*; macro_rules! test_op { @@ -640,3 +644,13 @@ mod tests { ); } } + +#[cfg(test)] +pub struct NoopTransformConfiguration; +#[cfg(test)] +impl TransformConfiguration for NoopTransformConfiguration { + fn transform(&mut self, _: &mut serde_json::Value) -> Result<(), ExtensionError> { + // Do nothing + Ok(()) + } +} diff --git a/src/dfx-core/src/extension/manifest/extension.rs b/src/dfx-core/src/extension/manifest/extension.rs index e74c26f615..caffffcc74 100644 --- a/src/dfx-core/src/extension/manifest/extension.rs +++ b/src/dfx-core/src/extension/manifest/extension.rs @@ -25,7 +25,7 @@ pub struct ExtensionManifest { } impl ExtensionManifest { - pub fn get( + pub fn get_by_extension_name( extension_name: &str, extension_manager: &ExtensionManager, ) -> Result { diff --git a/src/dfx-core/src/extension/mod.rs b/src/dfx-core/src/extension/mod.rs index e4b5082ad0..988db47afb 100644 --- a/src/dfx-core/src/extension/mod.rs +++ b/src/dfx-core/src/extension/mod.rs @@ -33,7 +33,7 @@ impl Extension { self, manager: &ExtensionManager, ) -> Result { - let manifest = ExtensionManifest::get(&self.name, manager)?; + let manifest = ExtensionManifest::get_by_extension_name(&self.name, manager)?; let cmd = Command::new(&self.name) .bin_name(&self.name) // don't accept unknown options diff --git a/src/dfx-core/src/network/provider.rs b/src/dfx-core/src/network/provider.rs index 1461417958..d382869892 100644 --- a/src/dfx-core/src/network/provider.rs +++ b/src/dfx-core/src/network/provider.rs @@ -588,7 +588,9 @@ mod tests { .unwrap(); } - let config = Config::from_dir(&project_dir, &semver::Version::new(0, 0, 0)) + let mut no_op_transformer = + crate::extension::manifest::custom_canister_type::NoopTransformConfiguration; + let config = Config::from_dir(&project_dir, &mut no_op_transformer) .unwrap() .unwrap(); let network_descriptor = create_network_descriptor( @@ -619,7 +621,6 @@ mod tests { } } }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); @@ -651,7 +652,6 @@ mod tests { } } }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); @@ -672,7 +672,6 @@ mod tests { "networks": { } }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); let network_descriptor = create_network_descriptor( @@ -698,7 +697,6 @@ mod tests { let config = Config::from_str( r#"{ }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); let network_descriptor = create_network_descriptor( @@ -736,7 +734,6 @@ mod tests { } } }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); @@ -780,7 +777,6 @@ mod tests { } } }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); @@ -824,7 +820,6 @@ mod tests { } } }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); @@ -866,7 +861,6 @@ mod tests { } } }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); @@ -909,7 +903,6 @@ mod tests { } } }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); @@ -957,7 +950,6 @@ mod tests { } } }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); @@ -998,7 +990,6 @@ mod tests { } } }"#, - &semver::Version::new(0, 0, 0), ) .unwrap(); diff --git a/src/dfx/src/lib/environment.rs b/src/dfx/src/lib/environment.rs index 6ee64479c1..267b534a6b 100644 --- a/src/dfx/src/lib/environment.rs +++ b/src/dfx/src/lib/environment.rs @@ -97,9 +97,9 @@ pub struct EnvironmentImpl { } impl EnvironmentImpl { - pub fn new() -> DfxResult { + pub fn new(extension_manager: &mut ExtensionManager) -> DfxResult { let shared_networks_config = NetworksConfig::new()?; - let config = Config::from_current_dir(dfx_version())?; + let config = Config::from_current_dir(extension_manager)?; if let Some(ref config) = config { let temp_dir = config.get_temp_path(); create_dir_all(&temp_dir).with_context(|| { diff --git a/src/dfx/src/main.rs b/src/dfx/src/main.rs index 712d2df3f3..eac923f437 100644 --- a/src/dfx/src/main.rs +++ b/src/dfx/src/main.rs @@ -164,7 +164,7 @@ fn main() { let mut args = std::env::args_os().collect::>(); let mut error_diagnosis: Diagnosis = NULL_DIAGNOSIS; - ExtensionManager::new(dfx_version()) + let mut extension_manager = ExtensionManager::new(dfx_version()) .and_then(|em| { let installed_extensions = em.installed_extensions_as_clap_commands()?; if !installed_extensions.is_empty() { @@ -179,7 +179,7 @@ fn main() { args.splice(idx..idx, ["extension", "run"].iter().map(OsString::from)); } } - Ok(()) + Ok(em) }) .unwrap_or_else(|err| { print_error_and_diagnosis(err.into(), error_diagnosis.clone()); @@ -191,10 +191,10 @@ fn main() { let identity = cli_opts.identity; let effective_canister_id = cli_opts.provisional_create_canister_effective_canister_id; let command = cli_opts.command; - let result = match EnvironmentImpl::new() { + let result = match EnvironmentImpl::new(&mut extension_manager) { Ok(env) => { maybe_redirect_dfx(env.get_version()).map_or((), |_| unreachable!()); - match EnvironmentImpl::new().map(|env| { + match EnvironmentImpl::new(&mut extension_manager).map(|env| { env.with_logger(log) .with_identity_override(identity) .with_verbose_level(verbose_level)