diff options
Diffstat (limited to 'crates/shirabe/src/command')
39 files changed, 3422 insertions, 1379 deletions
diff --git a/crates/shirabe/src/command/about_command.rs b/crates/shirabe/src/command/about_command.rs index 627a7f2..3b16b0e 100644 --- a/crates/shirabe/src/command/about_command.rs +++ b/crates/shirabe/src/command/about_command.rs @@ -1,9 +1,9 @@ //! ref: composer/src/Composer/Command/AboutCommand.php -use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; -use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; use crate::command::base_command::BaseCommand; use crate::composer::Composer; +use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; +use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; pub struct AboutCommand { inner: BaseCommand, diff --git a/crates/shirabe/src/command/archive_command.rs b/crates/shirabe/src/command/archive_command.rs index b07a303..b1c8e83 100644 --- a/crates/shirabe/src/command/archive_command.rs +++ b/crates/shirabe/src/command/archive_command.rs @@ -6,7 +6,7 @@ use anyhow::Result; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; -use shirabe_php_shim::{get_debug_type, LogicException}; +use shirabe_php_shim::{LogicException, get_debug_type}; use crate::command::base_command::BaseCommand; use crate::command::completion_trait::CompletionTrait; @@ -88,29 +88,59 @@ impl ArchiveCommand { None => Factory::create_config(None, None)?, }; - let format = input.get_option("format").as_string_opt() + let format = input + .get_option("format") + .as_string_opt() .map(|s| s.to_string()) - .unwrap_or_else(|| config.get("archive-format").as_string().unwrap_or("tar").to_string()); + .unwrap_or_else(|| { + config + .get("archive-format") + .as_string() + .unwrap_or("tar") + .to_string() + }); - let dir = input.get_option("dir").as_string_opt() + let dir = input + .get_option("dir") + .as_string_opt() .map(|s| s.to_string()) - .unwrap_or_else(|| config.get("archive-dir").as_string().unwrap_or(".").to_string()); + .unwrap_or_else(|| { + config + .get("archive-dir") + .as_string() + .unwrap_or(".") + .to_string() + }); let return_code = self.archive( self.inner.get_io(), &config, - input.get_argument("package").as_string_opt().map(|s| s.to_string()), - input.get_argument("version").as_string_opt().map(|s| s.to_string()), + input + .get_argument("package") + .as_string_opt() + .map(|s| s.to_string()), + input + .get_argument("version") + .as_string_opt() + .map(|s| s.to_string()), &format, &dir, - input.get_option("file").as_string_opt().map(|s| s.to_string()), - input.get_option("ignore-filters").as_bool().unwrap_or(false), + input + .get_option("file") + .as_string_opt() + .map(|s| s.to_string()), + input + .get_option("ignore-filters") + .as_bool() + .unwrap_or(false), composer.as_ref(), )?; if return_code == 0 { if let Some(ref composer) = composer { - composer.get_event_dispatcher().dispatch_script(ScriptEvents::POST_ARCHIVE_CMD, true); + composer + .get_event_dispatcher() + .dispatch_script(ScriptEvents::POST_ARCHIVE_CMD, true); } } @@ -135,7 +165,8 @@ impl ArchiveCommand { let factory = Factory::new(); let process = ProcessExecutor::new_default(); let http_downloader = Factory::create_http_downloader(io, config)?; - let download_manager = factory.create_download_manager(io, config, &http_downloader, &process)?; + let download_manager = + factory.create_download_manager(io, config, &http_downloader, &process)?; let loop_ = Loop::new(http_downloader, process); factory.create_archive_manager(config, &download_manager, &loop_)? }; @@ -149,13 +180,26 @@ impl ArchiveCommand { self.inner.require_composer()?.get_package().clone_box() }; - io.write_error(&format!("<info>Creating the archive into \"{}\".</info>", dest)); - let package_path = archive_manager.archive(package.as_ref(), format, dest, file_name.as_deref(), ignore_filters)?; + io.write_error(&format!( + "<info>Creating the archive into \"{}\".</info>", + dest + )); + let package_path = archive_manager.archive( + package.as_ref(), + format, + dest, + file_name.as_deref(), + ignore_filters, + )?; let fs = Filesystem::new(); let short_path = fs.find_shortest_path(&Platform::get_cwd(), &package_path, true); io.write_error_no_newline("Created: "); - let display = if short_path.len() < package_path.len() { &short_path } else { &package_path }; + let display = if short_path.len() < package_path.len() { + &short_path + } else { + &package_path + }; io.write(display); Ok(0) @@ -175,20 +219,33 @@ impl ArchiveCommand { if let Some(composer) = self.inner.try_composer() { let local_repo = composer.get_repository_manager().get_local_repository(); - let mut repos: Vec<Box<dyn crate::repository::repository_interface::RepositoryInterface>> = vec![local_repo.clone_box()]; - repos.extend(composer.get_repository_manager().get_repositories().iter().map(|r| r.clone_box())); + let mut repos: Vec< + Box<dyn crate::repository::repository_interface::RepositoryInterface>, + > = vec![local_repo.clone_box()]; + repos.extend( + composer + .get_repository_manager() + .get_repositories() + .iter() + .map(|r| r.clone_box()), + ); repo = CompositeRepository::new(repos); min_stability = composer.get_package().get_minimum_stability().to_string(); } else { let default_repos = RepositoryFactory::default_repos_with_default_manager(io)?; let repo_names: Vec<String> = default_repos.iter().map(|r| r.get_repo_name()).collect(); - io.write_error(&format!("No composer.json found in the current directory, searching packages from {}", repo_names.join(", "))); + io.write_error(&format!( + "No composer.json found in the current directory, searching packages from {}", + repo_names.join(", ") + )); repo = CompositeRepository::new(default_repos); min_stability = "stable".to_string(); } if let Some(version_str) = &version { - if let Some(matches) = Preg::match_strict_groups(r"{@(stable|RC|beta|alpha|dev)$}i", version_str) { + if let Some(matches) = + Preg::match_strict_groups(r"{@(stable|RC|beta|alpha|dev)$}i", version_str) + { min_stability = VersionParser::normalize_stability(&matches[1]); let full_match_len = matches[0].len(); version = Some(version_str[..version_str.len() - full_match_len].to_string()); @@ -203,33 +260,60 @@ impl ArchiveCommand { let package = if packages.len() > 1 { let version_selector = VersionSelector::new(&repo_set); - let best = version_selector.find_best_candidate(&package_name.to_lowercase(), version.as_deref(), &min_stability); + let best = version_selector.find_best_candidate( + &package_name.to_lowercase(), + version.as_deref(), + &min_stability, + ); let p = best.unwrap_or_else(|| packages.into_iter().next().unwrap()); - io.write_error(&format!("<info>Found multiple matches, selected {}.</info>", p.get_pretty_string())); + io.write_error(&format!( + "<info>Found multiple matches, selected {}.</info>", + p.get_pretty_string() + )); // alternatives message omitted for brevity (already logged via p being selected) io.write_error("<comment>Please use a more specific constraint to pick a different package.</comment>"); p } else if packages.len() == 1 { let p = packages.into_iter().next().unwrap(); - io.write_error(&format!("<info>Found an exact match {}.</info>", p.get_pretty_string())); + io.write_error(&format!( + "<info>Found an exact match {}.</info>", + p.get_pretty_string() + )); p } else { - io.write_error(&format!("<error>Could not find a package matching {}.</error>", package_name)); + io.write_error(&format!( + "<error>Could not find a package matching {}.</error>", + package_name + )); return Ok(None); }; - if (package.as_any() as &dyn Any).downcast_ref::<dyn CompletePackageInterface>().is_none() { + if (package.as_any() as &dyn Any) + .downcast_ref::<dyn CompletePackageInterface>() + .is_none() + { return Err(LogicException { - message: format!("Expected a CompletePackageInterface instance but found {}", get_debug_type(package.as_php_mixed())), + message: format!( + "Expected a CompletePackageInterface instance but found {}", + get_debug_type(package.as_php_mixed()) + ), code: 0, - }.into()); + } + .into()); } - if (package.as_any() as &dyn Any).downcast_ref::<BasePackage>().is_none() { + if (package.as_any() as &dyn Any) + .downcast_ref::<BasePackage>() + .is_none() + { return Err(LogicException { - message: format!("Expected a BasePackage instance but found {}", get_debug_type(package.as_php_mixed())), + message: format!( + "Expected a BasePackage instance but found {}", + get_debug_type(package.as_php_mixed()) + ), code: 0, - }.into()); + } + .into()); } Ok(Some(package.into_complete())) diff --git a/crates/shirabe/src/command/audit_command.rs b/crates/shirabe/src/command/audit_command.rs index 6889c99..7fb6c5f 100644 --- a/crates/shirabe/src/command/audit_command.rs +++ b/crates/shirabe/src/command/audit_command.rs @@ -1,9 +1,5 @@ //! ref: composer/src/Composer/Command/AuditCommand.php -use anyhow::Result; -use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; -use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; -use shirabe_php_shim::{array_fill_keys, array_merge, implode, in_array, InvalidArgumentException, PhpMixed, UnexpectedValueException}; use crate::advisory::audit_config::AuditConfig; use crate::advisory::auditor::Auditor; use crate::command::base_command::BaseCommand; @@ -13,6 +9,13 @@ use crate::package::package_interface::PackageInterface; use crate::repository::installed_repository::InstalledRepository; use crate::repository::repository_set::RepositorySet; use crate::repository::repository_utils::RepositoryUtils; +use anyhow::Result; +use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; +use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; +use shirabe_php_shim::{ + InvalidArgumentException, PhpMixed, UnexpectedValueException, array_fill_keys, array_merge, + implode, in_array, +}; #[derive(Debug)] pub struct AuditCommand { @@ -40,12 +43,18 @@ impl AuditCommand { ); } - pub fn execute(&mut self, input: &dyn InputInterface, _output: &dyn OutputInterface) -> Result<i64> { + pub fn execute( + &mut self, + input: &dyn InputInterface, + _output: &dyn OutputInterface, + ) -> Result<i64> { let composer = self.inner.require_composer()?; let packages = self.get_packages(&composer, input)?; if packages.is_empty() { - self.inner.get_io().write_error("No packages - skipping audit."); + self.inner + .get_io() + .write_error("No packages - skipping audit."); return Ok(0); } @@ -57,12 +66,31 @@ impl AuditCommand { let audit_config = AuditConfig::from_config(composer.get_config())?; - let abandoned = input.get_option("abandoned").as_string_opt().map(|s| s.to_string()); - if abandoned.is_some() && !in_array(PhpMixed::String(abandoned.clone().unwrap()), &PhpMixed::from(Auditor::ABANDONEDS.to_vec()), true) { + let abandoned = input + .get_option("abandoned") + .as_string_opt() + .map(|s| s.to_string()); + if abandoned.is_some() + && !in_array( + PhpMixed::String(abandoned.clone().unwrap()), + &PhpMixed::from(Auditor::ABANDONEDS.to_vec()), + true, + ) + { return Err(InvalidArgumentException { - message: format!("--abandoned must be one of {}.", implode(", ", &Auditor::ABANDONEDS.iter().map(|s| s.to_string()).collect::<Vec<_>>())), + message: format!( + "--abandoned must be one of {}.", + implode( + ", ", + &Auditor::ABANDONEDS + .iter() + .map(|s| s.to_string()) + .collect::<Vec<_>>() + ) + ), code: 0, - }.into()); + } + .into()); } let abandoned = abandoned.unwrap_or_else(|| audit_config.audit_abandoned.clone()); @@ -71,23 +99,33 @@ impl AuditCommand { array_fill_keys(input.get_option("ignore-severity"), PhpMixed::Null), PhpMixed::from(audit_config.ignore_severity_for_audit.clone()), ); - let ignore_unreachable = input.get_option("ignore-unreachable").as_bool().unwrap_or(false) || audit_config.ignore_unreachable; + let ignore_unreachable = input + .get_option("ignore-unreachable") + .as_bool() + .unwrap_or(false) + || audit_config.ignore_unreachable; - Ok(auditor.audit( - self.inner.get_io(), - &repo_set, - &packages, - &self.inner.get_audit_format(input, "format"), - false, - &audit_config.ignore_list_for_audit, - &abandoned, - &ignore_severities, - ignore_unreachable, - &audit_config.ignore_abandoned_for_audit, - )?.min(255)) + Ok(auditor + .audit( + self.inner.get_io(), + &repo_set, + &packages, + &self.inner.get_audit_format(input, "format"), + false, + &audit_config.ignore_list_for_audit, + &abandoned, + &ignore_severities, + ignore_unreachable, + &audit_config.ignore_abandoned_for_audit, + )? + .min(255)) } - fn get_packages(&self, composer: &Composer, input: &dyn InputInterface) -> Result<Vec<Box<dyn PackageInterface>>> { + fn get_packages( + &self, + composer: &Composer, + input: &dyn InputInterface, + ) -> Result<Vec<Box<dyn PackageInterface>>> { if input.get_option("locked").as_bool().unwrap_or(false) { if !composer.get_locker().is_locked() { return Err(UnexpectedValueException { @@ -96,14 +134,21 @@ impl AuditCommand { }.into()); } let locker = composer.get_locker(); - return Ok(locker.get_locked_repository(!input.get_option("no-dev").as_bool().unwrap_or(false))?.get_packages()); + return Ok(locker + .get_locked_repository(!input.get_option("no-dev").as_bool().unwrap_or(false))? + .get_packages()); } let root_pkg = composer.get_package(); - let installed_repo = InstalledRepository::new(vec![composer.get_repository_manager().get_local_repository()]); + let installed_repo = InstalledRepository::new(vec![ + composer.get_repository_manager().get_local_repository(), + ]); if input.get_option("no-dev").as_bool().unwrap_or(false) { - return Ok(RepositoryUtils::filter_required_packages(installed_repo.get_packages(), root_pkg)); + return Ok(RepositoryUtils::filter_required_packages( + installed_repo.get_packages(), + root_pkg, + )); } Ok(installed_repo.get_packages()) diff --git a/crates/shirabe/src/command/base_command.rs b/crates/shirabe/src/command/base_command.rs index 704b932..77777ef 100644 --- a/crates/shirabe/src/command/base_command.rs +++ b/crates/shirabe/src/command/base_command.rs @@ -11,8 +11,8 @@ use shirabe_external_packages::symfony::component::console::input::input_interfa use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface; use shirabe_external_packages::symfony::component::console::terminal::Terminal; use shirabe_php_shim::{ - count, explode, in_array, is_string, max, InvalidArgumentException, LogicException, PhpMixed, - RuntimeException, UnexpectedValueException, + InvalidArgumentException, LogicException, PhpMixed, RuntimeException, UnexpectedValueException, + count, explode, in_array, is_string, max, }; use crate::advisory::audit_config::AuditConfig; @@ -71,7 +71,9 @@ impl BaseCommand { disable_scripts: Option<bool>, ) -> Result<Option<Composer>> { if required { - return Ok(Some(self.require_composer(disable_plugins, disable_scripts)?)); + return Ok(Some( + self.require_composer(disable_plugins, disable_scripts)?, + )); } Ok(self.try_composer(disable_plugins, disable_scripts)) @@ -267,8 +269,14 @@ impl BaseCommand { ("COMPOSER_PREFER_LOWEST", vec!["prefer-lowest"]), ("COMPOSER_MINIMAL_CHANGES", vec!["minimal-changes"]), ("COMPOSER_WITH_DEPENDENCIES", vec!["with-dependencies"]), - ("COMPOSER_WITH_ALL_DEPENDENCIES", vec!["with-all-dependencies"]), - ("COMPOSER_NO_SECURITY_BLOCKING", vec!["no-security-blocking"]), + ( + "COMPOSER_WITH_ALL_DEPENDENCIES", + vec!["with-all-dependencies"], + ), + ( + "COMPOSER_NO_SECURITY_BLOCKING", + vec!["no-security-blocking"], + ), ] .into_iter() .collect(); @@ -285,7 +293,10 @@ impl BaseCommand { } if true == input.has_option("ignore-platform-reqs") { - if !input.get_option("ignore-platform-reqs").as_bool().unwrap_or(false) + if !input + .get_option("ignore-platform-reqs") + .as_bool() + .unwrap_or(false) && Platform::get_env("COMPOSER_IGNORE_PLATFORM_REQS") .as_bool() .unwrap_or(false) @@ -304,7 +315,10 @@ impl BaseCommand { .unwrap_or(false)) { let ignore_platform_req_env = Platform::get_env("COMPOSER_IGNORE_PLATFORM_REQ"); - let ignore_str = ignore_platform_req_env.as_string().unwrap_or("").to_string(); + let ignore_str = ignore_platform_req_env + .as_string() + .unwrap_or("") + .to_string(); if 0 == count(&input.get_option("ignore-platform-req")) && is_string(&ignore_platform_req_env) && "" != ignore_str @@ -385,9 +399,7 @@ impl BaseCommand { return Ok((prefer_source, prefer_dist)); } - if input.has_option("prefer-install") - && is_string(&input.get_option("prefer-install")) - { + if input.has_option("prefer-install") && is_string(&input.get_option("prefer-install")) { if input.get_option("prefer-source").as_bool().unwrap_or(false) { return Err(InvalidArgumentException { message: "--prefer-source can not be used together with --prefer-install" @@ -450,9 +462,7 @@ impl BaseCommand { &self, input: &dyn InputInterface, ) -> Result<Box<dyn PlatformRequirementFilterInterface>> { - if !input.has_option("ignore-platform-reqs") - || !input.has_option("ignore-platform-req") - { + if !input.has_option("ignore-platform-reqs") || !input.has_option("ignore-platform-req") { return Err(LogicException { message: "Calling getPlatformRequirementFilter from a command which does not define the --ignore-platform-req[s] flags is not permitted." @@ -462,7 +472,12 @@ impl BaseCommand { .into()); } - if true == input.get_option("ignore-platform-reqs").as_bool().unwrap_or(false) { + if true + == input + .get_option("ignore-platform-reqs") + .as_bool() + .unwrap_or(false) + { return Ok(PlatformRequirementFilterFactory::ignore_all()); } @@ -541,11 +556,7 @@ impl BaseCommand { /// @internal /// @param 'format'|'audit-format' $optName /// @return Auditor::FORMAT_* - pub fn get_audit_format( - &self, - input: &dyn InputInterface, - opt_name: &str, - ) -> Result<String> { + pub fn get_audit_format(&self, input: &dyn InputInterface, opt_name: &str) -> Result<String> { if !input.has_option(opt_name) { return Err(LogicException { message: format!( diff --git a/crates/shirabe/src/command/base_config_command.rs b/crates/shirabe/src/command/base_config_command.rs index 3d6e0da..74929da 100644 --- a/crates/shirabe/src/command/base_config_command.rs +++ b/crates/shirabe/src/command/base_config_command.rs @@ -1,9 +1,5 @@ //! ref: composer/src/Composer/Command/BaseConfigCommand.php -use indexmap::IndexMap; -use shirabe_php_shim::{touch, chmod, PhpMixed}; -use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; -use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; use crate::command::base_command::BaseCommand; use crate::config::Config; use crate::config::json_config_source::JsonConfigSource; @@ -11,6 +7,10 @@ use crate::factory::Factory; use crate::json::json_file::JsonFile; use crate::util::platform::Platform; use crate::util::silencer::Silencer; +use indexmap::IndexMap; +use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; +use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; +use shirabe_php_shim::{PhpMixed, chmod, touch}; #[derive(Debug)] pub struct BaseConfigCommand { @@ -63,7 +63,10 @@ impl BaseConfigCommand { touch(&path); self.config_file.as_mut().unwrap().write(PhpMixed::Array({ let mut m = IndexMap::new(); - m.insert("config".to_string(), Box::new(PhpMixed::Array(IndexMap::new()))); + m.insert( + "config".to_string(), + Box::new(PhpMixed::Array(IndexMap::new())), + ); m }))?; let _ = Silencer::call(|| { diff --git a/crates/shirabe/src/command/base_dependency_command.rs b/crates/shirabe/src/command/base_dependency_command.rs index 3a62a18..4ea779a 100644 --- a/crates/shirabe/src/command/base_dependency_command.rs +++ b/crates/shirabe/src/command/base_dependency_command.rs @@ -80,8 +80,9 @@ impl BaseDependencyCommand { if !locker.is_locked() { return Err(anyhow::anyhow!(UnexpectedValueException { - message: "A valid composer.lock file is required to run this command with --locked" - .to_string(), + message: + "A valid composer.lock file is required to run this command with --locked" + .to_string(), code: 0, })); } @@ -108,7 +109,10 @@ impl BaseDependencyCommand { repos.push(Box::new(local_repo)); let platform_overrides = composer.get_config().get("platform").unwrap_or_default(); - repos.push(Box::new(PlatformRepository::new(vec![], platform_overrides))); + repos.push(Box::new(PlatformRepository::new( + vec![], + platform_overrides, + ))); } let mut installed_repo = InstalledRepository::new(repos)?; @@ -158,11 +162,14 @@ impl BaseDependencyCommand { let parser = VersionParser::new(); let platform_constraint = parser.parse_constraints(&text_constraint)?; if platform_constraint.get_lower_bound() != Bound::zero() { - let version = platform_constraint.get_lower_bound().get_version().to_string(); + let version = platform_constraint + .get_lower_bound() + .get_version() + .to_string(); let temp_platform_pkg = Package::new(needle.clone(), version.clone(), version); - installed_repo.add_repository(Box::new(InstalledArrayRepository::new(vec![ - Box::new(temp_platform_pkg), - ])))?; + installed_repo.add_repository(Box::new(InstalledArrayRepository::new( + vec![Box::new(temp_platform_pkg)], + )))?; } } else { self.inner.get_io().write_error(&format!( @@ -221,9 +228,15 @@ impl BaseDependencyCommand { None }; - let render_tree = input.get_option(Self::OPTION_TREE).as_bool().unwrap_or(false); - let recursive = - render_tree || input.get_option(Self::OPTION_RECURSIVE).as_bool().unwrap_or(false); + let render_tree = input + .get_option(Self::OPTION_TREE) + .as_bool() + .unwrap_or(false); + let recursive = render_tree + || input + .get_option(Self::OPTION_RECURSIVE) + .as_bool() + .unwrap_or(false); let mut r#return: i64 = if inverted { 1 } else { 0 }; @@ -296,11 +309,7 @@ impl BaseDependencyCommand { Ok(r#return) } - pub(crate) fn print_table( - &self, - output: &dyn OutputInterface, - results: Vec<DependentsEntry>, - ) { + pub(crate) fn print_table(&self, output: &dyn OutputInterface, results: Vec<DependentsEntry>) { let mut table: Vec<Vec<String>> = vec![]; let mut doubles: IndexMap<String, bool> = IndexMap::new(); let mut results = results; @@ -316,12 +325,12 @@ impl BaseDependencyCommand { continue; } doubles.insert(unique.clone(), true); - let version = - if package.get_pretty_version() == RootPackage::DEFAULT_PRETTY_VERSION { - "-".to_string() - } else { - package.get_pretty_version().to_string() - }; + let version = if package.get_pretty_version() == RootPackage::DEFAULT_PRETTY_VERSION + { + "-".to_string() + } else { + package.get_pretty_version().to_string() + }; let package_url = PackageInfo::get_view_source_or_homepage_url(&*package); let name_with_link = match &package_url { Some(url) => format!( diff --git a/crates/shirabe/src/command/bump_command.rs b/crates/shirabe/src/command/bump_command.rs index 2787fcc..ddb17c2 100644 --- a/crates/shirabe/src/command/bump_command.rs +++ b/crates/shirabe/src/command/bump_command.rs @@ -4,7 +4,7 @@ use anyhow::Result; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; -use shirabe_php_shim::{file_get_contents, file_put_contents, is_writable, strtolower, PhpMixed}; +use shirabe_php_shim::{PhpMixed, file_get_contents, file_put_contents, is_writable, strtolower}; use crate::command::base_command::BaseCommand; use crate::command::completion_trait::CompletionTrait; @@ -100,7 +100,10 @@ impl BumpCommand { if !Filesystem::is_readable(&composer_json_path) { io.write_error( - PhpMixed::String(format!("<error>{} is not readable.</error>", composer_json_path)), + PhpMixed::String(format!( + "<error>{} is not readable.</error>", + composer_json_path + )), true, IOInterface::NORMAL, ); @@ -215,12 +218,9 @@ impl BumpCommand { .collect::<std::collections::HashSet<_>>() .into_iter() .collect(); - let pattern = - BasePackage::package_names_to_regexp(&unique_lower); + let pattern = BasePackage::package_names_to_regexp(&unique_lower); for (key, reqs) in tasks.iter_mut() { - reqs.retain(|pkg_name, _| { - Preg::is_match(&pattern, pkg_name).unwrap_or(false) - }); + reqs.retain(|pkg_name, _| Preg::is_match(&pattern, pkg_name).unwrap_or(false)); } packages_filter } else { @@ -246,8 +246,8 @@ impl BumpCommand { package = alias.get_alias_of(); } - let bumped = bumper - .bump_requirement(link.get_constraint().as_ref(), package.as_ref())?; + let bumped = + bumper.bump_requirement(link.get_constraint().as_ref(), package.as_ref())?; if bumped == current_constraint { continue; diff --git a/crates/shirabe/src/command/check_platform_reqs_command.rs b/crates/shirabe/src/command/check_platform_reqs_command.rs index fe7c79b..fcf10aa 100644 --- a/crates/shirabe/src/command/check_platform_reqs_command.rs +++ b/crates/shirabe/src/command/check_platform_reqs_command.rs @@ -4,13 +4,13 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; -use shirabe_php_shim::{strip_tags, PhpMixed}; +use shirabe_php_shim::{PhpMixed, strip_tags}; use shirabe_semver::constraint::constraint::Constraint; use crate::command::base_command::BaseCommand; +use crate::console::input::input_option::InputOption; use crate::json::json_file::JsonFile; use crate::package::link::Link; -use crate::console::input::input_option::InputOption; use crate::repository::installed_repository::InstalledRepository; use crate::repository::platform_repository::PlatformRepository; use crate::repository::root_package_repository::RootPackageRepository; @@ -45,7 +45,11 @@ impl CheckPlatformReqsCommand { ); } - pub fn execute(&self, input: &dyn InputInterface, _output: &dyn OutputInterface) -> Result<i64> { + pub fn execute( + &self, + input: &dyn InputInterface, + _output: &dyn OutputInterface, + ) -> Result<i64> { let composer = self.inner.require_composer()?; let io = self.inner.get_io(); @@ -82,19 +86,26 @@ impl CheckPlatformReqsCommand { if !no_dev { for (require, link) in composer.get_package().get_dev_requires() { - requires.entry(require.to_string()).or_insert_with(Vec::new).push(link.clone()); + requires + .entry(require.to_string()) + .or_insert_with(Vec::new) + .push(link.clone()); } } let root_pkg_repo = RootPackageRepository::new(composer.get_package().clone_box()); - let installed_repo = InstalledRepository::new(vec![installed_repo_base, Box::new(root_pkg_repo)]); + let installed_repo = + InstalledRepository::new(vec![installed_repo_base, Box::new(root_pkg_repo)]); for package in installed_repo.get_packages() { if remove_packages.contains(&package.get_name().to_string()) { continue; } for (require, link) in package.get_requires() { - requires.entry(require.to_string()).or_insert_with(Vec::new).push(link.clone()); + requires + .entry(require.to_string()) + .or_insert_with(Vec::new) + .push(link.clone()); } } @@ -111,7 +122,8 @@ impl CheckPlatformReqsCommand { 'requirements: for (require, links) in &requires_sorted { if PlatformRepository::is_platform_package(require) { - let candidates = installed_repo_with_platform.find_packages_with_replacers_and_providers(require); + let candidates = installed_repo_with_platform + .find_packages_with_replacers_and_providers(require); if !candidates.is_empty() { let mut req_results: Vec<CheckResult> = vec![]; 'candidates: for candidate in &candidates { @@ -121,7 +133,11 @@ impl CheckPlatformReqsCommand { Some(c) } else { let mut found = None; - for link in candidate.get_provides().iter().chain(candidate.get_replaces().iter()) { + for link in candidate + .get_provides() + .iter() + .chain(candidate.get_replaces().iter()) + { if link.get_target() == require { found = Some(link.get_constraint().clone_box()); break; @@ -149,7 +165,10 @@ impl CheckPlatformReqsCommand { provider: if candidate.get_name() == require { String::new() } else { - format!("<comment>provided by {}</comment>", candidate.get_pretty_name()) + format!( + "<comment>provided by {}</comment>", + candidate.get_pretty_name() + ) }, }); continue 'candidates; @@ -168,7 +187,10 @@ impl CheckPlatformReqsCommand { provider: if candidate.get_name() == require { String::new() } else { - format!("<comment>provided by {}</comment>", candidate.get_pretty_name()) + format!( + "<comment>provided by {}</comment>", + candidate.get_pretty_name() + ) }, }); continue 'requirements; @@ -190,7 +212,11 @@ impl CheckPlatformReqsCommand { } } - let format = input.get_option("format").as_string().unwrap_or("text").to_string(); + let format = input + .get_option("format") + .as_string() + .unwrap_or("text") + .to_string(); self.print_table(_output, &results, &format); Ok(exit_code) @@ -200,49 +226,91 @@ impl CheckPlatformReqsCommand { let io = self.inner.get_io(); if format == "json" { - let rows: Vec<PhpMixed> = results.iter().map(|result| { - let mut row = IndexMap::new(); - row.insert("name".to_string(), Box::new(PhpMixed::String(result.platform_package.clone()))); - row.insert("version".to_string(), Box::new(PhpMixed::String(result.version.clone()))); - row.insert("status".to_string(), Box::new(PhpMixed::String(strip_tags(&result.status)))); - if let Some(link) = &result.link { - let mut failed_req = IndexMap::new(); - failed_req.insert("source".to_string(), Box::new(PhpMixed::String(link.get_source().to_string()))); - failed_req.insert("type".to_string(), Box::new(PhpMixed::String(link.get_description().to_string()))); - failed_req.insert("target".to_string(), Box::new(PhpMixed::String(link.get_target().to_string()))); - failed_req.insert("constraint".to_string(), Box::new(PhpMixed::String(link.get_pretty_constraint().unwrap_or("").to_string()))); - row.insert("failed_requirement".to_string(), Box::new(PhpMixed::Array(failed_req))); - } else { - row.insert("failed_requirement".to_string(), Box::new(PhpMixed::Null)); - } - let provider_str = strip_tags(&result.provider); - row.insert("provider".to_string(), Box::new(if provider_str.is_empty() { - PhpMixed::Null - } else { - PhpMixed::String(provider_str) - })); - PhpMixed::Array(row) - }).collect(); - - io.write(&JsonFile::encode(&PhpMixed::List(rows.into_iter().map(Box::new).collect()))); - } else { - let rows: Vec<Vec<PhpMixed>> = results.iter().map(|result| { - vec![ - PhpMixed::String(result.platform_package.clone()), - PhpMixed::String(result.version.clone()), + let rows: Vec<PhpMixed> = results + .iter() + .map(|result| { + let mut row = IndexMap::new(); + row.insert( + "name".to_string(), + Box::new(PhpMixed::String(result.platform_package.clone())), + ); + row.insert( + "version".to_string(), + Box::new(PhpMixed::String(result.version.clone())), + ); + row.insert( + "status".to_string(), + Box::new(PhpMixed::String(strip_tags(&result.status))), + ); if let Some(link) = &result.link { - PhpMixed::String(format!("{} {} {} ({})", - link.get_source(), - link.get_description(), - link.get_target(), - link.get_pretty_constraint().unwrap_or(""), - )) + let mut failed_req = IndexMap::new(); + failed_req.insert( + "source".to_string(), + Box::new(PhpMixed::String(link.get_source().to_string())), + ); + failed_req.insert( + "type".to_string(), + Box::new(PhpMixed::String(link.get_description().to_string())), + ); + failed_req.insert( + "target".to_string(), + Box::new(PhpMixed::String(link.get_target().to_string())), + ); + failed_req.insert( + "constraint".to_string(), + Box::new(PhpMixed::String( + link.get_pretty_constraint().unwrap_or("").to_string(), + )), + ); + row.insert( + "failed_requirement".to_string(), + Box::new(PhpMixed::Array(failed_req)), + ); } else { - PhpMixed::String(String::new()) - }, - PhpMixed::String(format!("{} {}", result.status, result.provider).trim_end().to_string()), - ] - }).collect(); + row.insert("failed_requirement".to_string(), Box::new(PhpMixed::Null)); + } + let provider_str = strip_tags(&result.provider); + row.insert( + "provider".to_string(), + Box::new(if provider_str.is_empty() { + PhpMixed::Null + } else { + PhpMixed::String(provider_str) + }), + ); + PhpMixed::Array(row) + }) + .collect(); + + io.write(&JsonFile::encode(&PhpMixed::List( + rows.into_iter().map(Box::new).collect(), + ))); + } else { + let rows: Vec<Vec<PhpMixed>> = results + .iter() + .map(|result| { + vec![ + PhpMixed::String(result.platform_package.clone()), + PhpMixed::String(result.version.clone()), + if let Some(link) = &result.link { + PhpMixed::String(format!( + "{} {} {} ({})", + link.get_source(), + link.get_description(), + link.get_target(), + link.get_pretty_constraint().unwrap_or(""), + )) + } else { + PhpMixed::String(String::new()) + }, + PhpMixed::String( + format!("{} {}", result.status, result.provider) + .trim_end() + .to_string(), + ), + ] + }) + .collect(); self.inner.render_table(rows, output); } diff --git a/crates/shirabe/src/command/clear_cache_command.rs b/crates/shirabe/src/command/clear_cache_command.rs index fe2c4dd..06026a4 100644 --- a/crates/shirabe/src/command/clear_cache_command.rs +++ b/crates/shirabe/src/command/clear_cache_command.rs @@ -1,12 +1,12 @@ //! ref: composer/src/Composer/Command/ClearCacheCommand.php -use indexmap::IndexMap; -use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; -use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; use crate::cache::Cache; use crate::command::base_command::BaseCommand; use crate::console::input::input_option::InputOption; use crate::factory::Factory; +use indexmap::IndexMap; +use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; +use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; #[derive(Debug)] pub struct ClearCacheCommand { @@ -19,22 +19,24 @@ impl ClearCacheCommand { .set_name("clear-cache") .set_aliases(vec!["clearcache".to_string(), "cc".to_string()]) .set_description("Clears composer's internal package cache") - .set_definition(vec![ - InputOption::new( - "gc", - None, - InputOption::VALUE_NONE, - "Only run garbage collection, not a full cache clear", - ), - ]) + .set_definition(vec![InputOption::new( + "gc", + None, + InputOption::VALUE_NONE, + "Only run garbage collection, not a full cache clear", + )]) .set_help( "The <info>clear-cache</info> deletes all cached packages from composer's\n\ cache directory.\n\n\ - Read more at https://getcomposer.org/doc/03-cli.md#clear-cache-clearcache-cc" + Read more at https://getcomposer.org/doc/03-cli.md#clear-cache-clearcache-cc", ); } - pub fn execute(&self, input: &dyn InputInterface, _output: &dyn OutputInterface) -> anyhow::Result<i64> { + pub fn execute( + &self, + input: &dyn InputInterface, + _output: &dyn OutputInterface, + ) -> anyhow::Result<i64> { let composer = self.inner.try_composer(); let config = if let Some(composer) = composer { composer.get_config() @@ -45,9 +47,18 @@ impl ClearCacheCommand { let io = self.inner.get_io(); let mut cache_paths: IndexMap<String, String> = IndexMap::new(); - cache_paths.insert("cache-vcs-dir".to_string(), config.get("cache-vcs-dir").to_string()); - cache_paths.insert("cache-repo-dir".to_string(), config.get("cache-repo-dir").to_string()); - cache_paths.insert("cache-files-dir".to_string(), config.get("cache-files-dir").to_string()); + cache_paths.insert( + "cache-vcs-dir".to_string(), + config.get("cache-vcs-dir").to_string(), + ); + cache_paths.insert( + "cache-repo-dir".to_string(), + config.get("cache-repo-dir").to_string(), + ); + cache_paths.insert( + "cache-files-dir".to_string(), + config.get("cache-files-dir").to_string(), + ); cache_paths.insert("cache-dir".to_string(), config.get("cache-dir").to_string()); for (key, cache_path) in &cache_paths { @@ -59,28 +70,39 @@ impl ClearCacheCommand { let cache_path = shirabe_php_shim::realpath(cache_path); if !cache_path.as_ref().map(|s| !s.is_empty()).unwrap_or(false) { let cache_path_display = cache_path.as_deref().unwrap_or(""); - io.write_error(&format!("<info>Cache directory does not exist ({key}): {cache_path_display}</info>")); + io.write_error(&format!( + "<info>Cache directory does not exist ({key}): {cache_path_display}</info>" + )); continue; } let cache_path = cache_path.unwrap(); let mut cache = Cache::new(io, &cache_path); cache.set_read_only(config.get("cache-read-only").as_bool().unwrap_or(false)); if !cache.is_enabled() { - io.write_error(&format!("<info>Cache is not enabled ({key}): {cache_path}</info>")); + io.write_error(&format!( + "<info>Cache is not enabled ({key}): {cache_path}</info>" + )); continue; } if input.get_option("gc").as_bool() { - io.write_error(&format!("<info>Garbage-collecting cache ({key}): {cache_path}</info>")); + io.write_error(&format!( + "<info>Garbage-collecting cache ({key}): {cache_path}</info>" + )); if key == "cache-files-dir" { - cache.gc(config.get("cache-files-ttl"), config.get("cache-files-maxsize"))?; + cache.gc( + config.get("cache-files-ttl"), + config.get("cache-files-maxsize"), + )?; } else if key == "cache-repo-dir" { cache.gc(config.get("cache-ttl"), 1024 * 1024 * 1024)?; } else if key == "cache-vcs-dir" { cache.gc_vcs_cache(config.get("cache-ttl"))?; } } else { - io.write_error(&format!("<info>Clearing cache ({key}): {cache_path}</info>")); + io.write_error(&format!( + "<info>Clearing cache ({key}): {cache_path}</info>" + )); cache.clear()?; } } diff --git a/crates/shirabe/src/command/completion_trait.rs b/crates/shirabe/src/command/completion_trait.rs index 2594407..8278603 100644 --- a/crates/shirabe/src/command/completion_trait.rs +++ b/crates/shirabe/src/command/completion_trait.rs @@ -1,8 +1,5 @@ //! ref: composer/src/Composer/Command/CompletionTrait.php -use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_external_packages::symfony::component::console::completion::completion_input::CompletionInput; -use shirabe_php_shim::preg_quote; use crate::composer::Composer; use crate::package::base_package::BasePackage; use crate::package::package_interface::PackageInterface; @@ -11,9 +8,16 @@ use crate::repository::installed_repository::InstalledRepository; use crate::repository::platform_repository::PlatformRepository; use crate::repository::repository_interface::RepositoryInterface; use crate::repository::root_package_repository::RootPackageRepository; +use shirabe_external_packages::composer::pcre::preg::Preg; +use shirabe_external_packages::symfony::component::console::completion::completion_input::CompletionInput; +use shirabe_php_shim::preg_quote; pub trait CompletionTrait { - fn require_composer(&self, disable_plugins: Option<bool>, disable_scripts: Option<bool>) -> Composer; + fn require_composer( + &self, + disable_plugins: Option<bool>, + disable_scripts: Option<bool>, + ) -> Composer; fn suggest_prefer_install(&self) -> Vec<String> { vec!["dist".to_string(), "source".to_string(), "auto".to_string()] @@ -23,26 +27,46 @@ pub trait CompletionTrait { Box::new(move |_input: &CompletionInput| -> Vec<String> { let composer = self.require_composer(None, None); - let requires: Vec<String> = composer.get_package().get_requires().keys().cloned().collect(); - let dev_requires: Vec<String> = composer.get_package().get_dev_requires().keys().cloned().collect(); + let requires: Vec<String> = composer + .get_package() + .get_requires() + .keys() + .cloned() + .collect(); + let dev_requires: Vec<String> = composer + .get_package() + .get_dev_requires() + .keys() + .cloned() + .collect(); [requires, dev_requires].concat() }) } - fn suggest_installed_package(&self, include_root_package: bool, include_platform_packages: bool) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> { + fn suggest_installed_package( + &self, + include_root_package: bool, + include_platform_packages: bool, + ) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> { Box::new(move |input: &CompletionInput| -> Vec<String> { let composer = self.require_composer(None, None); - let mut installed_repos: Vec<Box<dyn crate::repository::repository_interface::RepositoryInterface>> = Vec::new(); + let mut installed_repos: Vec< + Box<dyn crate::repository::repository_interface::RepositoryInterface>, + > = Vec::new(); if include_root_package { - installed_repos.push(Box::new(RootPackageRepository::new(composer.get_package().clone()))); + installed_repos.push(Box::new(RootPackageRepository::new( + composer.get_package().clone(), + ))); } let locker = composer.get_locker(); if locker.is_locked() { installed_repos.push(Box::new(locker.get_locked_repository(true))); } else { - installed_repos.push(Box::new(composer.get_repository_manager().get_local_repository())); + installed_repos.push(Box::new( + composer.get_repository_manager().get_local_repository(), + )); } let mut platform_hint: Vec<String> = Vec::new(); @@ -54,7 +78,8 @@ pub trait CompletionTrait { }; if input.get_completion_value() == "" { // to reduce noise, when no text is yet entered we list only two entries for ext- and lib- prefixes - let mut hints_to_find: indexmap::IndexMap<String, i64> = indexmap::IndexMap::new(); + let mut hints_to_find: indexmap::IndexMap<String, i64> = + indexmap::IndexMap::new(); hints_to_find.insert("ext-".to_string(), 0); hints_to_find.insert("lib-".to_string(), 0); hints_to_find.insert("php".to_string(), 99); @@ -70,7 +95,11 @@ pub trait CompletionTrait { hints_to_find.remove(hint_prefix); platform_hint.push(format!( "{}...", - &pkg.get_name()[..pkg.get_name().len().saturating_sub(3).max(hint_prefix.len() + 1)] + &pkg.get_name()[..pkg + .get_name() + .len() + .saturating_sub(3) + .max(hint_prefix.len() + 1)] )); } continue 'pkg_loop; @@ -84,7 +113,9 @@ pub trait CompletionTrait { let installed_repo = InstalledRepository::new(installed_repos); - let mut result: Vec<String> = installed_repo.get_packages().iter() + let mut result: Vec<String> = installed_repo + .get_packages() + .iter() .map(|package| package.get_name().to_string()) .collect(); result.extend(platform_hint); @@ -92,25 +123,36 @@ pub trait CompletionTrait { }) } - fn suggest_installed_package_types(&self, include_root_package: bool) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> { + fn suggest_installed_package_types( + &self, + include_root_package: bool, + ) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> { Box::new(move |_input: &CompletionInput| -> Vec<String> { let composer = self.require_composer(None, None); - let mut installed_repos: Vec<Box<dyn crate::repository::repository_interface::RepositoryInterface>> = Vec::new(); + let mut installed_repos: Vec< + Box<dyn crate::repository::repository_interface::RepositoryInterface>, + > = Vec::new(); if include_root_package { - installed_repos.push(Box::new(RootPackageRepository::new(composer.get_package().clone()))); + installed_repos.push(Box::new(RootPackageRepository::new( + composer.get_package().clone(), + ))); } let locker = composer.get_locker(); if locker.is_locked() { installed_repos.push(Box::new(locker.get_locked_repository(true))); } else { - installed_repos.push(Box::new(composer.get_repository_manager().get_local_repository())); + installed_repos.push(Box::new( + composer.get_repository_manager().get_local_repository(), + )); } let installed_repo = InstalledRepository::new(installed_repos); - let mut types: Vec<String> = installed_repo.get_packages().iter() + let mut types: Vec<String> = installed_repo + .get_packages() + .iter() .map(|package| package.get_type().to_string()) .collect(); types.sort(); @@ -119,14 +161,18 @@ pub trait CompletionTrait { }) } - fn suggest_available_package(&self, max: i64) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> { + fn suggest_available_package( + &self, + max: i64, + ) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> { Box::new(move |input: &CompletionInput| -> Vec<String> { if max < 1 { return Vec::new(); } let composer = self.require_composer(None, None); - let repos = CompositeRepository::new(composer.get_repository_manager().get_repositories()); + let repos = + CompositeRepository::new(composer.get_repository_manager().get_repositories()); let mut results: Vec<String>; let mut show_vendors = false; @@ -154,7 +200,8 @@ pub trait CompletionTrait { } if show_vendors { - let mut results: Vec<String> = results.into_iter() + let mut results: Vec<String> = results + .into_iter() .map(|name| format!("{}/", name)) .collect(); @@ -188,13 +235,16 @@ pub trait CompletionTrait { }) } - fn suggest_available_package_incl_platform(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> { + fn suggest_available_package_incl_platform( + &self, + ) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> { Box::new(move |input: &CompletionInput| -> Vec<String> { - let matches = if Preg::is_match(r"{^(ext|lib|php)(-|$)|^com}", input.get_completion_value()) { - self.suggest_platform_package()(input) - } else { - Vec::new() - }; + let matches = + if Preg::is_match(r"{^(ext|lib|php)(-|$)|^com}", input.get_completion_value()) { + self.suggest_platform_package()(input) + } else { + Vec::new() + }; let max = 99i64 - matches.len() as i64; let mut result = matches; @@ -207,12 +257,17 @@ pub trait CompletionTrait { Box::new(move |input: &CompletionInput| -> Vec<String> { let repos = PlatformRepository::new( vec![], - self.require_composer(None, None).get_config().get("platform"), + self.require_composer(None, None) + .get_config() + .get("platform"), ); - let pattern = BasePackage::package_name_to_regexp(&format!("{}*", input.get_completion_value())); + let pattern = + BasePackage::package_name_to_regexp(&format!("{}*", input.get_completion_value())); - repos.get_packages().iter() + repos + .get_packages() + .iter() .map(|package| package.get_name().to_string()) .filter(|name| Preg::is_match(&pattern, name)) .collect() diff --git a/crates/shirabe/src/command/config_command.rs b/crates/shirabe/src/command/config_command.rs index 3924002..5237b62 100644 --- a/crates/shirabe/src/command/config_command.rs +++ b/crates/shirabe/src/command/config_command.rs @@ -8,19 +8,18 @@ use shirabe_external_packages::symfony::component::console::input::input_interfa use shirabe_external_packages::symfony::component::console::input::input_option::InputOption; use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface; use shirabe_php_shim::{ - array_filter, array_filter_use_key, array_is_list, array_map, array_merge, array_unique, - call_user_func, count, escapeshellcmd, exec, explode, file_exists, file_get_contents, implode, - in_array, is_array, is_bool, is_dir, is_numeric, is_object, is_string, json_encode, key, sort, - sprintf, str_replace, str_starts_with, strpos, strtolower, system, touch, var_export, - InvalidArgumentException, JsonObject, PhpMixed, RuntimeException, - ArrayObject, - JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, + ArrayObject, InvalidArgumentException, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, + JsonObject, PhpMixed, RuntimeException, array_filter, array_filter_use_key, array_is_list, + array_map, array_merge, array_unique, call_user_func, count, escapeshellcmd, exec, explode, + file_exists, file_get_contents, implode, in_array, is_array, is_bool, is_dir, is_numeric, + is_object, is_string, json_encode, key, sort, sprintf, str_replace, str_starts_with, strpos, + strtolower, system, touch, var_export, }; use crate::advisory::auditor::Auditor; use crate::command::base_config_command::BaseConfigCommand; -use crate::config::json_config_source::JsonConfigSource; use crate::config::Config; +use crate::config::json_config_source::JsonConfigSource; use crate::console::input::input_argument::InputArgument; use crate::factory::Factory; use crate::io::io_interface::IOInterface; @@ -120,23 +119,50 @@ impl ConfigCommand { ) -> anyhow::Result<()> { self.inner.initialize(input, output)?; - let auth_config_file = self.inner.get_auth_config_file(input, self.inner.config.as_ref().unwrap()); + let auth_config_file = self + .inner + .get_auth_config_file(input, self.inner.config.as_ref().unwrap()); - self.auth_config_file = Some(JsonFile::new(auth_config_file, None, Some(self.inner.inner.get_io()))); - self.auth_config_source = Some(JsonConfigSource::new_with_auth(self.auth_config_file.as_ref().unwrap(), true)); + self.auth_config_file = Some(JsonFile::new( + auth_config_file, + None, + Some(self.inner.inner.get_io()), + )); + self.auth_config_source = Some(JsonConfigSource::new_with_auth( + self.auth_config_file.as_ref().unwrap(), + true, + )); // Initialize the global file if it's not there, ignoring any warnings or notices - if input.get_option("global").as_bool() == Some(true) && !self.auth_config_file.as_ref().unwrap().exists() { + if input.get_option("global").as_bool() == Some(true) + && !self.auth_config_file.as_ref().unwrap().exists() + { touch(self.auth_config_file.as_ref().unwrap().get_path()); let mut empty_objs: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); for k in &[ - "bitbucket-oauth", "github-oauth", "gitlab-oauth", "gitlab-token", - "http-basic", "bearer", "forgejo-token", + "bitbucket-oauth", + "github-oauth", + "gitlab-oauth", + "gitlab-token", + "http-basic", + "bearer", + "forgejo-token", ] { - empty_objs.insert(k.to_string(), Box::new(PhpMixed::Object(ArrayObject::new()))); + empty_objs.insert( + k.to_string(), + Box::new(PhpMixed::Object(ArrayObject::new())), + ); } - self.auth_config_file.as_mut().unwrap().write(PhpMixed::Array(empty_objs))?; - let path_clone = self.auth_config_file.as_ref().unwrap().get_path().to_string(); + self.auth_config_file + .as_mut() + .unwrap() + .write(PhpMixed::Array(empty_objs))?; + let path_clone = self + .auth_config_file + .as_ref() + .unwrap() + .get_path() + .to_string(); Silencer::call(|| { shirabe_php_shim::chmod(&path_clone, 0o600); Ok(()) @@ -169,15 +195,28 @@ impl ConfigCommand { } let file = if input.get_option("auth").as_bool() == Some(true) { - self.auth_config_file.as_ref().unwrap().get_path().to_string() + self.auth_config_file + .as_ref() + .unwrap() + .get_path() + .to_string() } else { - self.inner.config_file.as_ref().unwrap().get_path().to_string() + self.inner + .config_file + .as_ref() + .unwrap() + .get_path() + .to_string() }; system(&format!( "{} {}{}", editor.unwrap_or_default(), file, - if Platform::is_windows() { "" } else { " > `tty`" } + if Platform::is_windows() { + "" + } else { + " > `tty`" + } )); return Ok(0); @@ -201,7 +240,10 @@ impl ConfigCommand { ); } - self.inner.inner.get_io().load_configuration(self.inner.config.as_ref().unwrap()); + self.inner + .inner + .get_io() + .load_configuration(self.inner.config.as_ref().unwrap()); // List the configuration of the file settings if input.get_option("list").as_bool() == Some(true) { @@ -226,7 +268,11 @@ impl ConfigCommand { let setting_values_raw = input.get_argument("setting-value"); let setting_values: Vec<String> = setting_values_raw .as_list() - .map(|l| l.iter().filter_map(|v| v.as_string().map(|s| s.to_string())).collect()) + .map(|l| { + l.iter() + .filter_map(|v| v.as_string().map(|s| s.to_string())) + .collect() + }) .unwrap_or_default(); if !setting_values.is_empty() && input.get_option("unset").as_bool() == Some(true) { return Err(RuntimeException { @@ -243,7 +289,10 @@ impl ConfigCommand { properties_defaults.insert("type".to_string(), PhpMixed::String("library".to_string())); properties_defaults.insert("description".to_string(), PhpMixed::String(String::new())); properties_defaults.insert("homepage".to_string(), PhpMixed::String(String::new())); - properties_defaults.insert("minimum-stability".to_string(), PhpMixed::String("stable".to_string())); + properties_defaults.insert( + "minimum-stability".to_string(), + PhpMixed::String("stable".to_string()), + ); properties_defaults.insert("prefer-stable".to_string(), PhpMixed::Bool(false)); properties_defaults.insert("keywords".to_string(), PhpMixed::List(vec![])); properties_defaults.insert("license".to_string(), PhpMixed::List(vec![])); @@ -251,22 +300,46 @@ impl ConfigCommand { properties_defaults.insert("extra".to_string(), PhpMixed::List(vec![])); let raw_data = self.inner.config_file.as_ref().unwrap().read()?; let mut data = self.inner.config.as_ref().unwrap().all(); - let mut source = self.inner.config.as_ref().unwrap().get_source_of_value(&setting_key); + let mut source = self + .inner + .config + .as_ref() + .unwrap() + .get_source_of_value(&setting_key); let mut value: PhpMixed; let mut matches: Vec<String> = vec![]; - if Preg::is_match("/^repos?(?:itories)?(?:\\.(.+))?/", &setting_key, Some(&mut matches)).unwrap_or(false) { + if Preg::is_match( + "/^repos?(?:itories)?(?:\\.(.+))?/", + &setting_key, + Some(&mut matches), + ) + .unwrap_or(false) + { if matches.get(1).is_none() { - value = data.as_array().and_then(|a| a.get("repositories")).map(|v| (**v).clone()).unwrap_or_else(|| PhpMixed::Array(IndexMap::new())); + value = data + .as_array() + .and_then(|a| a.get("repositories")) + .map(|v| (**v).clone()) + .unwrap_or_else(|| PhpMixed::Array(IndexMap::new())); } else { let repo_key = matches[1].clone(); - let repos = data.as_array().and_then(|a| a.get("repositories")).map(|v| (**v).clone()); - value = match repos.as_ref().and_then(|r| r.as_array().and_then(|a| a.get(&repo_key))) { + let repos = data + .as_array() + .and_then(|a| a.get("repositories")) + .map(|v| (**v).clone()); + value = match repos + .as_ref() + .and_then(|r| r.as_array().and_then(|a| a.get(&repo_key))) + { Some(v) => (**v).clone(), - None => return Err(InvalidArgumentException { - message: format!("There is no {} repository defined", repo_key), - code: 0, - }.into()), + None => { + return Err(InvalidArgumentException { + message: format!("There is no {} repository defined", repo_key), + code: 0, + } + .into()); + } }; } } else if strpos(&setting_key, ".").is_some() { @@ -274,7 +347,11 @@ impl ConfigCommand { if bits[0] == "extra" || bits[0] == "suggest" { data = raw_data.clone(); } else { - data = data.as_array().and_then(|a| a.get("config")).map(|v| (**v).clone()).unwrap_or(PhpMixed::Null); + data = data + .as_array() + .and_then(|a| a.get("config")) + .map(|v| (**v).clone()) + .unwrap_or(PhpMixed::Null); } let mut r#match = false; let mut key_acc: Option<String> = None; @@ -303,10 +380,20 @@ impl ConfigCommand { } value = data; - } else if data.as_array().and_then(|a| a.get("config")).and_then(|c| c.as_array()).map(|c| c.contains_key(&setting_key)).unwrap_or(false) { + } else if data + .as_array() + .and_then(|a| a.get("config")) + .and_then(|c| c.as_array()) + .map(|c| c.contains_key(&setting_key)) + .unwrap_or(false) + { value = self.inner.config.as_ref().unwrap().get_with_flags( &setting_key, - if input.get_option("absolute").as_bool() == Some(true) { 0 } else { Config::RELATIVE_PATHS }, + if input.get_option("absolute").as_bool() == Some(true) { + 0 + } else { + Config::RELATIVE_PATHS + }, ); // ensure we get {} output for properties which are objects if value.as_array().map(|a| a.is_empty()).unwrap_or(false) { @@ -335,7 +422,11 @@ impl ConfigCommand { "object", &type_array .as_list() - .map(|l| l.iter().filter_map(|v| v.as_string().map(|s| s.to_string())).collect::<Vec<_>>()) + .map(|l| { + l.iter() + .filter_map(|v| v.as_string().map(|s| s.to_string())) + .collect::<Vec<_>>() + }) .unwrap_or_default(), true, ) { @@ -343,11 +434,20 @@ impl ConfigCommand { } } } - } else if raw_data.as_array().and_then(|a| a.get(&setting_key)).is_some() + } else if raw_data + .as_array() + .and_then(|a| a.get(&setting_key)) + .is_some() && in_array(setting_key.as_str(), &properties, true) { value = (**raw_data.as_array().unwrap().get(&setting_key).unwrap()).clone(); - source = self.inner.config_file.as_ref().unwrap().get_path().to_string(); + source = self + .inner + .config_file + .as_ref() + .unwrap() + .get_path() + .to_string(); } else if let Some(v) = properties_defaults.get(&setting_key) { value = v.clone(); source = "defaults".to_string(); @@ -384,7 +484,12 @@ impl ConfigCommand { let boolean_validator = |val: &PhpMixed| -> bool { in_array( val.as_string().unwrap_or(""), - &vec!["true".to_string(), "false".to_string(), "1".to_string(), "0".to_string()], + &vec![ + "true".to_string(), + "false".to_string(), + "1".to_string(), + "0".to_string(), + ], true, ) }; @@ -399,19 +504,39 @@ impl ConfigCommand { // allow unsetting audit config entirely if input.get_option("unset").as_bool() == Some(true) && setting_key == "audit" { - self.inner.config_source.as_mut().unwrap().remove_config_setting(&setting_key); + self.inner + .config_source + .as_mut() + .unwrap() + .remove_config_setting(&setting_key); return Ok(0); } if input.get_option("unset").as_bool() == Some(true) - && (unique_config_values.contains_key(&setting_key) || multi_config_values.contains_key(&setting_key)) + && (unique_config_values.contains_key(&setting_key) + || multi_config_values.contains_key(&setting_key)) { - if setting_key == "disable-tls" && self.inner.config.as_ref().unwrap().get("disable-tls").as_bool().unwrap_or(false) { - self.inner.inner.get_io().write_error("<info>You are now running Composer with SSL/TLS protection enabled.</info>"); + if setting_key == "disable-tls" + && self + .inner + .config + .as_ref() + .unwrap() + .get("disable-tls") + .as_bool() + .unwrap_or(false) + { + self.inner.inner.get_io().write_error( + "<info>You are now running Composer with SSL/TLS protection enabled.</info>", + ); } - self.inner.config_source.as_mut().unwrap().remove_config_setting(&setting_key); + self.inner + .config_source + .as_mut() + .unwrap() + .remove_config_setting(&setting_key); return Ok(0); } @@ -427,42 +552,69 @@ impl ConfigCommand { } // handle preferred-install per-package config let mut matches: Vec<String> = vec![]; - if Preg::is_match("/^preferred-install\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { + if Preg::is_match( + "/^preferred-install\\.(.+)/", + &setting_key, + Some(&mut matches), + ) + .unwrap_or(false) + { if input.get_option("unset").as_bool() == Some(true) { - self.inner.config_source.as_mut().unwrap().remove_config_setting(&setting_key); + self.inner + .config_source + .as_mut() + .unwrap() + .remove_config_setting(&setting_key); return Ok(0); } let validator = &unique_config_values.get("preferred-install").unwrap().0; - if !validator(&PhpMixed::String(values[0].clone())).as_bool().unwrap_or(false) { + if !validator(&PhpMixed::String(values[0].clone())) + .as_bool() + .unwrap_or(false) + { return Err(RuntimeException { - message: format!("Invalid value for {}. Should be one of: auto, source, or dist", setting_key), + message: format!( + "Invalid value for {}. Should be one of: auto, source, or dist", + setting_key + ), code: 0, } .into()); } - self.inner.config_source.as_mut().unwrap().add_config_setting(&setting_key, PhpMixed::String(values[0].clone())); + self.inner + .config_source + .as_mut() + .unwrap() + .add_config_setting(&setting_key, PhpMixed::String(values[0].clone())); return Ok(0); } // handle allow-plugins config setting elements true or false to add/remove let mut matches: Vec<String> = vec![]; - if Preg::is_match("{^allow-plugins\\.([a-zA-Z0-9/*-]+)}", &setting_key, Some(&mut matches)).unwrap_or(false) { + if Preg::is_match( + "{^allow-plugins\\.([a-zA-Z0-9/*-]+)}", + &setting_key, + Some(&mut matches), + ) + .unwrap_or(false) + { if input.get_option("unset").as_bool() == Some(true) { - self.inner.config_source.as_mut().unwrap().remove_config_setting(&setting_key); + self.inner + .config_source + .as_mut() + .unwrap() + .remove_config_setting(&setting_key); return Ok(0); } if !boolean_validator(&PhpMixed::String(values[0].clone())) { return Err(RuntimeException { - message: sprintf( - "\"%s\" is an invalid value", - &[values[0].clone().into()], - ), + message: sprintf("\"%s\" is an invalid value", &[values[0].clone().into()]), code: 0, } .into()); @@ -470,7 +622,11 @@ impl ConfigCommand { let normalized_value = boolean_normalizer(&PhpMixed::String(values[0].clone())); - self.inner.config_source.as_mut().unwrap().add_config_setting(&setting_key, normalized_value); + self.inner + .config_source + .as_mut() + .unwrap() + .add_config_setting(&setting_key, normalized_value); return Ok(0); } @@ -493,7 +649,11 @@ impl ConfigCommand { if input.get_option("unset").as_bool() == Some(true) && (unique_props.contains_key(&setting_key) || multi_props.contains_key(&setting_key)) { - self.inner.config_source.as_mut().unwrap().remove_property(&setting_key); + self.inner + .config_source + .as_mut() + .unwrap() + .remove_property(&setting_key); return Ok(0); } @@ -510,17 +670,33 @@ impl ConfigCommand { // handle repositories let mut matches: Vec<String> = vec![]; - if Preg::is_match_strict_groups("/^repos?(?:itories)?\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { + if Preg::is_match_strict_groups( + "/^repos?(?:itories)?\\.(.+)/", + &setting_key, + Some(&mut matches), + ) + .unwrap_or(false) + { if input.get_option("unset").as_bool() == Some(true) { - self.inner.config_source.as_mut().unwrap().remove_repository(&matches[1]); + self.inner + .config_source + .as_mut() + .unwrap() + .remove_repository(&matches[1]); return Ok(0); } if 2 == count(&values) { let mut repo: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); - repo.insert("type".to_string(), Box::new(PhpMixed::String(values[0].clone()))); - repo.insert("url".to_string(), Box::new(PhpMixed::String(values[1].clone()))); + repo.insert( + "type".to_string(), + Box::new(PhpMixed::String(values[0].clone())), + ); + repo.insert( + "url".to_string(), + Box::new(PhpMixed::String(values[1].clone())), + ); self.inner.config_source.as_mut().unwrap().add_repository( &matches[1], PhpMixed::Array(repo), @@ -533,7 +709,10 @@ impl ConfigCommand { if 1 == count(&values) { let value = strtolower(&values[0]); if boolean_validator(&PhpMixed::String(value.clone())) { - if !boolean_normalizer(&PhpMixed::String(value.clone())).as_bool().unwrap_or(false) { + if !boolean_normalizer(&PhpMixed::String(value.clone())) + .as_bool() + .unwrap_or(false) + { self.inner.config_source.as_mut().unwrap().add_repository( &matches[1], PhpMixed::Bool(false), @@ -565,7 +744,11 @@ impl ConfigCommand { let mut matches: Vec<String> = vec![]; if Preg::is_match("/^extra\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { if input.get_option("unset").as_bool() == Some(true) { - self.inner.config_source.as_mut().unwrap().remove_property(&setting_key); + self.inner + .config_source + .as_mut() + .unwrap() + .remove_property(&setting_key); return Ok(0); } @@ -592,7 +775,8 @@ impl ConfigCommand { )); } else { // PHP "+" operator on arrays: keep keys from left, fill from right - let mut merged: IndexMap<String, Box<PhpMixed>> = value.as_array().cloned().unwrap_or_default(); + let mut merged: IndexMap<String, Box<PhpMixed>> = + value.as_array().cloned().unwrap_or_default(); if let Some(cv) = current_value.as_array() { for (k, v) in cv { if !merged.contains_key(k) { @@ -605,7 +789,11 @@ impl ConfigCommand { } } } - self.inner.config_source.as_mut().unwrap().add_property(&setting_key, value); + self.inner + .config_source + .as_mut() + .unwrap() + .add_property(&setting_key, value); return Ok(0); } @@ -614,7 +802,11 @@ impl ConfigCommand { let mut matches: Vec<String> = vec![]; if Preg::is_match("/^suggest\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { if input.get_option("unset").as_bool() == Some(true) { - self.inner.config_source.as_mut().unwrap().remove_property(&setting_key); + self.inner + .config_source + .as_mut() + .unwrap() + .remove_property(&setting_key); return Ok(0); } @@ -629,10 +821,17 @@ impl ConfigCommand { } // handle unsetting extra/suggest - if in_array(setting_key.as_str(), &vec!["suggest".to_string(), "extra".to_string()], true) - && input.get_option("unset").as_bool() == Some(true) + if in_array( + setting_key.as_str(), + &vec!["suggest".to_string(), "extra".to_string()], + true, + ) && input.get_option("unset").as_bool() == Some(true) { - self.inner.config_source.as_mut().unwrap().remove_property(&setting_key); + self.inner + .config_source + .as_mut() + .unwrap() + .remove_property(&setting_key); return Ok(0); } @@ -641,7 +840,11 @@ impl ConfigCommand { let mut matches: Vec<String> = vec![]; if Preg::is_match("/^platform\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { if input.get_option("unset").as_bool() == Some(true) { - self.inner.config_source.as_mut().unwrap().remove_config_setting(&setting_key); + self.inner + .config_source + .as_mut() + .unwrap() + .remove_config_setting(&setting_key); return Ok(0); } @@ -651,14 +854,22 @@ impl ConfigCommand { } else { PhpMixed::String(values[0].clone()) }; - self.inner.config_source.as_mut().unwrap().add_config_setting(&setting_key, value); + self.inner + .config_source + .as_mut() + .unwrap() + .add_config_setting(&setting_key, value); return Ok(0); } // handle unsetting platform if setting_key == "platform" && input.get_option("unset").as_bool() == Some(true) { - self.inner.config_source.as_mut().unwrap().remove_config_setting(&setting_key); + self.inner + .config_source + .as_mut() + .unwrap() + .remove_config_setting(&setting_key); return Ok(0); } @@ -666,16 +877,28 @@ impl ConfigCommand { // handle audit.ignore and audit.ignore-abandoned with --merge support if in_array( setting_key.as_str(), - &vec!["audit.ignore".to_string(), "audit.ignore-abandoned".to_string()], + &vec![ + "audit.ignore".to_string(), + "audit.ignore-abandoned".to_string(), + ], true, ) { if input.get_option("unset").as_bool() == Some(true) { - self.inner.config_source.as_mut().unwrap().remove_config_setting(&setting_key); + self.inner + .config_source + .as_mut() + .unwrap() + .remove_config_setting(&setting_key); return Ok(0); } - let mut value: PhpMixed = PhpMixed::List(values.iter().map(|s| Box::new(PhpMixed::String(s.clone()))).collect()); + let mut value: PhpMixed = PhpMixed::List( + values + .iter() + .map(|s| Box::new(PhpMixed::String(s.clone()))) + .collect(), + ); if input.get_option("json").as_bool() == Some(true) { value = JsonFile::parse_json(&values[0], "composer.json")?; if !is_array(&value) { @@ -709,7 +932,8 @@ impl ConfigCommand { )); } else if !array_is_list(¤t_value) && !array_is_list(&value) { // Both are associative arrays (objects), merge them - let mut merged: IndexMap<String, Box<PhpMixed>> = value.as_array().cloned().unwrap_or_default(); + let mut merged: IndexMap<String, Box<PhpMixed>> = + value.as_array().cloned().unwrap_or_default(); if let Some(cv) = current_value.as_array() { for (k, v) in cv { if !merged.contains_key(k) { @@ -728,7 +952,11 @@ impl ConfigCommand { } } - self.inner.config_source.as_mut().unwrap().add_config_setting(&setting_key, value); + self.inner + .config_source + .as_mut() + .unwrap() + .add_config_setting(&setting_key, value); return Ok(0); } @@ -851,30 +1079,50 @@ impl ConfigCommand { let mut matches: Vec<String> = vec![]; if Preg::is_match("/^scripts\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { if input.get_option("unset").as_bool() == Some(true) { - self.inner.config_source.as_mut().unwrap().remove_property(&setting_key); + self.inner + .config_source + .as_mut() + .unwrap() + .remove_property(&setting_key); return Ok(0); } let value: PhpMixed = if count(&values) > 1 { - PhpMixed::List(values.iter().map(|s| Box::new(PhpMixed::String(s.clone()))).collect()) + PhpMixed::List( + values + .iter() + .map(|s| Box::new(PhpMixed::String(s.clone()))) + .collect(), + ) } else { PhpMixed::String(values[0].clone()) }; - self.inner.config_source.as_mut().unwrap().add_property(&setting_key, value); + self.inner + .config_source + .as_mut() + .unwrap() + .add_property(&setting_key, value); return Ok(0); } // handle unsetting other top level properties if input.get_option("unset").as_bool() == Some(true) { - self.inner.config_source.as_mut().unwrap().remove_property(&setting_key); + self.inner + .config_source + .as_mut() + .unwrap() + .remove_property(&setting_key); return Ok(0); } Err(InvalidArgumentException { - message: format!("Setting {} does not exist or is not supported by this command", setting_key), + message: format!( + "Setting {} does not exist or is not supported by this command", + setting_key + ), code: 0, } .into()) @@ -917,11 +1165,27 @@ impl ConfigCommand { if key == "disable-tls" { if !normalized_value.as_bool().unwrap_or(false) - && self.inner.config.as_ref().unwrap().get("disable-tls").as_bool().unwrap_or(false) + && self + .inner + .config + .as_ref() + .unwrap() + .get("disable-tls") + .as_bool() + .unwrap_or(false) { - self.inner.inner.get_io().write_error("<info>You are now running Composer with SSL/TLS protection enabled.</info>"); + self.inner.inner.get_io().write_error( + "<info>You are now running Composer with SSL/TLS protection enabled.</info>", + ); } else if normalized_value.as_bool().unwrap_or(false) - && !self.inner.config.as_ref().unwrap().get("disable-tls").as_bool().unwrap_or(false) + && !self + .inner + .config + .as_ref() + .unwrap() + .get("disable-tls") + .as_bool() + .unwrap_or(false) { self.inner.inner.get_io().write_error("<warning>You are now running Composer with SSL/TLS protection disabled.</warning>"); } @@ -943,7 +1207,12 @@ impl ConfigCommand { method: &str, ) -> anyhow::Result<()> { let (validator, normalizer) = callbacks; - let values_mixed = PhpMixed::List(values.iter().map(|s| Box::new(PhpMixed::String(s.clone()))).collect()); + let values_mixed = PhpMixed::List( + values + .iter() + .map(|s| Box::new(PhpMixed::String(s.clone()))) + .collect(), + ); let validation = validator(&values_mixed); if validation.as_bool() != Some(true) { let suffix = if !validation.is_null() && validation.as_bool() != Some(false) { @@ -984,15 +1253,20 @@ impl ConfigCommand { let raw_contents_arr = raw_contents.as_array().cloned().unwrap_or_default(); let mut k = k; for (key, value) in &contents_arr { - if k.is_none() && !in_array( - key.as_str(), - &vec!["config".to_string(), "repositories".to_string()], - true, - ) { + if k.is_none() + && !in_array( + key.as_str(), + &vec!["config".to_string(), "repositories".to_string()], + true, + ) + { continue; } - let raw_val = raw_contents_arr.get(key).map(|v| (**v).clone()).unwrap_or(PhpMixed::Null); + let raw_val = raw_contents_arr + .get(key) + .map(|v| (**v).clone()) + .unwrap_or(PhpMixed::Null); let value_inner = (**value).clone(); @@ -1034,7 +1308,11 @@ impl ConfigCommand { let source = if show_source { format!( " ({})", - self.inner.config.as_ref().unwrap().get_source_of_value(&format!("{}{}", k.clone().unwrap_or_default(), key)) + self.inner + .config + .as_ref() + .unwrap() + .get_source_of_value(&format!("{}{}", k.clone().unwrap_or_default(), key)) ) } else { String::new() @@ -1050,11 +1328,21 @@ impl ConfigCommand { k.clone().unwrap() }; let id = Preg::replace("{\\..*$}", "", &id_source); - let id = Preg::replace("{[^a-z0-9]}i", "-", &strtolower(&shirabe_php_shim::trim(&id, " \t\n\r\0\u{0B}"))); + let id = Preg::replace( + "{[^a-z0-9]}i", + "-", + &strtolower(&shirabe_php_shim::trim(&id, " \t\n\r\0\u{0B}")), + ); let id = Preg::replace("{-+}", "-", &id); link = format!("https://getcomposer.org/doc/06-config.md#{}", id); } - if is_string(&raw_val) && raw_val.as_string().map(|s| s.to_string()).unwrap_or_default() != value_display { + if is_string(&raw_val) + && raw_val + .as_string() + .map(|s| s.to_string()) + .unwrap_or_default() + != value_display + { io.write( &format!( "[<fg=yellow;href={}>{}{}</>] <info>{} ({})</info>{}", @@ -1103,16 +1391,24 @@ impl ConfigCommand { // load configuration // TODO: BaseConfigCommand::get_composer_config_file is an instance method; using a free helper here. - let config_file = JsonFile::new(get_composer_config_file_static(input, &config), None, None); + let config_file = + JsonFile::new(get_composer_config_file_static(input, &config), None, None); if config_file.exists() { - config.merge(config_file.read().unwrap_or(PhpMixed::Null), config_file.get_path()); + config.merge( + config_file.read().unwrap_or(PhpMixed::Null), + config_file.get_path(), + ); } // load auth-configuration - let auth_config_file = JsonFile::new(get_auth_config_file_static(input, &config), None, None); + let auth_config_file = + JsonFile::new(get_auth_config_file_static(input, &config), None, None); if auth_config_file.exists() { let mut wrap: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); - wrap.insert("config".to_string(), Box::new(auth_config_file.read().unwrap_or(PhpMixed::Null))); + wrap.insert( + "config".to_string(), + Box::new(auth_config_file.read().unwrap_or(PhpMixed::Null)), + ); config.merge(PhpMixed::Array(wrap), auth_config_file.get_path()); } @@ -1120,20 +1416,38 @@ impl ConfigCommand { let raw_config = config.raw(); let raw_arr = raw_config.as_array().cloned().unwrap_or_default(); let mut keys: Vec<String> = array_merge( - flatten_setting_keys(raw_arr.get("config").map(|v| (**v).clone()).unwrap_or(PhpMixed::Null), ""), - flatten_setting_keys(raw_arr.get("repositories").map(|v| (**v).clone()).unwrap_or(PhpMixed::Null), "repositories."), + flatten_setting_keys( + raw_arr + .get("config") + .map(|v| (**v).clone()) + .unwrap_or(PhpMixed::Null), + "", + ), + flatten_setting_keys( + raw_arr + .get("repositories") + .map(|v| (**v).clone()) + .unwrap_or(PhpMixed::Null), + "repositories.", + ), ); // if unsetting … if input.get_option("unset").as_bool() == Some(true) { // … keep only the currently customized setting-keys … - let sources = vec![config_file.get_path().to_string(), auth_config_file.get_path().to_string()]; + let sources = vec![ + config_file.get_path().to_string(), + auth_config_file.get_path().to_string(), + ]; keys = array_filter(keys, |k: &String| -> bool { in_array(config.get_source_of_value(k).as_str(), &sources, true) }); } else { // … add all configurable package-properties, no matter if it exist - let configurable: Vec<String> = ConfigCommand::CONFIGURABLE_PACKAGE_PROPERTIES.iter().map(|s| s.to_string()).collect(); + let configurable: Vec<String> = ConfigCommand::CONFIGURABLE_PACKAGE_PROPERTIES + .iter() + .map(|s| s.to_string()) + .collect(); keys = array_merge(keys, configurable); // it would be nice to distinguish between showing and setting @@ -1144,9 +1458,17 @@ impl ConfigCommand { // add all existing configurable package-properties if config_file.exists() { - let configurable: Vec<String> = ConfigCommand::CONFIGURABLE_PACKAGE_PROPERTIES.iter().map(|s| s.to_string()).collect(); + let configurable: Vec<String> = ConfigCommand::CONFIGURABLE_PACKAGE_PROPERTIES + .iter() + .map(|s| s.to_string()) + .collect(); let properties = array_filter_use_key( - config_file.read().unwrap_or(PhpMixed::Null).as_array().cloned().unwrap_or_default(), + config_file + .read() + .unwrap_or(PhpMixed::Null) + .as_array() + .cloned() + .unwrap_or_default(), |key: &String| -> bool { in_array(key.as_str(), &configurable, true) }, ); @@ -1176,7 +1498,12 @@ pub type NormalizerFn = Box<dyn Fn(&PhpMixed) -> PhpMixed>; fn boolean_validator(val: &PhpMixed) -> PhpMixed { PhpMixed::Bool(in_array( val.as_string().unwrap_or(""), - &vec!["true".to_string(), "false".to_string(), "1".to_string(), "0".to_string()], + &vec![ + "true".to_string(), + "false".to_string(), + "1".to_string(), + "0".to_string(), + ], true, )) } @@ -1198,26 +1525,54 @@ fn build_unique_config_values() -> IndexMap<String, (ValidatorFn, NormalizerFn)> Box::new(|val| PhpMixed::Int(shirabe_php_shim::intval(val.as_string().unwrap_or("0")))), ), ); - m.insert("use-include-path".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); - m.insert("use-github-api".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); + m.insert( + "use-include-path".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); + m.insert( + "use-github-api".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); m.insert( "preferred-install".to_string(), ( - Box::new(|val| PhpMixed::Bool(in_array(val.as_string().unwrap_or(""), &vec!["auto".to_string(), "source".to_string(), "dist".to_string()], true))), + Box::new(|val| { + PhpMixed::Bool(in_array( + val.as_string().unwrap_or(""), + &vec!["auto".to_string(), "source".to_string(), "dist".to_string()], + true, + )) + }), Box::new(|val| val.clone()), ), ); m.insert( "gitlab-protocol".to_string(), ( - Box::new(|val| PhpMixed::Bool(in_array(val.as_string().unwrap_or(""), &vec!["git".to_string(), "http".to_string(), "https".to_string()], true))), + Box::new(|val| { + PhpMixed::Bool(in_array( + val.as_string().unwrap_or(""), + &vec!["git".to_string(), "http".to_string(), "https".to_string()], + true, + )) + }), Box::new(|val| val.clone()), ), ); m.insert( "store-auths".to_string(), ( - Box::new(|val| PhpMixed::Bool(in_array(val.as_string().unwrap_or(""), &vec!["true".to_string(), "false".to_string(), "prompt".to_string()], true))), + Box::new(|val| { + PhpMixed::Bool(in_array( + val.as_string().unwrap_or(""), + &vec![ + "true".to_string(), + "false".to_string(), + "prompt".to_string(), + ], + true, + )) + }), Box::new(|val| { let s = val.as_string().unwrap_or(""); if s == "prompt" { @@ -1228,16 +1583,73 @@ fn build_unique_config_values() -> IndexMap<String, (ValidatorFn, NormalizerFn)> }), ), ); - m.insert("notify-on-install".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); - m.insert("vendor-dir".to_string(), (Box::new(|val| PhpMixed::Bool(is_string(val))), Box::new(|val| val.clone()))); - m.insert("bin-dir".to_string(), (Box::new(|val| PhpMixed::Bool(is_string(val))), Box::new(|val| val.clone()))); - m.insert("archive-dir".to_string(), (Box::new(|val| PhpMixed::Bool(is_string(val))), Box::new(|val| val.clone()))); - m.insert("archive-format".to_string(), (Box::new(|val| PhpMixed::Bool(is_string(val))), Box::new(|val| val.clone()))); - m.insert("data-dir".to_string(), (Box::new(|val| PhpMixed::Bool(is_string(val))), Box::new(|val| val.clone()))); - m.insert("cache-dir".to_string(), (Box::new(|val| PhpMixed::Bool(is_string(val))), Box::new(|val| val.clone()))); - m.insert("cache-files-dir".to_string(), (Box::new(|val| PhpMixed::Bool(is_string(val))), Box::new(|val| val.clone()))); - m.insert("cache-repo-dir".to_string(), (Box::new(|val| PhpMixed::Bool(is_string(val))), Box::new(|val| val.clone()))); - m.insert("cache-vcs-dir".to_string(), (Box::new(|val| PhpMixed::Bool(is_string(val))), Box::new(|val| val.clone()))); + m.insert( + "notify-on-install".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); + m.insert( + "vendor-dir".to_string(), + ( + Box::new(|val| PhpMixed::Bool(is_string(val))), + Box::new(|val| val.clone()), + ), + ); + m.insert( + "bin-dir".to_string(), + ( + Box::new(|val| PhpMixed::Bool(is_string(val))), + Box::new(|val| val.clone()), + ), + ); + m.insert( + "archive-dir".to_string(), + ( + Box::new(|val| PhpMixed::Bool(is_string(val))), + Box::new(|val| val.clone()), + ), + ); + m.insert( + "archive-format".to_string(), + ( + Box::new(|val| PhpMixed::Bool(is_string(val))), + Box::new(|val| val.clone()), + ), + ); + m.insert( + "data-dir".to_string(), + ( + Box::new(|val| PhpMixed::Bool(is_string(val))), + Box::new(|val| val.clone()), + ), + ); + m.insert( + "cache-dir".to_string(), + ( + Box::new(|val| PhpMixed::Bool(is_string(val))), + Box::new(|val| val.clone()), + ), + ); + m.insert( + "cache-files-dir".to_string(), + ( + Box::new(|val| PhpMixed::Bool(is_string(val))), + Box::new(|val| val.clone()), + ), + ); + m.insert( + "cache-repo-dir".to_string(), + ( + Box::new(|val| PhpMixed::Bool(is_string(val))), + Box::new(|val| val.clone()), + ), + ); + m.insert( + "cache-vcs-dir".to_string(), + ( + Box::new(|val| PhpMixed::Bool(is_string(val))), + Box::new(|val| val.clone()), + ), + ); m.insert( "cache-ttl".to_string(), ( @@ -1255,21 +1667,53 @@ fn build_unique_config_values() -> IndexMap<String, (ValidatorFn, NormalizerFn)> m.insert( "cache-files-maxsize".to_string(), ( - Box::new(|val| PhpMixed::Bool(Preg::is_match("/^\\s*([0-9.]+)\\s*(?:([kmg])(?:i?b)?)?\\s*$/i", val.as_string().unwrap_or(""), None).unwrap_or(false))), + Box::new(|val| { + PhpMixed::Bool( + Preg::is_match( + "/^\\s*([0-9.]+)\\s*(?:([kmg])(?:i?b)?)?\\s*$/i", + val.as_string().unwrap_or(""), + None, + ) + .unwrap_or(false), + ) + }), Box::new(|val| val.clone()), ), ); m.insert( "bin-compat".to_string(), ( - Box::new(|val| PhpMixed::Bool(in_array(val.as_string().unwrap_or(""), &vec!["auto".to_string(), "full".to_string(), "proxy".to_string(), "symlink".to_string()], false))), + Box::new(|val| { + PhpMixed::Bool(in_array( + val.as_string().unwrap_or(""), + &vec![ + "auto".to_string(), + "full".to_string(), + "proxy".to_string(), + "symlink".to_string(), + ], + false, + )) + }), Box::new(|val| val.clone()), ), ); m.insert( "discard-changes".to_string(), ( - Box::new(|val| PhpMixed::Bool(in_array(val.as_string().unwrap_or(""), &vec!["stash".to_string(), "true".to_string(), "false".to_string(), "1".to_string(), "0".to_string()], true))), + Box::new(|val| { + PhpMixed::Bool(in_array( + val.as_string().unwrap_or(""), + &vec![ + "stash".to_string(), + "true".to_string(), + "false".to_string(), + "1".to_string(), + "0".to_string(), + ], + true, + )) + }), Box::new(|val| { let s = val.as_string().unwrap_or(""); if s == "stash" { @@ -1293,18 +1737,55 @@ fn build_unique_config_values() -> IndexMap<String, (ValidatorFn, NormalizerFn)> }), ), ); - m.insert("sort-packages".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); - m.insert("optimize-autoloader".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); - m.insert("classmap-authoritative".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); - m.insert("apcu-autoloader".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); - m.insert("prepend-autoloader".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); - m.insert("update-with-minimal-changes".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); - m.insert("disable-tls".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); - m.insert("secure-http".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); + m.insert( + "sort-packages".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); + m.insert( + "optimize-autoloader".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); + m.insert( + "classmap-authoritative".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); + m.insert( + "apcu-autoloader".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); + m.insert( + "prepend-autoloader".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); + m.insert( + "update-with-minimal-changes".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); + m.insert( + "disable-tls".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); + m.insert( + "secure-http".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); m.insert( "bump-after-update".to_string(), ( - Box::new(|val| PhpMixed::Bool(in_array(val.as_string().unwrap_or(""), &vec!["dev".to_string(), "no-dev".to_string(), "true".to_string(), "false".to_string(), "1".to_string(), "0".to_string()], true))), + Box::new(|val| { + PhpMixed::Bool(in_array( + val.as_string().unwrap_or(""), + &vec![ + "dev".to_string(), + "no-dev".to_string(), + "true".to_string(), + "false".to_string(), + "1".to_string(), + "0".to_string(), + ], + true, + )) + }), Box::new(|val| { let s = val.as_string().unwrap_or(""); if s == "dev" || s == "no-dev" { @@ -1318,25 +1799,71 @@ fn build_unique_config_values() -> IndexMap<String, (ValidatorFn, NormalizerFn)> m.insert( "cafile".to_string(), ( - Box::new(|val| PhpMixed::Bool(file_exists(val.as_string().unwrap_or("")) && Filesystem::is_readable(val.as_string().unwrap_or("")))), - Box::new(|val| if val.as_string() == Some("null") { PhpMixed::Null } else { val.clone() }), + Box::new(|val| { + PhpMixed::Bool( + file_exists(val.as_string().unwrap_or("")) + && Filesystem::is_readable(val.as_string().unwrap_or("")), + ) + }), + Box::new(|val| { + if val.as_string() == Some("null") { + PhpMixed::Null + } else { + val.clone() + } + }), ), ); m.insert( "capath".to_string(), ( - Box::new(|val| PhpMixed::Bool(is_dir(val.as_string().unwrap_or("")) && Filesystem::is_readable(val.as_string().unwrap_or("")))), - Box::new(|val| if val.as_string() == Some("null") { PhpMixed::Null } else { val.clone() }), + Box::new(|val| { + PhpMixed::Bool( + is_dir(val.as_string().unwrap_or("")) + && Filesystem::is_readable(val.as_string().unwrap_or("")), + ) + }), + Box::new(|val| { + if val.as_string() == Some("null") { + PhpMixed::Null + } else { + val.clone() + } + }), ), ); - m.insert("github-expose-hostname".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); - m.insert("htaccess-protect".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); - m.insert("lock".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); - m.insert("allow-plugins".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); + m.insert( + "github-expose-hostname".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); + m.insert( + "htaccess-protect".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); + m.insert( + "lock".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); + m.insert( + "allow-plugins".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); m.insert( "platform-check".to_string(), ( - Box::new(|val| PhpMixed::Bool(in_array(val.as_string().unwrap_or(""), &vec!["php-only".to_string(), "true".to_string(), "false".to_string(), "1".to_string(), "0".to_string()], true))), + Box::new(|val| { + PhpMixed::Bool(in_array( + val.as_string().unwrap_or(""), + &vec![ + "php-only".to_string(), + "true".to_string(), + "false".to_string(), + "1".to_string(), + "0".to_string(), + ], + true, + )) + }), Box::new(|val| { let s = val.as_string().unwrap_or(""); if s == "php-only" { @@ -1350,7 +1877,17 @@ fn build_unique_config_values() -> IndexMap<String, (ValidatorFn, NormalizerFn)> m.insert( "use-parent-dir".to_string(), ( - Box::new(|val| PhpMixed::Bool(in_array(val.as_string().unwrap_or(""), &vec!["true".to_string(), "false".to_string(), "prompt".to_string()], true))), + Box::new(|val| { + PhpMixed::Bool(in_array( + val.as_string().unwrap_or(""), + &vec![ + "true".to_string(), + "false".to_string(), + "prompt".to_string(), + ], + true, + )) + }), Box::new(|val| { let s = val.as_string().unwrap_or(""); if s == "prompt" { @@ -1364,13 +1901,32 @@ fn build_unique_config_values() -> IndexMap<String, (ValidatorFn, NormalizerFn)> m.insert( "audit.abandoned".to_string(), ( - Box::new(|val| PhpMixed::Bool(in_array(val.as_string().unwrap_or(""), &vec![Auditor::ABANDONED_IGNORE.to_string(), Auditor::ABANDONED_REPORT.to_string(), Auditor::ABANDONED_FAIL.to_string()], true))), + Box::new(|val| { + PhpMixed::Bool(in_array( + val.as_string().unwrap_or(""), + &vec![ + Auditor::ABANDONED_IGNORE.to_string(), + Auditor::ABANDONED_REPORT.to_string(), + Auditor::ABANDONED_FAIL.to_string(), + ], + true, + )) + }), Box::new(|val| val.clone()), ), ); - m.insert("audit.ignore-unreachable".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); - m.insert("audit.block-insecure".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); - m.insert("audit.block-abandoned".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); + m.insert( + "audit.ignore-unreachable".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); + m.insert( + "audit.block-insecure".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); + m.insert( + "audit.block-abandoned".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); let _ = identity; m @@ -1387,8 +1943,14 @@ fn build_multi_config_values() -> IndexMap<String, (ValidatorFn, NormalizerFn)> } if let Some(list) = vals.as_list() { for val in list { - if !in_array(val.as_string().unwrap_or(""), &vec!["git".to_string(), "https".to_string(), "ssh".to_string()], false) { - return PhpMixed::String("valid protocols include: git, https, ssh".to_string()); + if !in_array( + val.as_string().unwrap_or(""), + &vec!["git".to_string(), "https".to_string(), "ssh".to_string()], + false, + ) { + return PhpMixed::String( + "valid protocols include: git, https, ssh".to_string(), + ); } } } @@ -1430,8 +1992,19 @@ fn build_multi_config_values() -> IndexMap<String, (ValidatorFn, NormalizerFn)> } if let Some(list) = vals.as_list() { for val in list { - if !in_array(val.as_string().unwrap_or(""), &vec!["low".to_string(), "medium".to_string(), "high".to_string(), "critical".to_string()], true) { - return PhpMixed::String("valid severities include: low, medium, high, critical".to_string()); + if !in_array( + val.as_string().unwrap_or(""), + &vec![ + "low".to_string(), + "medium".to_string(), + "high".to_string(), + "critical".to_string(), + ], + true, + ) { + return PhpMixed::String( + "valid severities include: low, medium, high, critical".to_string(), + ); } } } @@ -1445,11 +2018,41 @@ fn build_multi_config_values() -> IndexMap<String, (ValidatorFn, NormalizerFn)> fn build_unique_props() -> IndexMap<String, (ValidatorFn, NormalizerFn)> { let mut m: IndexMap<String, (ValidatorFn, NormalizerFn)> = IndexMap::new(); - m.insert("name".to_string(), (Box::new(|val| PhpMixed::Bool(is_string(val))), Box::new(|val| val.clone()))); - m.insert("type".to_string(), (Box::new(|val| PhpMixed::Bool(is_string(val))), Box::new(|val| val.clone()))); - m.insert("description".to_string(), (Box::new(|val| PhpMixed::Bool(is_string(val))), Box::new(|val| val.clone()))); - m.insert("homepage".to_string(), (Box::new(|val| PhpMixed::Bool(is_string(val))), Box::new(|val| val.clone()))); - m.insert("version".to_string(), (Box::new(|val| PhpMixed::Bool(is_string(val))), Box::new(|val| val.clone()))); + m.insert( + "name".to_string(), + ( + Box::new(|val| PhpMixed::Bool(is_string(val))), + Box::new(|val| val.clone()), + ), + ); + m.insert( + "type".to_string(), + ( + Box::new(|val| PhpMixed::Bool(is_string(val))), + Box::new(|val| val.clone()), + ), + ); + m.insert( + "description".to_string(), + ( + Box::new(|val| PhpMixed::Bool(is_string(val))), + Box::new(|val| val.clone()), + ), + ); + m.insert( + "homepage".to_string(), + ( + Box::new(|val| PhpMixed::Bool(is_string(val))), + Box::new(|val| val.clone()), + ), + ); + m.insert( + "version".to_string(), + ( + Box::new(|val| PhpMixed::Bool(is_string(val))), + Box::new(|val| val.clone()), + ), + ); m.insert( "minimum-stability".to_string(), ( @@ -1457,10 +2060,17 @@ fn build_unique_props() -> IndexMap<String, (ValidatorFn, NormalizerFn)> { let normalized = VersionParser::normalize_stability(val.as_string().unwrap_or("")); PhpMixed::Bool(base_package::STABILITIES.contains_key(normalized.as_str())) }), - Box::new(|val| PhpMixed::String(VersionParser::normalize_stability(val.as_string().unwrap_or("")))), + Box::new(|val| { + PhpMixed::String(VersionParser::normalize_stability( + val.as_string().unwrap_or(""), + )) + }), ), ); - m.insert("prefer-stable".to_string(), (Box::new(boolean_validator), Box::new(boolean_normalizer))); + m.insert( + "prefer-stable".to_string(), + (Box::new(boolean_validator), Box::new(boolean_normalizer)), + ); m } @@ -1505,7 +2115,10 @@ fn flatten_setting_keys(config: PhpMixed, prefix: &str) -> Vec<String> { // array-lists must not be added to completion // sub-keys of repository-keys must not be added to completion if is_array(value) && !array_is_list(value) && prefix != "repositories." { - keys.push(flatten_setting_keys((**value).clone(), &format!("{}{}.", prefix, key))); + keys.push(flatten_setting_keys( + (**value).clone(), + &format!("{}{}.", prefix, key), + )); } } @@ -1519,7 +2132,10 @@ fn flatten_setting_keys(config: PhpMixed, prefix: &str) -> Vec<String> { // Helpers for the suggester since BaseConfigCommand methods need an instance. fn get_composer_config_file_static(input: &CompletionInput, config: &Config) -> String { if input.get_option("global").as_bool() == Some(true) { - format!("{}/config.json", config.get("home").as_string().unwrap_or("")) + format!( + "{}/config.json", + config.get("home").as_string().unwrap_or("") + ) } else { input .get_option("file") diff --git a/crates/shirabe/src/command/create_project_command.rs b/crates/shirabe/src/command/create_project_command.rs index 6614b11..c649848 100644 --- a/crates/shirabe/src/command/create_project_command.rs +++ b/crates/shirabe/src/command/create_project_command.rs @@ -7,17 +7,17 @@ use shirabe_external_packages::symfony::component::console::input::input_interfa use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface; use shirabe_external_packages::symfony::component::finder::finder::Finder; use shirabe_php_shim::{ - array_pop, chdir, explode_with_limit, file_exists, getcwd, implode, is_dir, is_file, - mkdir, realpath, rtrim, strtolower, unlink, InvalidArgumentException, PhpMixed, - RuntimeException, UnexpectedValueException, DIRECTORY_SEPARATOR, + DIRECTORY_SEPARATOR, InvalidArgumentException, PhpMixed, RuntimeException, + UnexpectedValueException, array_pop, chdir, explode_with_limit, file_exists, getcwd, implode, + is_dir, is_file, mkdir, realpath, rtrim, strtolower, unlink, }; use crate::advisory::auditor::Auditor; use crate::command::base_command::BaseCommand; use crate::command::completion_trait::CompletionTrait; use crate::composer::Composer; -use crate::config::json_config_source::JsonConfigSource; use crate::config::Config; +use crate::config::json_config_source::JsonConfigSource; use crate::console::input::input_argument::InputArgument; use crate::console::input::input_option::InputOption; use crate::dependency_resolver::operation::install_operation::InstallOperation; @@ -25,9 +25,9 @@ use crate::factory::Factory; use crate::filter::platform_requirement_filter::ignore_all_platform_requirement_filter::IgnoreAllPlatformRequirementFilter; use crate::filter::platform_requirement_filter::platform_requirement_filter_factory::PlatformRequirementFilterFactory; use crate::filter::platform_requirement_filter::platform_requirement_filter_interface::PlatformRequirementFilterInterface; +use crate::installer::Installer; use crate::installer::project_installer::ProjectInstaller; use crate::installer::suggested_packages_reporter::SuggestedPackagesReporter; -use crate::installer::Installer; use crate::io::io_interface::IOInterface; use crate::json::json_file::JsonFile; use crate::package::alias_package::AliasPackage; @@ -116,13 +116,18 @@ impl CreateProjectCommand { let config = Factory::create_config(None, None)?; let io = self.inner.get_io(); - let (prefer_source, prefer_dist) = - self.inner.get_preferred_install_options(&config, input, true)?; + let (prefer_source, prefer_dist) = self + .inner + .get_preferred_install_options(&config, input, true)?; if input.get_option("dev").as_bool().unwrap_or(false) { io.write_error("<warning>You are using the deprecated option \"dev\". Dev packages are installed by default now.</warning>"); } - if input.get_option("no-custom-installers").as_bool().unwrap_or(false) { + if input + .get_option("no-custom-installers") + .as_bool() + .unwrap_or(false) + { io.write_error("<warning>You are using the deprecated option \"no-custom-installers\". Use \"no-plugins\" instead.</warning>"); input.set_option("no-plugins", PhpMixed::Bool(true)); } @@ -136,11 +141,8 @@ impl CreateProjectCommand { } .into()); } - let mut parts = explode_with_limit( - "/", - &strtolower(package.as_string().unwrap_or("")), - 2, - ); + let mut parts = + explode_with_limit("/", &strtolower(package.as_string().unwrap_or("")), 2); let prompt = format!( "New project directory [<comment>{}</comment>]: ", array_pop(&mut parts).unwrap_or_default() @@ -150,7 +152,11 @@ impl CreateProjectCommand { let repository_opt = input.get_option("repository"); let repository_url_opt = input.get_option("repository-url"); - let repositories = if repository_opt.as_list().map(|l| l.len() > 0).unwrap_or(false) { + let repositories = if repository_opt + .as_list() + .map(|l| l.len() > 0) + .unwrap_or(false) + { Some(repository_opt) } else { Some(repository_url_opt) @@ -160,10 +166,22 @@ impl CreateProjectCommand { io, config, input, - input.get_argument("package").as_string().map(|s| s.to_string()), - input.get_argument("directory").as_string().map(|s| s.to_string()), - input.get_argument("version").as_string().map(|s| s.to_string()), - input.get_option("stability").as_string().map(|s| s.to_string()), + input + .get_argument("package") + .as_string() + .map(|s| s.to_string()), + input + .get_argument("directory") + .as_string() + .map(|s| s.to_string()), + input + .get_argument("version") + .as_string() + .map(|s| s.to_string()), + input + .get_option("stability") + .as_string() + .map(|s| s.to_string()), prefer_source, prefer_dist, !input.get_option("no-dev").as_bool().unwrap_or(false), @@ -173,8 +191,14 @@ impl CreateProjectCommand { input.get_option("no-progress").as_bool().unwrap_or(false), input.get_option("no-install").as_bool().unwrap_or(false), Some(self.inner.get_platform_requirement_filter(input)?), - !input.get_option("no-secure-http").as_bool().unwrap_or(false), - input.get_option("add-repository").as_bool().unwrap_or(false), + !input + .get_option("no-secure-http") + .as_bool() + .unwrap_or(false), + input + .get_option("add-repository") + .as_bool() + .unwrap_or(false), ) } @@ -255,13 +279,8 @@ impl CreateProjectCommand { unlink("composer.lock"); } - let mut composer = self.create_composer_instance( - input, - io, - None, - disable_plugins, - Some(disable_scripts), - )?; + let mut composer = + self.create_composer_instance(input, io, None, disable_plugins, Some(disable_scripts))?; // add the repository to the composer.json and use it for the install run later if let Some(repos) = repositories.as_ref() { @@ -288,20 +307,13 @@ impl CreateProjectCommand { let is_packagist_disabled = (repo_config.contains_key("packagist") && repo_config.len() == 1 - && repo_config.get("packagist").and_then(|v| v.as_bool()) - == Some(false)) + && repo_config.get("packagist").and_then(|v| v.as_bool()) == Some(false)) || (repo_config.contains_key("packagist.org") && repo_config.len() == 1 - && repo_config - .get("packagist.org") - .and_then(|v| v.as_bool()) + && repo_config.get("packagist.org").and_then(|v| v.as_bool()) == Some(false)); if is_packagist_disabled { - config_source.add_repository( - "packagist.org", - PhpMixed::Bool(false), - false, - ); + config_source.add_repository("packagist.org", PhpMixed::Bool(false), false); } else { config_source.add_repository( &name, @@ -315,8 +327,8 @@ impl CreateProjectCommand { ); } - composer = self - .create_composer_instance(input, io, None, disable_plugins, None)?; + composer = + self.create_composer_instance(input, io, None, disable_plugins, None)?; } } } @@ -332,8 +344,9 @@ impl CreateProjectCommand { // use the new config including the newly installed project let config = composer.get_config(); - let (ps, pd) = - self.inner.get_preferred_install_options(config, input, false)?; + let (ps, pd) = self + .inner + .get_preferred_install_options(config, input, false)?; prefer_source = ps; prefer_dist = pd; @@ -356,7 +369,10 @@ impl CreateProjectCommand { config.get("optimize-autoloader").as_bool().unwrap_or(false), ) .set_class_map_authoritative( - config.get("classmap-authoritative").as_bool().unwrap_or(false), + config + .get("classmap-authoritative") + .as_bool() + .unwrap_or(false), ) .set_apcu_autoloader( config.get("apcu-autoloader").as_bool().unwrap_or(false), @@ -456,11 +472,8 @@ impl CreateProjectCommand { // rewriting self.version dependencies with explicit version numbers if the package's vcs metadata is gone if !has_vcs { let package = composer.get_package(); - let config_source = JsonConfigSource::new(JsonFile::new( - "composer.json".to_string(), - None, - None, - )); + let config_source = + JsonConfigSource::new(JsonFile::new("composer.json".to_string(), None, None)); for (r#type, meta) in SUPPORTED_LINK_TYPES.iter() { // PHP: $package->{'get'.$meta['method']}() — dynamic getter dispatch // TODO(phase-b): dynamic getter dispatch by name @@ -479,10 +492,9 @@ impl CreateProjectCommand { } // dispatch event - composer.get_event_dispatcher().dispatch_script( - ScriptEvents::POST_CREATE_PROJECT_CMD, - install_dev_packages, - ); + composer + .get_event_dispatcher() + .dispatch_script(ScriptEvents::POST_CREATE_PROJECT_CMD, install_dev_packages); chdir(&old_cwd); @@ -515,8 +527,12 @@ impl CreateProjectCommand { // TODO(phase-b): VersionParser has no public `new` yet let parser: VersionParser = todo!("VersionParser::new()"); let requirements = parser.parse_name_version_pairs(vec![package_name.to_string()])?; - let name = - strtolower(requirements[0].get("name").map(|s| s.as_str()).unwrap_or("")); + let name = strtolower( + requirements[0] + .get("name") + .map(|s| s.as_str()) + .unwrap_or(""), + ); if package_version.is_none() && requirements[0].contains_key("version") { package_version = requirements[0].get("version").cloned(); } @@ -606,7 +622,10 @@ impl CreateProjectCommand { "{{^[^,\\s]*?@({})$}}i", implode( "|", - &STABILITIES.keys().map(|k| k.to_string()).collect::<Vec<_>>() + &STABILITIES + .keys() + .map(|k| k.to_string()) + .collect::<Vec<_>>() ) ), package_version.as_deref().unwrap_or(""), @@ -619,8 +638,7 @@ impl CreateProjectCommand { } } - let stability = - VersionParser::normalize_stability(stability.as_deref().unwrap_or("")); + let stability = VersionParser::normalize_stability(stability.as_deref().unwrap_or("")); if !STABILITIES.contains_key(stability.as_str()) { return Err(InvalidArgumentException { @@ -629,7 +647,10 @@ impl CreateProjectCommand { stability, implode( ", ", - &STABILITIES.keys().map(|k| k.to_string()).collect::<Vec<_>>() + &STABILITIES + .keys() + .map(|k| k.to_string()) + .collect::<Vec<_>>() ) ), code: 0, @@ -660,13 +681,10 @@ impl CreateProjectCommand { RepositoryFactory::config_from_string(io, config, repo, true)?; let is_packagist_disabled = (repo_config.contains_key("packagist") && repo_config.len() == 1 - && repo_config.get("packagist").and_then(|v| v.as_bool()) - == Some(false)) + && repo_config.get("packagist").and_then(|v| v.as_bool()) == Some(false)) || (repo_config.contains_key("packagist.org") && repo_config.len() == 1 - && repo_config - .get("packagist.org") - .and_then(|v| v.as_bool()) + && repo_config.get("packagist.org").and_then(|v| v.as_bool()) == Some(false)); if is_packagist_disabled { continue; @@ -687,10 +705,7 @@ impl CreateProjectCommand { .entry("options".to_string()) .or_insert(PhpMixed::Array(indexmap::IndexMap::new())); if let PhpMixed::Array(options_map) = options_entry { - options_map.insert( - "symlink".to_string(), - Box::new(PhpMixed::Bool(false)), - ); + options_map.insert("symlink".to_string(), Box::new(PhpMixed::Bool(false))); } } @@ -774,7 +789,11 @@ impl CreateProjectCommand { if let Some(real_dir) = realpath(&directory) { let real_dir_clone = real_dir.clone(); signal_handler = Some(SignalHandler::create( - vec![SignalHandler::SIGINT, SignalHandler::SIGTERM, SignalHandler::SIGHUP], + vec![ + SignalHandler::SIGINT, + SignalHandler::SIGTERM, + SignalHandler::SIGHUP, + ], Box::new(move |signal: String, handler: &SignalHandler| { // TODO(phase-b): self.get_io().write_error(...) inside the closure let _ = &signal; diff --git a/crates/shirabe/src/command/depends_command.rs b/crates/shirabe/src/command/depends_command.rs index 36f7e22..9ec3dde 100644 --- a/crates/shirabe/src/command/depends_command.rs +++ b/crates/shirabe/src/command/depends_command.rs @@ -1,11 +1,11 @@ //! ref: composer/src/Composer/Command/DependsCommand.php -use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; -use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; use crate::command::base_dependency_command::BaseDependencyCommand; use crate::command::completion_trait::CompletionTrait; use crate::console::input::input_argument::InputArgument; use crate::console::input::input_option::InputOption; +use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; +use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; pub struct DependsCommand { inner: BaseDependencyCommand, @@ -50,7 +50,7 @@ impl DependsCommand { .set_help( "Displays detailed information about where a package is referenced.\n\n\ <info>php composer.phar depends composer/composer</info>\n\n\ - Read more at https://getcomposer.org/doc/03-cli.md#depends-why" + Read more at https://getcomposer.org/doc/03-cli.md#depends-why", ); } diff --git a/crates/shirabe/src/command/diagnose_command.rs b/crates/shirabe/src/command/diagnose_command.rs index 664b242..07e367f 100644 --- a/crates/shirabe/src/command/diagnose_command.rs +++ b/crates/shirabe/src/command/diagnose_command.rs @@ -8,15 +8,14 @@ use shirabe_external_packages::symfony::component::console::input::input_interfa use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface; use shirabe_external_packages::symfony::component::process::executable_finder::ExecutableFinder; use shirabe_php_shim::{ - count, curl_version, defined, disk_free_space, extension_loaded, file_exists, filter_var, - function_exists, get_class, hash, implode, ini_get, ioncube_loader_iversion, - ioncube_loader_version, is_array, is_string, key, max_i64, ob_get_clean, ob_start, phpinfo, - reset, rtrim, sprintf, str_contains, str_replace, str_starts_with, strpos, strstr, strtolower, - trim, version_compare, FILTER_VALIDATE_BOOLEAN, INFO_GENERAL, - InvalidArgumentException, PhpMixed, RuntimeException, - OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT, PHP_BINARY, PHP_EOL, PHP_VERSION, - PHP_VERSION_ID, PHP_WINDOWS_VERSION_BUILD, CURL_HTTP_VERSION_2_0, CURL_VERSION_HTTP2, CURL_VERSION_HTTP3, CURL_VERSION_ZSTD, + FILTER_VALIDATE_BOOLEAN, INFO_GENERAL, InvalidArgumentException, OPENSSL_VERSION_NUMBER, + OPENSSL_VERSION_TEXT, PHP_BINARY, PHP_EOL, PHP_VERSION, PHP_VERSION_ID, + PHP_WINDOWS_VERSION_BUILD, PhpMixed, RuntimeException, count, curl_version, defined, + disk_free_space, extension_loaded, file_exists, filter_var, function_exists, get_class, hash, + implode, ini_get, ioncube_loader_iversion, ioncube_loader_version, is_array, is_string, key, + max_i64, ob_get_clean, ob_start, phpinfo, reset, rtrim, sprintf, str_contains, str_replace, + str_starts_with, strpos, strstr, strtolower, trim, version_compare, }; use crate::advisory::auditor::Auditor; @@ -70,7 +69,11 @@ impl DiagnoseCommand { ); } - pub(crate) fn execute(&mut self, input: &dyn InputInterface, output: &dyn OutputInterface) -> anyhow::Result<i64> { + pub(crate) fn execute( + &mut self, + input: &dyn InputInterface, + output: &dyn OutputInterface, + ) -> anyhow::Result<i64> { let composer = self.inner.try_composer(); let io = self.inner.get_io(); @@ -78,8 +81,16 @@ impl DiagnoseCommand { if let Some(ref c) = composer { config = c.get_config().clone(); - let command_event = CommandEvent::new(PluginEvents::COMMAND, "diagnose", input, output, vec![], IndexMap::new()); - c.get_event_dispatcher().dispatch(command_event.get_name(), &command_event); + let command_event = CommandEvent::new( + PluginEvents::COMMAND, + "diagnose", + input, + output, + vec![], + IndexMap::new(), + ); + c.get_event_dispatcher() + .dispatch(command_event.get_name(), &command_event); self.process = Some( c.get_loop() .get_process_executor() @@ -94,7 +105,10 @@ impl DiagnoseCommand { let mut secure_http_wrap: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); let mut config_inner: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); config_inner.insert("secure-http".to_string(), Box::new(PhpMixed::Bool(false))); - secure_http_wrap.insert("config".to_string(), Box::new(PhpMixed::Array(config_inner))); + secure_http_wrap.insert( + "config".to_string(), + Box::new(PhpMixed::Array(config_inner)), + ); let mut config = config; config.merge(PhpMixed::Array(secure_http_wrap), Config::SOURCE_COMMAND); config.prohibit_url_by_config("http://repo.packagist.org", &NullIO::new()); @@ -111,26 +125,40 @@ impl DiagnoseCommand { self.output_result(r); } - io.write(&format!("Composer version: <comment>{}</comment>", Composer::get_version())); + io.write(&format!( + "Composer version: <comment>{}</comment>", + Composer::get_version() + )); io.write_no_newline("Checking Composer and its dependencies for vulnerabilities: "); let r = self.check_composer_audit(&config)?; self.output_result(r); - let platform_overrides = config.get("platform").as_array().cloned().unwrap_or_default(); + let platform_overrides = config + .get("platform") + .as_array() + .cloned() + .unwrap_or_default(); let platform_repo = PlatformRepository::new(vec![], platform_overrides); let php_pkg = platform_repo.find_package("php", "*").unwrap(); let mut php_version = php_pkg.get_pretty_version().to_string(); if let Some(cp) = php_pkg.as_complete_package_interface() { if str_contains(&cp.get_description().unwrap_or_default(), "overridden") { - php_version = format!("{} - {}", php_version, cp.get_description().unwrap_or_default()); + php_version = format!( + "{} - {}", + php_version, + cp.get_description().unwrap_or_default() + ); } } io.write(&format!("PHP version: <comment>{}</comment>", php_version)); if defined("PHP_BINARY") { - io.write(&format!("PHP binary path: <comment>{}</comment>", PHP_BINARY)); + io.write(&format!( + "PHP binary path: <comment>{}</comment>", + PHP_BINARY + )); } io.write(&format!( @@ -242,7 +270,10 @@ impl DiagnoseCommand { } io.write_no_newline(&format!( "Checking connectivity to {}: ", - repo_arr.get("url").and_then(|v| v.as_string()).unwrap_or("") + repo_arr + .get("url") + .and_then(|v| v.as_string()) + .unwrap_or("") )); let r = self.check_composer_repo(&url, &config)?; self.output_result(r); @@ -257,7 +288,8 @@ impl DiagnoseCommand { }; let proxy_check_result: Result<(), anyhow::Error> = (|| -> anyhow::Result<()> { for proto in &protos { - let proxy = proxy_manager.get_proxy_for_request(&format!("{}://repo.packagist.org", proto)); + let proxy = + proxy_manager.get_proxy_for_request(&format!("{}://repo.packagist.org", proto)); if !proxy.get_status().is_empty() { let r#type = if proxy.is_secure() { "HTTPS" } else { "HTTP" }; io.write_no_newline(&format!("Checking {} proxy with {}: ", r#type, proto)); @@ -274,14 +306,22 @@ impl DiagnoseCommand { self.output_result(if is_string(&status) { status } else { - PhpMixed::String(format!("<error>[{}] {}</error>", get_class(&e), e.to_string())) + PhpMixed::String(format!( + "<error>[{}] {}</error>", + get_class(&e), + e.to_string() + )) }); } else { return Err(e); } } - let oauth = config.get("github-oauth").as_array().cloned().unwrap_or_default(); + let oauth = config + .get("github-oauth") + .as_array() + .cloned() + .unwrap_or_default(); if count(&oauth) > 0 { for (domain, token) in &oauth { io.write_no_newline(&format!("Checking {} oauth access: ", domain)); @@ -313,10 +353,18 @@ impl DiagnoseCommand { if te.get_code() == 401 { self.output_result(PhpMixed::String("<comment>The oauth token for github.com seems invalid, run \"composer config --global --unset github-oauth.github.com\" to remove it</comment>".to_string())); } else { - self.output_result(PhpMixed::String(format!("<error>[{}] {}</error>", get_class(&e), e.to_string()))); + self.output_result(PhpMixed::String(format!( + "<error>[{}] {}</error>", + get_class(&e), + e.to_string() + ))); } } else { - self.output_result(PhpMixed::String(format!("<error>[{}] {}</error>", get_class(&e), e.to_string()))); + self.output_result(PhpMixed::String(format!( + "<error>[{}] {}</error>", + get_class(&e), + e.to_string() + ))); } } } @@ -379,7 +427,11 @@ impl DiagnoseCommand { let mut output = String::new(); self.process.as_mut().unwrap().execute( - &vec!["git".to_string(), "config".to_string(), "color.ui".to_string()], + &vec![ + "git".to_string(), + "config".to_string(), + "color.ui".to_string(), + ], &mut output, ); if strtolower(&trim(&output, " \t\n\r\0\u{0B}")) == "always" { @@ -393,7 +445,10 @@ impl DiagnoseCommand { }; if version_compare("2.24.0", &git_version, ">") { - return format!("<warning>Your git version ({}) is too old and possibly will cause issues. Please upgrade to git 2.24 or above</>", git_version); + return format!( + "<warning>Your git version ({}) is too old and possibly will cause issues. Please upgrade to git 2.24 or above</>", + git_version + ); } format!("<info>OK</> <comment>git version {}</>", git_version) @@ -411,7 +466,10 @@ impl DiagnoseCommand { tls_warning = Some("<warning>Composer is configured to disable SSL/TLS protection. This will leave remote HTTPS requests vulnerable to Man-In-The-Middle attacks.</warning>".to_string()); } - match self.http_downloader.as_mut().unwrap().get(&format!("{}://repo.packagist.org/packages.json", proto), IndexMap::new()) { + match self.http_downloader.as_mut().unwrap().get( + &format!("{}://repo.packagist.org/packages.json", proto), + IndexMap::new(), + ) { Ok(_) => {} Err(e) => { if let Some(te) = e.downcast_ref::<TransportException>() { @@ -456,7 +514,12 @@ impl DiagnoseCommand { tls_warning = Some("<warning>Composer is configured to disable SSL/TLS protection. This will leave remote HTTPS requests vulnerable to Man-In-The-Middle attacks.</warning>".to_string()); } - match self.http_downloader.as_mut().unwrap().get(url, IndexMap::new()) { + match self + .http_downloader + .as_mut() + .unwrap() + .get(url, IndexMap::new()) + { Ok(_) => {} Err(e) => { if let Some(te) = e.downcast_ref::<TransportException>() { @@ -489,7 +552,11 @@ impl DiagnoseCommand { Ok(PhpMixed::Bool(true)) } - fn check_http_proxy(&mut self, proxy: &RequestProxy, protocol: &str) -> anyhow::Result<PhpMixed> { + fn check_http_proxy( + &mut self, + proxy: &RequestProxy, + protocol: &str, + ) -> anyhow::Result<PhpMixed> { let result = self.check_connectivity_and_composer_network_http_enablement(); if result.as_bool() != Some(true) { return Ok(result); @@ -508,21 +575,32 @@ impl DiagnoseCommand { .http_downloader .as_mut() .unwrap() - .get(&format!("{}://repo.packagist.org/packages.json", protocol), IndexMap::new())? + .get( + &format!("{}://repo.packagist.org/packages.json", protocol), + IndexMap::new(), + )? .decode_json()?; if let Some(provider_includes) = json.as_array().and_then(|a| a.get("provider-includes")) { let mut hash_val = reset(&provider_includes.as_array().cloned().unwrap_or_default()); - hash_val = hash_val.as_array().and_then(|a| a.get("sha256")).map(|v| (**v).clone()).unwrap_or(PhpMixed::Null); + hash_val = hash_val + .as_array() + .and_then(|a| a.get("sha256")) + .map(|v| (**v).clone()) + .unwrap_or(PhpMixed::Null); let path = str_replace( "%hash%", hash_val.as_string().unwrap_or(""), - &key(&provider_includes.as_array().cloned().unwrap_or_default()).unwrap_or_default(), + &key(&provider_includes.as_array().cloned().unwrap_or_default()) + .unwrap_or_default(), ); let provider = self .http_downloader .as_mut() .unwrap() - .get(&format!("{}://repo.packagist.org/{}", protocol, path), IndexMap::new())? + .get( + &format!("{}://repo.packagist.org/{}", protocol, path), + IndexMap::new(), + )? .get_body(); if hash("sha256", &provider) != hash_val.as_string().unwrap_or("") { @@ -533,7 +611,10 @@ impl DiagnoseCommand { } } - Ok(PhpMixed::String(format!("<info>OK</> <comment>{}</>", proxy_status))) + Ok(PhpMixed::String(format!( + "<info>OK</> <comment>{}</>", + proxy_status + ))) } fn check_github_oauth(&mut self, domain: &str, token: &str) -> anyhow::Result<PhpMixed> { @@ -542,7 +623,11 @@ impl DiagnoseCommand { return Ok(result); } - self.inner.get_io().set_authentication(domain.to_string(), token.to_string(), Some("x-oauth-basic".to_string())); + self.inner.get_io().set_authentication( + domain.to_string(), + token.to_string(), + Some("x-oauth-basic".to_string()), + ); let url = if domain == "github.com" { format!("https://api.{}/", domain) } else { @@ -550,14 +635,19 @@ impl DiagnoseCommand { }; let mut opts: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); - opts.insert("retry-auth-failure".to_string(), Box::new(PhpMixed::Bool(false))); + opts.insert( + "retry-auth-failure".to_string(), + Box::new(PhpMixed::Bool(false)), + ); match self.http_downloader.as_mut().unwrap().get(&url, opts) { Ok(response) => { let expiration = response.get_header("github-authentication-token-expiration"); if expiration.is_none() { - return Ok(PhpMixed::String("<info>OK</> <comment>does not expire</>".to_string())); + return Ok(PhpMixed::String( + "<info>OK</> <comment>does not expire</>".to_string(), + )); } Ok(PhpMixed::String(format!( @@ -574,19 +664,31 @@ impl DiagnoseCommand { ))); } } - Ok(PhpMixed::String(format!("<error>[{}] {}</error>", get_class(&e), e.to_string()))) + Ok(PhpMixed::String(format!( + "<error>[{}] {}</error>", + get_class(&e), + e.to_string() + ))) } } } - fn get_github_rate_limit(&mut self, domain: &str, token: Option<&str>) -> anyhow::Result<PhpMixed> { + fn get_github_rate_limit( + &mut self, + domain: &str, + token: Option<&str>, + ) -> anyhow::Result<PhpMixed> { let result = self.check_connectivity_and_composer_network_http_enablement(); if result.as_bool() != Some(true) { return Ok(result); } if let Some(t) = token { - self.inner.get_io().set_authentication(domain.to_string(), t.to_string(), Some("x-oauth-basic".to_string())); + self.inner.get_io().set_authentication( + domain.to_string(), + t.to_string(), + Some("x-oauth-basic".to_string()), + ); } let url = if domain == "github.com" { @@ -595,8 +697,16 @@ impl DiagnoseCommand { format!("https://{}/api/rate_limit", domain) }; let mut opts: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); - opts.insert("retry-auth-failure".to_string(), Box::new(PhpMixed::Bool(false))); - let data = self.http_downloader.as_mut().unwrap().get(&url, opts)?.decode_json()?; + opts.insert( + "retry-auth-failure".to_string(), + Box::new(PhpMixed::Bool(false)), + ); + let data = self + .http_downloader + .as_mut() + .unwrap() + .get(&url, opts)? + .decode_json()?; Ok(data .as_array() @@ -614,7 +724,11 @@ impl DiagnoseCommand { let min_space_free = 1024 * 1024; let home_dir = config.get("home").as_string().unwrap_or("").to_string(); - let vendor_dir = config.get("vendor-dir").as_string().unwrap_or("").to_string(); + let vendor_dir = config + .get("vendor-dir") + .as_string() + .unwrap_or("") + .to_string(); let mut dir = home_dir.clone(); let df_home = disk_free_space(&home_dir); if df_home.map(|d| d < min_space_free).unwrap_or(false) { @@ -634,7 +748,9 @@ impl DiagnoseCommand { let mut errors: Vec<Box<PhpMixed>> = vec![]; let io = self.inner.get_io(); - if file_exists(&format!("{}/keys.tags.pub", home)) && file_exists(&format!("{}/keys.dev.pub", home)) { + if file_exists(&format!("{}/keys.tags.pub", home)) + && file_exists(&format!("{}/keys.dev.pub", home)) + { io.write(""); } @@ -644,7 +760,9 @@ impl DiagnoseCommand { Keys::fingerprint(&format!("{}/keys.tags.pub", home)) )); } else { - errors.push(Box::new(PhpMixed::String("<error>Missing pubkey for tags verification</error>".to_string()))); + errors.push(Box::new(PhpMixed::String( + "<error>Missing pubkey for tags verification</error>".to_string(), + ))); } if file_exists(&format!("{}/keys.dev.pub", home)) { @@ -653,11 +771,15 @@ impl DiagnoseCommand { Keys::fingerprint(&format!("{}/keys.dev.pub", home)) )); } else { - errors.push(Box::new(PhpMixed::String("<error>Missing pubkey for dev verification</error>".to_string()))); + errors.push(Box::new(PhpMixed::String( + "<error>Missing pubkey for dev verification</error>".to_string(), + ))); } if !errors.is_empty() { - errors.push(Box::new(PhpMixed::String("<error>Run composer self-update --update-keys to set them up</error>".to_string()))); + errors.push(Box::new(PhpMixed::String( + "<error>Run composer self-update --update-keys to set them up</error>".to_string(), + ))); } if !errors.is_empty() { @@ -676,10 +798,21 @@ impl DiagnoseCommand { let versions_util = Versions::new(config.clone(), self.http_downloader.clone().unwrap()); let latest = match versions_util.get_latest() { Ok(l) => l, - Err(e) => return Ok(PhpMixed::String(format!("<error>[{}] {}</error>", get_class(&e), e.to_string()))), + Err(e) => { + return Ok(PhpMixed::String(format!( + "<error>[{}] {}</error>", + get_class(&e), + e.to_string() + ))); + } }; - let latest_version = latest.as_array().and_then(|a| a.get("version")).and_then(|v| v.as_string()).unwrap_or("").to_string(); + let latest_version = latest + .as_array() + .and_then(|a| a.get("version")) + .and_then(|v| v.as_string()) + .unwrap_or("") + .to_string(); if Composer::VERSION != latest_version && Composer::VERSION != "@package_version@" { return Ok(PhpMixed::String(format!( "<comment>You are not running the latest {} version, run `composer self-update` to update ({} => {})</comment>", @@ -723,12 +856,22 @@ impl DiagnoseCommand { if version != "@package_version@" { let version_parser = VersionParser::new(); let normalized_version = version_parser.normalize(&version, None)?; - let root_pkg = RootPackage::new("composer/composer".to_string(), normalized_version, version.clone()); + let root_pkg = RootPackage::new( + "composer/composer".to_string(), + normalized_version, + version.clone(), + ); packages.push(Box::new(root_pkg)); } let mut repo_config: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); - repo_config.insert("type".to_string(), Box::new(PhpMixed::String("composer".to_string()))); - repo_config.insert("url".to_string(), Box::new(PhpMixed::String("https://packagist.org".to_string()))); + repo_config.insert( + "type".to_string(), + Box::new(PhpMixed::String("composer".to_string())), + ); + repo_config.insert( + "url".to_string(), + Box::new(PhpMixed::String("https://packagist.org".to_string())), + ); repo_set.add_repository(Box::new(ComposerRepository::new( PhpMixed::Array(repo_config), Box::new(NullIO::new()), @@ -750,11 +893,20 @@ impl DiagnoseCommand { &IndexMap::new(), ) { Ok(r) => r, - Err(e) => return Ok(PhpMixed::String(format!("<highlight>Failed performing audit: {}</>", e.to_string()))), + Err(e) => { + return Ok(PhpMixed::String(format!( + "<highlight>Failed performing audit: {}</>", + e.to_string() + ))); + } }; if result > 0 { - return Ok(PhpMixed::String(format!("<highlight>Audit found some issues:</>{}{}", PHP_EOL, io.get_output()))); + return Ok(PhpMixed::String(format!( + "<highlight>Audit found some issues:</>{}{}", + PHP_EOL, + io.get_output() + ))); } Ok(PhpMixed::Bool(true)) @@ -768,20 +920,51 @@ impl DiagnoseCommand { let version = curl_version(); let version_arr = version.as_array().cloned().unwrap_or_default(); - let libz_version = version_arr.get("libz_version").and_then(|v| v.as_string()).filter(|s| !s.is_empty()).unwrap_or("missing").to_string(); - let brotli_version = version_arr.get("brotli_version").and_then(|v| v.as_string()).filter(|s| !s.is_empty()).unwrap_or("missing").to_string(); - let ssl_version = version_arr.get("ssl_version").and_then(|v| v.as_string()).filter(|s| !s.is_empty()).unwrap_or("missing").to_string(); - let features = version_arr.get("features").and_then(|v| v.as_int()).unwrap_or(0); - let has_zstd = features != 0 && defined("CURL_VERSION_ZSTD") && 0 != (features & CURL_VERSION_ZSTD); + let libz_version = version_arr + .get("libz_version") + .and_then(|v| v.as_string()) + .filter(|s| !s.is_empty()) + .unwrap_or("missing") + .to_string(); + let brotli_version = version_arr + .get("brotli_version") + .and_then(|v| v.as_string()) + .filter(|s| !s.is_empty()) + .unwrap_or("missing") + .to_string(); + let ssl_version = version_arr + .get("ssl_version") + .and_then(|v| v.as_string()) + .filter(|s| !s.is_empty()) + .unwrap_or("missing") + .to_string(); + let features = version_arr + .get("features") + .and_then(|v| v.as_int()) + .unwrap_or(0); + let has_zstd = features != 0 + && defined("CURL_VERSION_ZSTD") + && 0 != (features & CURL_VERSION_ZSTD); let mut http_versions = "1.0, 1.1".to_string(); - if features != 0 && defined("CURL_VERSION_HTTP2") && defined("CURL_HTTP_VERSION_2_0") && (CURL_VERSION_HTTP2 & features) != 0 { + if features != 0 + && defined("CURL_VERSION_HTTP2") + && defined("CURL_HTTP_VERSION_2_0") + && (CURL_VERSION_HTTP2 & features) != 0 + { http_versions.push_str(", 2"); } - if features != 0 && defined("CURL_VERSION_HTTP3") && (features & CURL_VERSION_HTTP3) != 0 { + if features != 0 + && defined("CURL_VERSION_HTTP3") + && (features & CURL_VERSION_HTTP3) != 0 + { http_versions.push_str(", 3"); } - let curl_version_str = version_arr.get("version").and_then(|v| v.as_string()).unwrap_or("").to_string(); + let curl_version_str = version_arr + .get("version") + .and_then(|v| v.as_string()) + .unwrap_or("") + .to_string(); return format!( "<comment>{}</comment> libz <comment>{}</comment> brotli <comment>{}</comment> zstd <comment>{}</comment> ssl <comment>{}</comment> HTTP <comment>{}</comment>", curl_version_str, @@ -876,12 +1059,18 @@ impl DiagnoseCommand { errors.insert("iconv_mbstring".to_string(), PhpMixed::Bool(true)); } - if !filter_var(&ini_get("allow_url_fopen"), FILTER_VALIDATE_BOOLEAN).as_bool().unwrap_or(false) { + if !filter_var(&ini_get("allow_url_fopen"), FILTER_VALIDATE_BOOLEAN) + .as_bool() + .unwrap_or(false) + { errors.insert("allow_url_fopen".to_string(), PhpMixed::Bool(true)); } if extension_loaded("ionCube Loader") && ioncube_loader_iversion() < 40009 { - errors.insert("ioncube".to_string(), PhpMixed::String(ioncube_loader_version())); + errors.insert( + "ioncube".to_string(), + PhpMixed::String(ioncube_loader_version()), + ); } if PHP_VERSION_ID < 70205 { @@ -898,7 +1087,9 @@ impl DiagnoseCommand { if !defined("HHVM_VERSION") && !extension_loaded("apcu") - && filter_var(&ini_get("apc.enable_cli"), FILTER_VALIDATE_BOOLEAN).as_bool().unwrap_or(false) + && filter_var(&ini_get("apc.enable_cli"), FILTER_VALIDATE_BOOLEAN) + .as_bool() + .unwrap_or(false) { warnings.insert("apc_cli".to_string(), PhpMixed::Bool(true)); } @@ -930,7 +1121,10 @@ impl DiagnoseCommand { } } - if filter_var(&ini_get("xdebug.profiler_enabled"), FILTER_VALIDATE_BOOLEAN).as_bool().unwrap_or(false) { + if filter_var(&ini_get("xdebug.profiler_enabled"), FILTER_VALIDATE_BOOLEAN) + .as_bool() + .unwrap_or(false) + { warnings.insert("xdebug_profile".to_string(), PhpMixed::Bool(true)); } else if XdebugHandler::is_xdebug_active() { warnings.insert("xdebug_loaded".to_string(), PhpMixed::Bool(true)); @@ -942,12 +1136,19 @@ impl DiagnoseCommand { && version_compare(PHP_VERSION, "7.3.10", "<"))) { let _ = PHP_WINDOWS_VERSION_BUILD; - warnings.insert("onedrive".to_string(), PhpMixed::String(PHP_VERSION.to_string())); + warnings.insert( + "onedrive".to_string(), + PhpMixed::String(PHP_VERSION.to_string()), + ); } if extension_loaded("uopz") - && !(filter_var(&ini_get("uopz.disable"), FILTER_VALIDATE_BOOLEAN).as_bool().unwrap_or(false) - || filter_var(&ini_get("uopz.exit"), FILTER_VALIDATE_BOOLEAN).as_bool().unwrap_or(false)) + && !(filter_var(&ini_get("uopz.disable"), FILTER_VALIDATE_BOOLEAN) + .as_bool() + .unwrap_or(false) + || filter_var(&ini_get("uopz.exit"), FILTER_VALIDATE_BOOLEAN) + .as_bool() + .unwrap_or(false)) { warnings.insert("uopz".to_string(), PhpMixed::Bool(true)); } @@ -959,11 +1160,26 @@ impl DiagnoseCommand { if !errors.is_empty() { for (error, current) in &errors { let text = match error.as_str() { - "json" => format!("{}The json extension is missing.{}Install it or recompile php without --disable-json", PHP_EOL, PHP_EOL), - "phar" => format!("{}The phar extension is missing.{}Install it or recompile php without --disable-phar", PHP_EOL, PHP_EOL), - "filter" => format!("{}The filter extension is missing.{}Install it or recompile php without --disable-filter", PHP_EOL, PHP_EOL), - "hash" => format!("{}The hash extension is missing.{}Install it or recompile php without --disable-hash", PHP_EOL, PHP_EOL), - "iconv_mbstring" => format!("{}The iconv OR mbstring extension is required and both are missing.{}Install either of them or recompile php without --disable-iconv", PHP_EOL, PHP_EOL), + "json" => format!( + "{}The json extension is missing.{}Install it or recompile php without --disable-json", + PHP_EOL, PHP_EOL + ), + "phar" => format!( + "{}The phar extension is missing.{}Install it or recompile php without --disable-phar", + PHP_EOL, PHP_EOL + ), + "filter" => format!( + "{}The filter extension is missing.{}Install it or recompile php without --disable-filter", + PHP_EOL, PHP_EOL + ), + "hash" => format!( + "{}The hash extension is missing.{}Install it or recompile php without --disable-hash", + PHP_EOL, PHP_EOL + ), + "iconv_mbstring" => format!( + "{}The iconv OR mbstring extension is required and both are missing.{}Install either of them or recompile php without --disable-iconv", + PHP_EOL, PHP_EOL + ), "php" => format!( "{}Your PHP ({}) is too old, you must upgrade to PHP 7.2.5 or higher.", PHP_EOL, @@ -1034,7 +1250,8 @@ impl DiagnoseCommand { ), "openssl_version" => { // Attempt to parse version number out, fallback to whole string value. - let openssl_trimmed = trim(&strstr(OPENSSL_VERSION_TEXT, " ", false), " \t\n\r\0\u{0B}"); + let openssl_trimmed = + trim(&strstr(OPENSSL_VERSION_TEXT, " ", false), " \t\n\r\0\u{0B}"); let mut openssl_version = strstr(&openssl_trimmed, " ", true); if openssl_version.is_empty() { openssl_version = OPENSSL_VERSION_TEXT.to_string(); @@ -1107,8 +1324,12 @@ impl DiagnoseCommand { /// Check if allow_url_fopen is ON fn check_connectivity(&self) -> PhpMixed { - if !ini_get("allow_url_fopen").parse::<bool>().unwrap_or(false) && ini_get("allow_url_fopen") != "1" { - return PhpMixed::String("<info>SKIP</> <comment>Because allow_url_fopen is missing.</>".to_string()); + if !ini_get("allow_url_fopen").parse::<bool>().unwrap_or(false) + && ini_get("allow_url_fopen") != "1" + { + return PhpMixed::String( + "<info>SKIP</> <comment>Because allow_url_fopen is missing.</>".to_string(), + ); } PhpMixed::Bool(true) @@ -1130,8 +1351,14 @@ impl DiagnoseCommand { /// Check if Composer network is enabled for HTTP/S fn check_composer_network_http_enablement(&self) -> PhpMixed { - if Platform::get_env("COMPOSER_DISABLE_NETWORK").map(|v| !v.is_empty() && v != "0").unwrap_or(false) { - return PhpMixed::String("<info>SKIP</> <comment>Network is disabled by COMPOSER_DISABLE_NETWORK.</>".to_string()); + if Platform::get_env("COMPOSER_DISABLE_NETWORK") + .map(|v| !v.is_empty() && v != "0") + .unwrap_or(false) + { + return PhpMixed::String( + "<info>SKIP</> <comment>Network is disabled by COMPOSER_DISABLE_NETWORK.</>" + .to_string(), + ); } PhpMixed::Bool(true) diff --git a/crates/shirabe/src/command/dump_autoload_command.rs b/crates/shirabe/src/command/dump_autoload_command.rs index 7341124..295f1c0 100644 --- a/crates/shirabe/src/command/dump_autoload_command.rs +++ b/crates/shirabe/src/command/dump_autoload_command.rs @@ -3,7 +3,7 @@ use anyhow::Result; use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; -use shirabe_php_shim::{file_exists, InvalidArgumentException, PhpMixed}; +use shirabe_php_shim::{InvalidArgumentException, PhpMixed, file_exists}; use crate::command::base_command::BaseCommand; use crate::console::input::input_option::InputOption; @@ -52,7 +52,9 @@ impl DumpAutoloadCommand { vec![], vec![], ); - composer.get_event_dispatcher().dispatch(command_event.get_name(), &command_event); + composer + .get_event_dispatcher() + .dispatch(command_event.get_name(), &command_event); let installation_manager = composer.get_installation_manager(); let local_repo = composer.get_repository_manager().get_local_repository(); @@ -71,21 +73,37 @@ impl DumpAutoloadCommand { let optimize = input.get_option("optimize").as_bool().unwrap_or(false) || config.get("optimize-autoloader").as_bool().unwrap_or(false); - let authoritative = input.get_option("classmap-authoritative").as_bool().unwrap_or(false) - || config.get("classmap-authoritative").as_bool().unwrap_or(false); - let apcu_prefix = input.get_option("apcu-prefix").as_string_opt().map(|s| s.to_string()); + let authoritative = input + .get_option("classmap-authoritative") + .as_bool() + .unwrap_or(false) + || config + .get("classmap-authoritative") + .as_bool() + .unwrap_or(false); + let apcu_prefix = input + .get_option("apcu-prefix") + .as_string_opt() + .map(|s| s.to_string()); let apcu = apcu_prefix.is_some() || input.get_option("apcu").as_bool().unwrap_or(false) || config.get("apcu-autoloader").as_bool().unwrap_or(false); - if input.get_option("strict-psr").as_bool().unwrap_or(false) && !optimize && !authoritative { + if input.get_option("strict-psr").as_bool().unwrap_or(false) && !optimize && !authoritative + { return Err(InvalidArgumentException { message: "--strict-psr mode only works with optimized autoloader, use --optimize or --classmap-authoritative if you want a strict return value.".to_string(), code: 0, } .into()); } - if input.get_option("strict-ambiguous").as_bool().unwrap_or(false) && !optimize && !authoritative { + if input + .get_option("strict-ambiguous") + .as_bool() + .unwrap_or(false) + && !optimize + && !authoritative + { return Err(InvalidArgumentException { message: "--strict-ambiguous mode only works with optimized autoloader, use --optimize or --classmap-authoritative if you want a strict return value.".to_string(), code: 0, @@ -94,11 +112,17 @@ impl DumpAutoloadCommand { } if authoritative { - self.inner.get_io().write("<info>Generating optimized autoload files (authoritative)</info>"); + self.inner + .get_io() + .write("<info>Generating optimized autoload files (authoritative)</info>"); } else if optimize { - self.inner.get_io().write("<info>Generating optimized autoload files</info>"); + self.inner + .get_io() + .write("<info>Generating optimized autoload files</info>"); } else { - self.inner.get_io().write("<info>Generating autoload files</info>"); + self.inner + .get_io() + .write("<info>Generating autoload files</info>"); } let generator = composer.get_autoload_generator(); @@ -111,7 +135,9 @@ impl DumpAutoloadCommand { if input.get_option("dev").as_bool().unwrap_or(false) { if input.get_option("no-dev").as_bool().unwrap_or(false) { return Err(InvalidArgumentException { - message: "You can not use both --no-dev and --dev as they conflict with each other.".to_string(), + message: + "You can not use both --no-dev and --dev as they conflict with each other." + .to_string(), code: 0, } .into()); @@ -121,7 +147,8 @@ impl DumpAutoloadCommand { generator.set_class_map_authoritative(authoritative); generator.set_run_scripts(true); generator.set_apcu(apcu, apcu_prefix.as_deref()); - generator.set_platform_requirement_filter(self.inner.get_platform_requirement_filter(input)?); + generator + .set_platform_requirement_filter(self.inner.get_platform_requirement_filter(input)?); let class_map = generator.dump( config, &local_repo, @@ -131,23 +158,39 @@ impl DumpAutoloadCommand { optimize, None, composer.get_locker(), - input.get_option("strict-ambiguous").as_bool().unwrap_or(false), + input + .get_option("strict-ambiguous") + .as_bool() + .unwrap_or(false), )?; let number_of_classes = class_map.len(); if authoritative { self.inner.get_io().write(&format!("<info>Generated optimized autoload files (authoritative) containing {} classes</info>", number_of_classes)); } else if optimize { - self.inner.get_io().write(&format!("<info>Generated optimized autoload files containing {} classes</info>", number_of_classes)); + self.inner.get_io().write(&format!( + "<info>Generated optimized autoload files containing {} classes</info>", + number_of_classes + )); } else { - self.inner.get_io().write("<info>Generated autoload files</info>"); + self.inner + .get_io() + .write("<info>Generated autoload files</info>"); } - if missing_dependencies || (input.get_option("strict-psr").as_bool().unwrap_or(false) && !class_map.get_psr_violations().is_empty()) { + if missing_dependencies + || (input.get_option("strict-psr").as_bool().unwrap_or(false) + && !class_map.get_psr_violations().is_empty()) + { return Ok(1); } - if input.get_option("strict-ambiguous").as_bool().unwrap_or(false) && !class_map.get_ambiguous_classes(false).is_empty() { + if input + .get_option("strict-ambiguous") + .as_bool() + .unwrap_or(false) + && !class_map.get_ambiguous_classes(false).is_empty() + { return Ok(2); } diff --git a/crates/shirabe/src/command/exec_command.rs b/crates/shirabe/src/command/exec_command.rs index 432e3a0..5602e85 100644 --- a/crates/shirabe/src/command/exec_command.rs +++ b/crates/shirabe/src/command/exec_command.rs @@ -3,7 +3,7 @@ use anyhow::Result; use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; -use shirabe_php_shim::{basename, chdir, getcwd, glob, PhpMixed, RuntimeException}; +use shirabe_php_shim::{PhpMixed, RuntimeException, basename, chdir, getcwd, glob}; use crate::command::base_command::BaseCommand; use crate::console::input::input_argument::InputArgument; @@ -43,13 +43,19 @@ impl ExecCommand { ); } - pub fn interact(&self, input: &mut dyn InputInterface, _output: &dyn OutputInterface) -> Result<()> { + pub fn interact( + &self, + input: &mut dyn InputInterface, + _output: &dyn OutputInterface, + ) -> Result<()> { let binaries = self.get_binaries(false)?; if binaries.is_empty() { return Ok(()); } - if input.get_argument("binary").as_string_opt().is_some() || input.get_option("list").as_bool().unwrap_or(false) { + if input.get_argument("binary").as_string_opt().is_some() + || input.get_option("list").as_bool().unwrap_or(false) + { return Ok(()); } @@ -70,45 +76,77 @@ impl ExecCommand { Ok(()) } - pub fn execute(&self, input: &dyn InputInterface, _output: &dyn OutputInterface) -> Result<i64> { + pub fn execute( + &self, + input: &dyn InputInterface, + _output: &dyn OutputInterface, + ) -> Result<i64> { let composer = self.inner.require_composer()?; - if input.get_option("list").as_bool().unwrap_or(false) || input.get_argument("binary").as_string_opt().is_none() { + if input.get_option("list").as_bool().unwrap_or(false) + || input.get_argument("binary").as_string_opt().is_none() + { let bins = self.get_binaries(true)?; if bins.is_empty() { - let bin_dir = composer.get_config().get("bin-dir").as_string().unwrap_or("").to_string(); + let bin_dir = composer + .get_config() + .get("bin-dir") + .as_string() + .unwrap_or("") + .to_string(); return Err(RuntimeException { - message: format!("No binaries found in composer.json or in bin-dir ({})", bin_dir), + message: format!( + "No binaries found in composer.json or in bin-dir ({})", + bin_dir + ), code: 0, } .into()); } - self.inner.get_io().write("<comment>Available binaries:</comment>"); + self.inner + .get_io() + .write("<comment>Available binaries:</comment>"); for bin in &bins { - self.inner.get_io().write(&format!("<info>- {}</info>", bin)); + self.inner + .get_io() + .write(&format!("<info>- {}</info>", bin)); } return Ok(0); } - let binary = input.get_argument("binary").as_string().unwrap_or("").to_string(); + let binary = input + .get_argument("binary") + .as_string() + .unwrap_or("") + .to_string(); let dispatcher = composer.get_event_dispatcher(); dispatcher.add_listener("__exec_command", &binary); - let initial_working_directory = self.inner.get_application().get_initial_working_directory(); + let initial_working_directory = + self.inner.get_application().get_initial_working_directory(); if let Some(ref iwd) = initial_working_directory { if getcwd().as_deref() != Some(iwd.as_str()) { - chdir(iwd).map_err(|e| { - RuntimeException { message: format!("Could not switch back to working directory \"{}\"", iwd.display()), code: 0 } + chdir(iwd).map_err(|e| RuntimeException { + message: format!( + "Could not switch back to working directory \"{}\"", + iwd.display() + ), + code: 0, })?; } } - let args = input.get_argument("args") + let args = input + .get_argument("args") .as_list() - .map(|l| l.iter().filter_map(|v| v.as_string().map(|s| s.to_string())).collect::<Vec<_>>()) + .map(|l| { + l.iter() + .filter_map(|v| v.as_string().map(|s| s.to_string())) + .collect::<Vec<_>>() + }) .unwrap_or_default(); Ok(dispatcher.dispatch_script("__exec_command", true, args)?) @@ -116,11 +154,19 @@ impl ExecCommand { fn get_binaries(&self, for_display: bool) -> Result<Vec<String>> { let composer = self.inner.require_composer()?; - let bin_dir = composer.get_config().get("bin-dir").as_string().unwrap_or("").to_string(); + let bin_dir = composer + .get_config() + .get("bin-dir") + .as_string() + .unwrap_or("") + .to_string(); let bins = glob(&format!("{}/*", bin_dir)); let local_bins_raw: Vec<String> = composer.get_package().get_binaries(); let local_bins: Vec<String> = if for_display { - local_bins_raw.into_iter().map(|e| format!("{} (local)", e)).collect() + local_bins_raw + .into_iter() + .map(|e| format!("{} (local)", e)) + .collect() } else { local_bins_raw }; diff --git a/crates/shirabe/src/command/fund_command.rs b/crates/shirabe/src/command/fund_command.rs index 41b1805..398efd9 100644 --- a/crates/shirabe/src/command/fund_command.rs +++ b/crates/shirabe/src/command/fund_command.rs @@ -29,35 +29,63 @@ impl FundCommand { self.inner .set_name("fund") .set_description("Discover how to help fund the maintenance of your dependencies") - .set_definition(vec![ - InputOption::new("format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(PhpMixed::String("text".to_string())), vec!["text".to_string(), "json".to_string()]), - ]); + .set_definition(vec![InputOption::new( + "format", + Some(PhpMixed::String("f".to_string())), + Some(InputOption::VALUE_REQUIRED), + "Format of the output: text or json", + Some(PhpMixed::String("text".to_string())), + vec!["text".to_string(), "json".to_string()], + )]); } - pub fn execute(&self, input: &dyn InputInterface, _output: &dyn OutputInterface) -> Result<i64> { + pub fn execute( + &self, + input: &dyn InputInterface, + _output: &dyn OutputInterface, + ) -> Result<i64> { let composer = self.inner.require_composer()?; let repo = composer.get_repository_manager().get_local_repository(); - let remote_repos = CompositeRepository::new(composer.get_repository_manager().get_repositories()); + let remote_repos = + CompositeRepository::new(composer.get_repository_manager().get_repositories()); let mut fundings: IndexMap<String, IndexMap<String, Vec<String>>> = IndexMap::new(); let mut packages_to_load: IndexMap<String, Box<MatchAllConstraint>> = IndexMap::new(); for package in repo.get_packages() { - if (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>().is_some() { + if (package.as_any() as &dyn Any) + .downcast_ref::<AliasPackage>() + .is_some() + { continue; } - packages_to_load.insert(package.get_name().to_string(), Box::new(MatchAllConstraint::new())); + packages_to_load.insert( + package.get_name().to_string(), + Box::new(MatchAllConstraint::new()), + ); } // load all packages dev versions in parallel - let result = remote_repos.load_packages(&packages_to_load, &IndexMap::from([("dev".to_string(), BasePackage::STABILITY_DEV)]), &IndexMap::new())?; + let result = remote_repos.load_packages( + &packages_to_load, + &IndexMap::from([("dev".to_string(), BasePackage::STABILITY_DEV)]), + &IndexMap::new(), + )?; // collect funding data from default branches for package in &result.packages { - if (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>().is_none() { + if (package.as_any() as &dyn Any) + .downcast_ref::<AliasPackage>() + .is_none() + { // TODO: check for CompleteAliasPackage as well - if let Some(complete_pkg) = (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>() { - if complete_pkg.is_default_branch() && !complete_pkg.get_funding().is_empty() && packages_to_load.contains_key(complete_pkg.get_name()) { + if let Some(complete_pkg) = + (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>() + { + if complete_pkg.is_default_branch() + && !complete_pkg.get_funding().is_empty() + && packages_to_load.contains_key(complete_pkg.get_name()) + { Self::insert_funding_data(&mut fundings, complete_pkg)?; packages_to_load.remove(complete_pkg.get_name()); } @@ -67,11 +95,17 @@ impl FundCommand { // collect funding from installed packages if none was found in the default branch above for package in repo.get_packages() { - if (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>().is_some() || !packages_to_load.contains_key(package.get_name()) { + if (package.as_any() as &dyn Any) + .downcast_ref::<AliasPackage>() + .is_some() + || !packages_to_load.contains_key(package.get_name()) + { continue; } // TODO: check for CompleteAliasPackage as well - if let Some(complete_pkg) = (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>() { + if let Some(complete_pkg) = + (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>() + { if !complete_pkg.get_funding().is_empty() { Self::insert_funding_data(&mut fundings, complete_pkg)?; } @@ -82,9 +116,16 @@ impl FundCommand { let io = self.inner.get_io(); - let format = input.get_option("format").as_string().unwrap_or("text").to_string(); + let format = input + .get_option("format") + .as_string() + .unwrap_or("text") + .to_string(); if !matches!(format.as_str(), "text" | "json") { - io.write_error(&format!("Unsupported format \"{}\". See help for supported formats.", format)); + io.write_error(&format!( + "Unsupported format \"{}\". See help for supported formats.", + format + )); return Ok(1); } @@ -102,12 +143,18 @@ impl FundCommand { io.write(&line); prev = Some(line); } - io.write(&format!(" <href={}>{}</>", OutputFormatter::escape(url), url)); + io.write(&format!( + " <href={}>{}</>", + OutputFormatter::escape(url), + url + )); } } io.write(""); - io.write("Please consider following these links and sponsoring the work of package authors!"); + io.write( + "Please consider following these links and sponsoring the work of package authors!", + ); io.write("Thank you!"); } else if format == "json" { io.write(&JsonFile::encode(&fundings)); @@ -118,7 +165,10 @@ impl FundCommand { Ok(0) } - fn insert_funding_data(fundings: &mut IndexMap<String, IndexMap<String, Vec<String>>>, package: &CompletePackage) -> Result<()> { + fn insert_funding_data( + fundings: &mut IndexMap<String, IndexMap<String, Vec<String>>>, + package: &CompletePackage, + ) -> Result<()> { let pretty_name = package.get_pretty_name(); let (vendor, package_name) = pretty_name.split_once('/').unwrap_or(("", pretty_name)); @@ -128,15 +178,25 @@ impl FundCommand { continue; } let mut url = url_val.unwrap().to_string(); - let r#type = funding_option.get("type").and_then(|v| v.as_string()).unwrap_or(""); + let r#type = funding_option + .get("type") + .and_then(|v| v.as_string()) + .unwrap_or(""); if r#type == "github" { - if let Ok(Some(matches)) = Preg::is_match_with_indexed_captures(r"^https://github.com/([^/]+)$", &url) { + if let Ok(Some(matches)) = + Preg::is_match_with_indexed_captures(r"^https://github.com/([^/]+)$", &url) + { if let Some(sponsor) = matches.into_iter().nth(1) { url = format!("https://github.com/sponsors/{}", sponsor); } } } - fundings.entry(vendor.to_string()).or_default().entry(url).or_default().push(package_name.to_string()); + fundings + .entry(vendor.to_string()) + .or_default() + .entry(url) + .or_default() + .push(package_name.to_string()); } Ok(()) } diff --git a/crates/shirabe/src/command/global_command.rs b/crates/shirabe/src/command/global_command.rs index 5dc22a3..695232c 100644 --- a/crates/shirabe/src/command/global_command.rs +++ b/crates/shirabe/src/command/global_command.rs @@ -9,7 +9,7 @@ use shirabe_external_packages::symfony::console::completion::completion_suggesti use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::console::input::string_input::StringInput; use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; -use shirabe_php_shim::{chdir, LogicException, RuntimeException}; +use shirabe_php_shim::{LogicException, RuntimeException, chdir}; use crate::command::base_command::BaseCommand; use crate::console::input::input_argument::InputArgument; @@ -26,7 +26,8 @@ impl GlobalCommand { pub fn complete(&self, input: &CompletionInput, suggestions: &mut CompletionSuggestions) { let application = self.inner.get_application(); if input.must_suggest_argument_values_for("command-name") { - let names: Vec<String> = application.all() + let names: Vec<String> = application + .all() .into_iter() .filter(|cmd| !cmd.is_hidden()) .filter_map(|cmd| cmd.get_name().map(|n| n.to_string())) @@ -35,7 +36,11 @@ impl GlobalCommand { return; } - let command_name = input.get_argument("command-name").as_string().unwrap_or("").to_string(); + let command_name = input + .get_argument("command-name") + .as_string() + .unwrap_or("") + .to_string(); if application.has(&command_name) { let sub_input = self.prepare_subcommand_input(input.as_input_interface(), true); let sub_input = CompletionInput::from_string(&sub_input.to_string(), 2); @@ -51,8 +56,20 @@ impl GlobalCommand { .set_name("global") .set_description("Allows running commands in the global composer dir ($COMPOSER_HOME)") .set_definition(vec![ - InputArgument::new("command-name", Some(InputArgument::REQUIRED), "", None, vec![]), - InputArgument::new("args", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "", None, vec![]), + InputArgument::new( + "command-name", + Some(InputArgument::REQUIRED), + "", + None, + vec![], + ), + InputArgument::new( + "args", + Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), + "", + None, + vec![], + ), ]) .set_help( "Use this command as a wrapper to run other Composer commands\n\ @@ -65,7 +82,7 @@ impl GlobalCommand { XDG_CONFIG_HOME or default to /home/<user>/.config/composer\n\n\ Note: This path may vary depending on customizations to bin-dir in\n\ composer.json or the environmental variable COMPOSER_BIN_DIR.\n\n\ - Read more at https://getcomposer.org/doc/03-cli.md#global" + Read more at https://getcomposer.org/doc/03-cli.md#global", ); } @@ -89,7 +106,11 @@ impl GlobalCommand { Ok(self.inner.get_application().run(&sub_input, output)?) } - fn prepare_subcommand_input(&self, input: &dyn InputInterface, quiet: bool) -> Result<StringInput> { + fn prepare_subcommand_input( + &self, + input: &dyn InputInterface, + quiet: bool, + ) -> Result<StringInput> { if Platform::get_env("COMPOSER").is_some() { Platform::clear_env("COMPOSER"); } @@ -104,17 +125,29 @@ impl GlobalCommand { return Err(RuntimeException { message: "Could not create home directory".to_string(), code: 0, - }.into()); + } + .into()); } } - chdir(&home).map_err(|e| RuntimeException { message: format!("Could not switch to home directory \"{}\"", home), code: 0 })?; + chdir(&home).map_err(|e| RuntimeException { + message: format!("Could not switch to home directory \"{}\"", home), + code: 0, + })?; if !quiet { - self.inner.get_io().write_error(&format!("<info>Changed current directory to {}</info>", home)); + self.inner.get_io().write_error(&format!( + "<info>Changed current directory to {}</info>", + home + )); } - let new_input_str = Preg::replace(r"{\bg(?:l(?:o(?:b(?:a(?:l)?)?)?)?)?\b}", "", &input.to_string(), 1)?; + let new_input_str = Preg::replace( + r"{\bg(?:l(?:o(?:b(?:a(?:l)?)?)?)?)?\b}", + "", + &input.to_string(), + 1, + )?; self.inner.get_application().reset_composer(); Ok(StringInput::new(new_input_str)) diff --git a/crates/shirabe/src/command/home_command.rs b/crates/shirabe/src/command/home_command.rs index 12ea759..142f868 100644 --- a/crates/shirabe/src/command/home_command.rs +++ b/crates/shirabe/src/command/home_command.rs @@ -3,7 +3,7 @@ use anyhow::Result; use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; -use shirabe_php_shim::{filter_var, FILTER_VALIDATE_URL}; +use shirabe_php_shim::{FILTER_VALIDATE_URL, filter_var}; use crate::command::base_command::BaseCommand; use crate::command::completion_trait::CompletionTrait; @@ -37,31 +37,60 @@ impl HomeCommand { None, self.suggest_installed_package(), ), - InputOption::new("homepage", Some(shirabe_php_shim::PhpMixed::String("H".to_string())), Some(InputOption::VALUE_NONE), "Open the homepage instead of the repository URL.", None, vec![]), - InputOption::new("show", Some(shirabe_php_shim::PhpMixed::String("s".to_string())), Some(InputOption::VALUE_NONE), "Only show the homepage or repository URL.", None, vec![]), + InputOption::new( + "homepage", + Some(shirabe_php_shim::PhpMixed::String("H".to_string())), + Some(InputOption::VALUE_NONE), + "Open the homepage instead of the repository URL.", + None, + vec![], + ), + InputOption::new( + "show", + Some(shirabe_php_shim::PhpMixed::String("s".to_string())), + Some(InputOption::VALUE_NONE), + "Only show the homepage or repository URL.", + None, + vec![], + ), ]) .set_help( "The home command opens or shows a package's repository URL or\n\ homepage in your default browser.\n\n\ To open the homepage by default, use -H or --homepage.\n\ To show instead of open the repository or homepage URL, use -s or --show.\n\n\ - Read more at https://getcomposer.org/doc/03-cli.md#browse-home" + Read more at https://getcomposer.org/doc/03-cli.md#browse-home", ); } - pub fn execute(&self, input: &dyn InputInterface, _output: &dyn OutputInterface) -> Result<i64> { + pub fn execute( + &self, + input: &dyn InputInterface, + _output: &dyn OutputInterface, + ) -> Result<i64> { let repos = self.initialize_repos()?; let io = self.inner.get_io(); let mut return_code: i64 = 0; - let packages: Vec<String> = input.get_argument("packages") + let packages: Vec<String> = input + .get_argument("packages") .as_list() - .map(|l| l.iter().filter_map(|v| v.as_string().map(|s| s.to_string())).collect()) + .map(|l| { + l.iter() + .filter_map(|v| v.as_string().map(|s| s.to_string())) + .collect() + }) .unwrap_or_default(); let packages = if packages.is_empty() { io.write_error("No package specified, opening homepage for the root package"); - vec![self.inner.require_composer()?.get_package().get_name().to_string()] + vec![ + self.inner + .require_composer()? + .get_package() + .get_name() + .to_string(), + ] } else { packages }; @@ -87,12 +116,19 @@ impl HomeCommand { if !package_exists { return_code = 1; - io.write_error(&format!("<warning>Package {} not found</warning>", package_name)); + io.write_error(&format!( + "<warning>Package {} not found</warning>", + package_name + )); } if !handled { return_code = 1; - let msg = if show_homepage { "Invalid or missing homepage" } else { "Invalid or missing repository URL" }; + let msg = if show_homepage { + "Invalid or missing homepage" + } else { + "Invalid or missing repository URL" + }; io.write_error(&format!("<warning>{} for {}</warning>", msg, package_name)); } } @@ -100,9 +136,17 @@ impl HomeCommand { Ok(return_code) } - fn handle_package(&self, package: &dyn CompletePackageInterface, show_homepage: bool, show_only: bool) -> bool { + fn handle_package( + &self, + package: &dyn CompletePackageInterface, + show_homepage: bool, + show_only: bool, + ) -> bool { let support = package.get_support(); - let mut url = support.get("source").and_then(|v| v.as_string()).map(|s| s.to_string()) + let mut url = support + .get("source") + .and_then(|v| v.as_string()) + .map(|s| s.to_string()) .or_else(|| package.get_source_url().map(|s| s.to_string())); if url.as_deref().map_or(true, |s| s.is_empty()) || show_homepage { url = package.get_homepage().map(|s| s.to_string()); @@ -143,7 +187,10 @@ impl HomeCommand { } else if osx == 0 { process.execute(&["open", url], None); } else { - io.write_error(&format!("No suitable browser opening command found, open yourself: {}", url)); + io.write_error(&format!( + "No suitable browser opening command found, open yourself: {}", + url + )); } } @@ -152,14 +199,20 @@ impl HomeCommand { if let Some(composer) = composer { let mut repos: Vec<Box<dyn RepositoryInterface>> = vec![]; - repos.push(Box::new(RootPackageRepository::new(composer.get_package().clone_package()))); - repos.push(Box::new(composer.get_repository_manager().get_local_repository())); + repos.push(Box::new(RootPackageRepository::new( + composer.get_package().clone_package(), + ))); + repos.push(Box::new( + composer.get_repository_manager().get_local_repository(), + )); for repo in composer.get_repository_manager().get_repositories() { repos.push(repo); } return Ok(repos); } - Ok(RepositoryFactory::default_repos_with_default_manager(self.inner.get_io())) + Ok(RepositoryFactory::default_repos_with_default_manager( + self.inner.get_io(), + )) } } diff --git a/crates/shirabe/src/command/init_command.rs b/crates/shirabe/src/command/init_command.rs index 9e55092..e61b7a6 100644 --- a/crates/shirabe/src/command/init_command.rs +++ b/crates/shirabe/src/command/init_command.rs @@ -9,11 +9,11 @@ use shirabe_external_packages::symfony::component::console::input::array_input:: use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface; use shirabe_php_shim::{ - array_filter, array_flip, array_intersect_key, array_keys, array_map, basename, empty, - explode, file, file_exists, file_get_contents, file_put_contents, function_exists, - get_current_user, implode, is_dir, is_string, preg_quote, realpath, server_get, sprintf, - str_replace, strpos, strtolower, trim, ucwords, FILE_IGNORE_NEW_LINES, FILTER_VALIDATE_EMAIL, - InvalidArgumentException, PhpMixed, PHP_EOL, + FILE_IGNORE_NEW_LINES, FILTER_VALIDATE_EMAIL, InvalidArgumentException, PHP_EOL, PhpMixed, + array_filter, array_flip, array_intersect_key, array_keys, array_map, basename, empty, explode, + file, file_exists, file_get_contents, file_put_contents, function_exists, get_current_user, + implode, is_dir, is_string, preg_quote, realpath, server_get, sprintf, str_replace, strpos, + strtolower, trim, ucwords, }; use crate::command::base_command::BaseCommand; @@ -95,13 +95,9 @@ impl InitCommand { "autoload".to_string(), ]; let mut options = array_filter( - &array_intersect_key( - &input.get_options(), - &array_flip(&allowlist), - ), + &array_intersect_key(&input.get_options(), &array_flip(&allowlist)), |val: &PhpMixed| { - !matches!(val, PhpMixed::Null) - && !matches!(val, PhpMixed::List(l) if l.is_empty()) + !matches!(val, PhpMixed::Null) && !matches!(val, PhpMixed::List(l) if l.is_empty()) }, ); @@ -138,9 +134,7 @@ impl InitCommand { .into_iter() .map(|m| { Box::new(PhpMixed::Array( - m.into_iter() - .map(|(k, v)| (k, Box::new(v))) - .collect(), + m.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), )) }) .collect(), @@ -250,10 +244,7 @@ impl InitCommand { .to_string(); let namespace = self.namespace_from_package_name(&name).unwrap_or_default(); let mut psr4: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); - psr4.insert( - format!("{}\\", namespace), - Box::new(PhpMixed::String(ap)), - ); + psr4.insert(format!("{}\\", namespace), Box::new(PhpMixed::String(ap))); let mut autoload_obj: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); autoload_obj.insert("psr-4".to_string(), Box::new(PhpMixed::Array(psr4))); options.insert("autoload".to_string(), PhpMixed::Array(autoload_obj)); @@ -303,7 +294,9 @@ impl InitCommand { // try to downcast to JsonValidationException if let Some(json_err) = e.downcast_ref::<JsonValidationException>() { io.write_error( - PhpMixed::String("<error>Schema validation error, aborting</error>".to_string()), + PhpMixed::String( + "<error>Schema validation error, aborting</error>".to_string(), + ), true, IOInterface::NORMAL, ); @@ -312,10 +305,7 @@ impl InitCommand { implode(&format!("{} - ", PHP_EOL), &json_err.get_errors()) ); io.write_error( - PhpMixed::String(format!( - "{}:{}{}", - json_err.message, PHP_EOL, errors - )), + PhpMixed::String(format!("{}:{}{}", json_err.message, PHP_EOL, errors)), true, IOInterface::NORMAL, ); @@ -356,7 +346,8 @@ impl InitCommand { } } - let question = "Would you like to install dependencies now [<comment>yes</comment>]? ".to_string(); + let question = + "Would you like to install dependencies now [<comment>yes</comment>]? ".to_string(); if input.is_interactive() && self.has_dependencies(&options) && io.ask_confirmation(question, true) @@ -395,11 +386,7 @@ impl InitCommand { Ok(0) } - pub(crate) fn initialize( - &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, - ) { + pub(crate) fn initialize(&mut self, input: &dyn InputInterface, output: &dyn OutputInterface) { self.inner.initialize(input, output); if !input.is_interactive() { @@ -442,16 +429,13 @@ impl InitCommand { io.load_configuration(&config); let mut repo_manager = RepositoryFactory::manager(io, &config, None, None); - let mut repos: Vec<Box<dyn crate::repository::repository_interface::RepositoryInterface>> = - vec![Box::new(PlatformRepository::new(vec![], PhpMixed::Null))]; + let mut repos: Vec< + Box<dyn crate::repository::repository_interface::RepositoryInterface>, + > = vec![Box::new(PlatformRepository::new(vec![], PhpMixed::Null))]; let mut create_default_packagist_repo = true; for repo in &repositories { - let repo_config = RepositoryFactory::config_from_string( - io, - &config, - repo, - Some(true), - )?; + let repo_config = + RepositoryFactory::config_from_string(io, &config, repo, Some(true))?; let is_packagist_false = repo_config .get("packagist") .map(|v| v.as_bool() == Some(false)) @@ -476,10 +460,7 @@ impl InitCommand { if create_default_packagist_repo { let mut default_config: IndexMap<String, PhpMixed> = IndexMap::new(); - default_config.insert( - "type".to_string(), - PhpMixed::String("composer".to_string()), - ); + default_config.insert("type".to_string(), PhpMixed::String("composer".to_string())); default_config.insert( "url".to_string(), PhpMixed::String("https://repo.packagist.org".to_string()), @@ -672,9 +653,7 @@ impl InitCommand { ), type_val, ); - if type_value.as_string() == Some("") - || matches!(type_value, PhpMixed::Bool(false)) - { + if type_value.as_string() == Some("") || matches!(type_value, PhpMixed::Bool(false)) { type_value = PhpMixed::Null; } input.set_option("type", type_value); @@ -820,7 +799,9 @@ impl InitCommand { .as_string() .unwrap_or("") .to_string(); - let namespace = self.namespace_from_package_name(&name_str).unwrap_or_default(); + let namespace = self + .namespace_from_package_name(&name_str) + .unwrap_or_default(); let autoload_for_validate = autoload.clone(); let autoload_default = autoload.clone(); let autoload_value = io.ask_and_validate( @@ -869,7 +850,7 @@ impl InitCommand { /// @return array{name: string, email: string|null} fn parse_author_string(&self, author: &str) -> Result<IndexMap<String, Option<String>>> { if let Some(m) = Preg::is_match_strict_groups( - r"/^(?P<name>[- .,\p{L}\p{N}\p{Mn}\'’\"()]+)(?:\s+<(?P<email>.+?)>)?$/u", + r#"/^(?P<name>[- .,\p{L}\p{N}\p{Mn}\'’\"()]+)(?:\s+<(?P<email>.+?)>)?$/u"#, author, ) { let email = m.get("email").cloned(); @@ -902,10 +883,7 @@ impl InitCommand { } /// @return array<int, array{name: string, email?: string}> - pub(crate) fn format_authors( - &self, - author: &str, - ) -> Result<Vec<IndexMap<String, PhpMixed>>> { + pub(crate) fn format_authors(&self, author: &str) -> Result<Vec<IndexMap<String, PhpMixed>>> { let parsed = self.parse_author_string(author)?; let mut author_map: IndexMap<String, PhpMixed> = IndexMap::new(); let name = parsed.get("name").cloned().unwrap_or(None); @@ -950,18 +928,13 @@ impl InitCommand { let mut output = String::new(); if process.execute( - &vec![ - "git".to_string(), - "config".to_string(), - "-l".to_string(), - ], + &vec!["git".to_string(), "config".to_string(), "-l".to_string()], &mut output, None, ) == 0 { self.git_config = Some(IndexMap::new()); - let matches = - Preg::is_match_all_strict_groups(r"{^([^=]+)=(.*)$}m", &output); + let matches = Preg::is_match_all_strict_groups(r"{^([^=]+)=(.*)$}m", &output); if let Some(m) = matches { let keys: Vec<String> = m.get(1).cloned().unwrap_or_default(); let values: Vec<String> = m.get(2).cloned().unwrap_or_default(); @@ -1033,15 +1006,12 @@ impl InitCommand { fn update_dependencies(&self, output: &dyn OutputInterface) { // PHP try/catch: catch \Exception - let result = self - .inner - .get_application() - .and_then(|app| { - let update_command = app.find("update")?; - app.reset_composer()?; - update_command.run(ArrayInput::new(IndexMap::new()), output)?; - Ok(()) - }); + let result = self.inner.get_application().and_then(|app| { + let update_command = app.find("update")?; + app.reset_composer()?; + update_command.run(ArrayInput::new(IndexMap::new()), output)?; + Ok(()) + }); if let Err(_e) = result { self.inner.get_io().write_error( PhpMixed::String( @@ -1055,15 +1025,12 @@ impl InitCommand { } fn run_dump_autoload_command(&self, output: &dyn OutputInterface) { - let result = self - .inner - .get_application() - .and_then(|app| { - let command = app.find("dump-autoload")?; - app.reset_composer()?; - command.run(ArrayInput::new(IndexMap::new()), output)?; - Ok(()) - }); + let result = self.inner.get_application().and_then(|app| { + let command = app.find("dump-autoload")?; + app.reset_composer()?; + command.run(ArrayInput::new(IndexMap::new()), output)?; + Ok(()) + }); if let Err(_e) = result { self.inner.get_io().write_error( PhpMixed::String("Could not run dump-autoload.".to_string()), diff --git a/crates/shirabe/src/command/install_command.rs b/crates/shirabe/src/command/install_command.rs index 8302137..264b58c 100644 --- a/crates/shirabe/src/command/install_command.rs +++ b/crates/shirabe/src/command/install_command.rs @@ -74,8 +74,13 @@ impl InstallCommand { } let args = input.get_argument("packages"); - let args_vec: Vec<String> = args.as_list() - .map(|l| l.iter().filter_map(|v| v.as_string().map(|s| s.to_string())).collect()) + let args_vec: Vec<String> = args + .as_list() + .map(|l| { + l.iter() + .filter_map(|v| v.as_string().map(|s| s.to_string())) + .collect() + }) .unwrap_or_default(); if !args_vec.is_empty() { io.write_error(&format!( @@ -106,23 +111,43 @@ impl InstallCommand { vec![], vec![], ); - composer.get_event_dispatcher().dispatch(command_event.get_name(), &command_event); + composer + .get_event_dispatcher() + .dispatch(command_event.get_name(), &command_event); let install = Installer::create(io, &composer); let config = composer.get_config(); - let (prefer_source, prefer_dist) = self.inner.get_preferred_install_options(config, input)?; + let (prefer_source, prefer_dist) = + self.inner.get_preferred_install_options(config, input)?; - let optimize = input.get_option("optimize-autoloader").as_bool().unwrap_or(false) + let optimize = input + .get_option("optimize-autoloader") + .as_bool() + .unwrap_or(false) || config.get("optimize-autoloader").as_bool().unwrap_or(false); - let authoritative = input.get_option("classmap-authoritative").as_bool().unwrap_or(false) - || config.get("classmap-authoritative").as_bool().unwrap_or(false); - let apcu_prefix = input.get_option("apcu-autoloader-prefix").as_string_opt().map(|s| s.to_string()); + let authoritative = input + .get_option("classmap-authoritative") + .as_bool() + .unwrap_or(false) + || config + .get("classmap-authoritative") + .as_bool() + .unwrap_or(false); + let apcu_prefix = input + .get_option("apcu-autoloader-prefix") + .as_string_opt() + .map(|s| s.to_string()); let apcu = apcu_prefix.is_some() - || input.get_option("apcu-autoloader").as_bool().unwrap_or(false) + || input + .get_option("apcu-autoloader") + .as_bool() + .unwrap_or(false) || config.get("apcu-autoloader").as_bool().unwrap_or(false); - composer.get_installation_manager().set_output_progress(!input.get_option("no-progress").as_bool().unwrap_or(false)); + composer + .get_installation_manager() + .set_output_progress(!input.get_option("no-progress").as_bool().unwrap_or(false)); install .set_dry_run(input.get_option("dry-run").as_bool().unwrap_or(false)) @@ -136,7 +161,10 @@ impl InstallCommand { .set_class_map_authoritative(authoritative) .set_apcu_autoloader(apcu, apcu_prefix.as_deref()) .set_platform_requirement_filter(self.inner.get_platform_requirement_filter(input)?) - .set_audit_config(self.inner.create_audit_config(composer.get_config(), input)?) + .set_audit_config( + self.inner + .create_audit_config(composer.get_config(), input)?, + ) .set_error_on_audit(input.get_option("audit").as_bool().unwrap_or(false)); if input.get_option("no-plugins").as_bool().unwrap_or(false) { diff --git a/crates/shirabe/src/command/licenses_command.rs b/crates/shirabe/src/command/licenses_command.rs index 84cbff6..ddb7b32 100644 --- a/crates/shirabe/src/command/licenses_command.rs +++ b/crates/shirabe/src/command/licenses_command.rs @@ -33,16 +33,41 @@ impl LicensesCommand { .set_name("licenses") .set_description("Shows information about licenses of dependencies") .set_definition(vec![ - InputOption::new("format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text, json or summary", Some(PhpMixed::String("text".to_string())), vec!["text".to_string(), "json".to_string(), "summary".to_string()]), - InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables search in require-dev packages.", None, vec![]), - InputOption::new("locked", None, Some(InputOption::VALUE_NONE), "Shows licenses from the lock file instead of installed packages.", None, vec![]), + InputOption::new( + "format", + Some(PhpMixed::String("f".to_string())), + Some(InputOption::VALUE_REQUIRED), + "Format of the output: text, json or summary", + Some(PhpMixed::String("text".to_string())), + vec![ + "text".to_string(), + "json".to_string(), + "summary".to_string(), + ], + ), + InputOption::new( + "no-dev", + None, + Some(InputOption::VALUE_NONE), + "Disables search in require-dev packages.", + None, + vec![], + ), + InputOption::new( + "locked", + None, + Some(InputOption::VALUE_NONE), + "Shows licenses from the lock file instead of installed packages.", + None, + vec![], + ), ]) .set_help( "The license command displays detailed information about the licenses of\n\ the installed dependencies.\n\n\ Use --locked to show licenses from composer.lock instead of what's currently\n\ installed in the vendor directory.\n\n\ - Read more at https://getcomposer.org/doc/03-cli.md#licenses" + Read more at https://getcomposer.org/doc/03-cli.md#licenses", ); } @@ -50,8 +75,11 @@ impl LicensesCommand { let composer = self.inner.require_composer()?; // TODO(plugin): dispatch COMMAND event for plugin hooks - let command_event = CommandEvent::new(PluginEvents::COMMAND, "licenses".to_string(), input, output); - composer.get_event_dispatcher().dispatch(command_event.get_name(), &command_event); + let command_event = + CommandEvent::new(PluginEvents::COMMAND, "licenses".to_string(), input, output); + composer + .get_event_dispatcher() + .dispatch(command_event.get_name(), &command_event); let root = composer.get_package(); @@ -78,7 +106,11 @@ impl LicensesCommand { let packages = PackageSorter::sort_packages_alphabetically(packages); let io = self.inner.get_io(); - let format = input.get_option("format").as_string().unwrap_or("text").to_string(); + let format = input + .get_option("format") + .as_string() + .unwrap_or("text") + .to_string(); match format.as_str() { "text" => { let root_licenses = root.get_license(); @@ -87,23 +119,39 @@ impl LicensesCommand { } else { root_licenses.join(", ") }; - io.write(&format!("Name: <comment>{}</comment>", root.get_pretty_name())); - io.write(&format!("Version: <comment>{}</comment>", root.get_full_pretty_version())); + io.write(&format!( + "Name: <comment>{}</comment>", + root.get_pretty_name() + )); + io.write(&format!( + "Version: <comment>{}</comment>", + root.get_full_pretty_version() + )); io.write(&format!("Licenses: <comment>{}</comment>", licenses_str)); io.write("Dependencies:"); io.write(""); let mut table = Table::new(output); table.set_style("compact"); - table.set_headers(vec!["Name".to_string(), "Version".to_string(), "Licenses".to_string()]); + table.set_headers(vec![ + "Name".to_string(), + "Version".to_string(), + "Licenses".to_string(), + ]); for package in &packages { let link = PackageInfo::get_view_source_or_homepage_url(package.as_ref()); let name = if let Some(link) = link { - format!("<href={}>{}</>", OutputFormatter::escape(&link), package.get_pretty_name()) + format!( + "<href={}>{}</>", + OutputFormatter::escape(&link), + package.get_pretty_name() + ) } else { package.get_pretty_name().to_string() }; - let pkg_licenses = if let Some(complete_pkg) = (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>() { + let pkg_licenses = if let Some(complete_pkg) = + (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>() + { complete_pkg.get_license() } else { vec![] @@ -113,40 +161,85 @@ impl LicensesCommand { } else { pkg_licenses.join(", ") }; - table.add_row(vec![name, package.get_full_pretty_version().to_string(), licenses_str]); + table.add_row(vec![ + name, + package.get_full_pretty_version().to_string(), + licenses_str, + ]); } table.render(); } "json" => { - let mut dependencies: IndexMap<String, IndexMap<String, PhpMixed>> = IndexMap::new(); + let mut dependencies: IndexMap<String, IndexMap<String, PhpMixed>> = + IndexMap::new(); for package in &packages { - let pkg_licenses = if let Some(complete_pkg) = (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>() { + let pkg_licenses = if let Some(complete_pkg) = + (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>() + { complete_pkg.get_license() } else { vec![] }; let mut dep_info: IndexMap<String, PhpMixed> = IndexMap::new(); - dep_info.insert("version".to_string(), PhpMixed::String(package.get_full_pretty_version().to_string())); - dep_info.insert("license".to_string(), PhpMixed::List(pkg_licenses.into_iter().map(|l| Box::new(PhpMixed::String(l))).collect())); + dep_info.insert( + "version".to_string(), + PhpMixed::String(package.get_full_pretty_version().to_string()), + ); + dep_info.insert( + "license".to_string(), + PhpMixed::List( + pkg_licenses + .into_iter() + .map(|l| Box::new(PhpMixed::String(l))) + .collect(), + ), + ); dependencies.insert(package.get_pretty_name().to_string(), dep_info); } let mut output_map: IndexMap<String, PhpMixed> = IndexMap::new(); - output_map.insert("name".to_string(), PhpMixed::String(root.get_pretty_name().to_string())); - output_map.insert("version".to_string(), PhpMixed::String(root.get_full_pretty_version().to_string())); + output_map.insert( + "name".to_string(), + PhpMixed::String(root.get_pretty_name().to_string()), + ); + output_map.insert( + "version".to_string(), + PhpMixed::String(root.get_full_pretty_version().to_string()), + ); let root_licenses = root.get_license(); - output_map.insert("license".to_string(), PhpMixed::List(root_licenses.into_iter().map(|l| Box::new(PhpMixed::String(l))).collect())); - output_map.insert("dependencies".to_string(), PhpMixed::Array( - dependencies.into_iter().map(|(k, v)| (k, Box::new(PhpMixed::Array( - v.into_iter().map(|(k2, v2)| (k2, Box::new(v2))).collect() - )))).collect() - )); + output_map.insert( + "license".to_string(), + PhpMixed::List( + root_licenses + .into_iter() + .map(|l| Box::new(PhpMixed::String(l))) + .collect(), + ), + ); + output_map.insert( + "dependencies".to_string(), + PhpMixed::Array( + dependencies + .into_iter() + .map(|(k, v)| { + ( + k, + Box::new(PhpMixed::Array( + v.into_iter().map(|(k2, v2)| (k2, Box::new(v2))).collect(), + )), + ) + }) + .collect(), + ), + ); io.write(&JsonFile::encode(&output_map)); } "summary" => { let mut used_licenses: IndexMap<String, i64> = IndexMap::new(); for package in &packages { - let mut licenses = if let Some(complete_pkg) = (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>() { + let mut licenses = if let Some(complete_pkg) = + (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>() + { complete_pkg.get_license() } else { vec![] @@ -162,7 +255,8 @@ impl LicensesCommand { let mut entries: Vec<(String, i64)> = used_licenses.into_iter().collect(); entries.sort_by(|a, b| b.1.cmp(&a.1)); - let rows: Vec<Vec<String>> = entries.iter() + let rows: Vec<Vec<String>> = entries + .iter() .map(|(license, count)| vec![license.clone(), count.to_string()]) .collect(); @@ -174,9 +268,13 @@ impl LicensesCommand { } _ => { return Err(RuntimeException { - message: format!("Unsupported format \"{}\". See help for supported formats.", format), + message: format!( + "Unsupported format \"{}\". See help for supported formats.", + format + ), code: 0, - }.into()); + } + .into()); } } diff --git a/crates/shirabe/src/command/mod.rs b/crates/shirabe/src/command/mod.rs new file mode 100644 index 0000000..9761805 --- /dev/null +++ b/crates/shirabe/src/command/mod.rs @@ -0,0 +1,38 @@ +pub mod about_command; +pub mod archive_command; +pub mod audit_command; +pub mod base_command; +pub mod base_config_command; +pub mod base_dependency_command; +pub mod bump_command; +pub mod check_platform_reqs_command; +pub mod clear_cache_command; +pub mod completion_trait; +pub mod config_command; +pub mod create_project_command; +pub mod depends_command; +pub mod diagnose_command; +pub mod dump_autoload_command; +pub mod exec_command; +pub mod fund_command; +pub mod global_command; +pub mod home_command; +pub mod init_command; +pub mod install_command; +pub mod licenses_command; +pub mod outdated_command; +pub mod package_discovery_trait; +pub mod prohibits_command; +pub mod reinstall_command; +pub mod remove_command; +pub mod repository_command; +pub mod require_command; +pub mod run_script_command; +pub mod script_alias_command; +pub mod search_command; +pub mod self_update_command; +pub mod show_command; +pub mod status_command; +pub mod suggests_command; +pub mod update_command; +pub mod validate_command; diff --git a/crates/shirabe/src/command/outdated_command.rs b/crates/shirabe/src/command/outdated_command.rs index f7a8f47..ed927b7 100644 --- a/crates/shirabe/src/command/outdated_command.rs +++ b/crates/shirabe/src/command/outdated_command.rs @@ -1,15 +1,15 @@ //! ref: composer/src/Composer/Command/OutdatedCommand.php +use crate::command::base_command::BaseCommand; +use crate::command::completion_trait::CompletionTrait; +use crate::console::input::input_argument::InputArgument; +use crate::console::input::input_option::InputOption; use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::symfony::console::input::array_input::ArrayInput; use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; use shirabe_php_shim::PhpMixed; -use crate::command::base_command::BaseCommand; -use crate::command::completion_trait::CompletionTrait; -use crate::console::input::input_argument::InputArgument; -use crate::console::input::input_option::InputOption; #[derive(Debug)] pub struct OutdatedCommand { @@ -54,12 +54,20 @@ impl OutdatedCommand { ); } - pub fn execute(&mut self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> { + pub fn execute( + &mut self, + input: &dyn InputInterface, + output: &dyn OutputInterface, + ) -> Result<i64> { let mut args: IndexMap<String, PhpMixed> = IndexMap::new(); args.insert("command".to_string(), PhpMixed::String("show".to_string())); args.insert("--latest".to_string(), PhpMixed::Bool(true)); - if input.get_option("no-interaction").as_bool().unwrap_or(false) { + if input + .get_option("no-interaction") + .as_bool() + .unwrap_or(false) + { args.insert("--no-interaction".to_string(), PhpMixed::Bool(true)); } if input.get_option("no-plugins").as_bool().unwrap_or(false) { @@ -102,8 +110,15 @@ impl OutdatedCommand { if input.get_option("sort-by-age").as_bool().unwrap_or(false) { args.insert("--sort-by-age".to_string(), PhpMixed::Bool(true)); } - args.insert("--ignore-platform-req".to_string(), input.get_option("ignore-platform-req")); - if input.get_option("ignore-platform-reqs").as_bool().unwrap_or(false) { + args.insert( + "--ignore-platform-req".to_string(), + input.get_option("ignore-platform-req"), + ); + if input + .get_option("ignore-platform-reqs") + .as_bool() + .unwrap_or(false) + { args.insert("--ignore-platform-reqs".to_string(), PhpMixed::Bool(true)); } args.insert("--format".to_string(), input.get_option("format")); diff --git a/crates/shirabe/src/command/package_discovery_trait.rs b/crates/shirabe/src/command/package_discovery_trait.rs index df55c5c..8fcf2c8 100644 --- a/crates/shirabe/src/command/package_discovery_trait.rs +++ b/crates/shirabe/src/command/package_discovery_trait.rs @@ -8,9 +8,10 @@ use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface; use shirabe_php_shim::{ - array_keys, array_slice, array_unshift, array_values, asort, count, explode, file_get_contents, - implode, in_array, is_array, is_file, is_numeric, is_string, json_decode, levenshtein, - sprintf, strlen, strpos, trim, InvalidArgumentException, LogicException, PhpMixed, PHP_EOL, + InvalidArgumentException, LogicException, PHP_EOL, PhpMixed, array_keys, array_slice, + array_unshift, array_values, asort, count, explode, file_get_contents, implode, in_array, + is_array, is_file, is_numeric, is_string, json_decode, levenshtein, sprintf, strlen, strpos, + trim, }; use crate::composer::Composer; @@ -40,29 +41,29 @@ pub trait PackageDiscoveryTrait { // PHP: trait dependencies (provided by BaseCommand) fn get_io(&self) -> &dyn IOInterface; fn try_composer(&self) -> Option<Composer>; - fn require_composer(&self, disable_plugins: Option<bool>, disable_scripts: Option<bool>) - -> Composer; + fn require_composer( + &self, + disable_plugins: Option<bool>, + disable_scripts: Option<bool>, + ) -> Composer; fn get_platform_requirement_filter( &self, input: &dyn InputInterface, ) -> Box<dyn crate::filter::platform_requirement_filter::platform_requirement_filter_interface::PlatformRequirementFilterInterface>; - fn normalize_requirements( - &self, - requires: Vec<String>, - ) -> Vec<IndexMap<String, String>>; + fn normalize_requirements(&self, requires: Vec<String>) -> Vec<IndexMap<String, String>>; fn get_repos(&mut self) -> &CompositeRepository { if self.get_repos_mut().is_none() { // PHP: array_merge([new PlatformRepository], RepositoryFactory::defaultReposWithDefaultManager($this->getIO())) - let mut repos: Vec<Box<dyn crate::repository::repository_interface::RepositoryInterface>> = vec![ + let mut repos: Vec< + Box<dyn crate::repository::repository_interface::RepositoryInterface>, + > = vec![ // TODO(phase-b): PlatformRepository::new() signature Box::new(todo!("PlatformRepository::new()") as PlatformRepository), ]; let io_owned: Box<dyn IOInterface> = todo!("clone self.get_io() into a Box"); - for repo in - RepositoryFactory::default_repos_with_default_manager(io_owned) - { + for repo in RepositoryFactory::default_repos_with_default_manager(io_owned) { repos.push(repo); } *self.get_repos_mut() = Some(CompositeRepository::new(repos)); @@ -192,10 +193,7 @@ pub trait PackageDiscoveryTrait { &[ PhpMixed::String(version), PhpMixed::String( - requirement - .get("name") - .cloned() - .unwrap_or_default(), + requirement.get("name").cloned().unwrap_or_default(), ), ], )), @@ -245,10 +243,7 @@ pub trait PackageDiscoveryTrait { let mut matches = self.get_repos().search(package.clone(), 0, None); if count(&PhpMixed::List( - matches - .iter() - .map(|_| Box::new(PhpMixed::Null)) - .collect(), + matches.iter().map(|_| Box::new(PhpMixed::Null)).collect(), )) > 0 { // Remove existing packages from search results. @@ -277,10 +272,7 @@ pub trait PackageDiscoveryTrait { if !exact_match { let providers = self.get_repos().get_providers(package.clone()); if count(&PhpMixed::List( - providers - .iter() - .map(|_| Box::new(PhpMixed::Null)) - .collect(), + providers.iter().map(|_| Box::new(PhpMixed::Null)).collect(), )) > 0 { // PHP: array_unshift($matches, ['name' => $package, 'description' => '']); @@ -352,16 +344,12 @@ pub trait PackageDiscoveryTrait { true, IOInterface::NORMAL, ); - io.write_error( - PhpMixed::String(String::new()), - true, - IOInterface::NORMAL, - ); + io.write_error(PhpMixed::String(String::new()), true, IOInterface::NORMAL); let matches_clone = matches.clone(); let version_parser_clone = version_parser.clone(); - let validator: Box<dyn Fn(PhpMixed) -> PhpMixed> = Box::new( - move |selection_mixed: PhpMixed| -> PhpMixed { + let validator: Box<dyn Fn(PhpMixed) -> PhpMixed> = + Box::new(move |selection_mixed: PhpMixed| -> PhpMixed { let selection = selection_mixed.as_string().unwrap_or("").to_string(); if "" == selection { return PhpMixed::Bool(false); @@ -399,8 +387,7 @@ pub trait PackageDiscoveryTrait { // TODO(phase-b): throw new \Exception('Not a valid selection'); panic!("Not a valid selection"); - }, - ); + }); package = io .ask_and_validate( @@ -416,16 +403,15 @@ pub trait PackageDiscoveryTrait { // no constraint yet, determine the best version automatically if !package.is_empty() && strpos(&package, " ").is_none() { - let validator: Box<dyn Fn(PhpMixed) -> PhpMixed> = Box::new( - |input_mixed: PhpMixed| -> PhpMixed { + let validator: Box<dyn Fn(PhpMixed) -> PhpMixed> = + Box::new(|input_mixed: PhpMixed| -> PhpMixed { let input = trim(input_mixed.as_string().unwrap_or(""), None); if strlen(&input) > 0 { PhpMixed::String(input) } else { PhpMixed::Bool(false) } - }, - ); + }); let constraint_mixed = io.ask_and_validate( "Enter the version constraint to require (or leave blank to use the latest version): ".to_string(), @@ -523,26 +509,22 @@ pub trait PackageDiscoveryTrait { // Check if it is a virtual package provided by others let providers = repo_set.get_providers(name); if count(&PhpMixed::List( - providers - .iter() - .map(|_| Box::new(PhpMixed::Null)) - .collect(), + providers.iter().map(|_| Box::new(PhpMixed::Null)).collect(), )) > 0 { let mut constraint = "*".to_string(); if input.is_interactive() { let providers_count = providers.len(); let name_owned = name.to_string(); - let validator: Box<dyn Fn(PhpMixed) -> PhpMixed> = Box::new( - move |value_mixed: PhpMixed| -> PhpMixed { + let validator: Box<dyn Fn(PhpMixed) -> PhpMixed> = + Box::new(move |value_mixed: PhpMixed| -> PhpMixed { let value = value_mixed.as_string().unwrap_or("").to_string(); let parser = VersionParser::new(); // TODO(phase-b): parse_constraints returns Result let _ = parser.parse_constraints(&value); PhpMixed::String(value) - }, - ); + }); constraint = self .get_io() .ask_and_validate( @@ -676,10 +658,7 @@ pub trait PackageDiscoveryTrait { // Check for similar names/typos let similar = self.find_similar(name)?; if count(&PhpMixed::List( - similar - .iter() - .map(|_| Box::new(PhpMixed::Null)) - .collect(), + similar.iter().map(|_| Box::new(PhpMixed::Null)).collect(), )) > 0 { if in_array( @@ -737,7 +716,11 @@ pub trait PackageDiscoveryTrait { message: sprintf( &format!( "Could not find package %s.\n\nDid you mean {}?\n %s", - if similar.len() > 1 { "one of these" } else { "this" }, + if similar.len() > 1 { + "one of these" + } else { + "this" + }, ), &[ PhpMixed::String(name.to_string()), @@ -779,7 +762,8 @@ pub trait PackageDiscoveryTrait { let results: Vec<SearchResult> = match (|| -> Result<Vec<SearchResult>> { if self.get_repos_mut().is_none() { return Err(LogicException { - message: "findSimilar was called before $this->repos was initialized".to_string(), + message: "findSimilar was called before $this->repos was initialized" + .to_string(), code: 0, } .into()); @@ -890,8 +874,11 @@ pub trait PackageDiscoveryTrait { .is_some(); if has_config_platform && is_complete { // TODO(phase-b): platform_pkg.get_description() via CompletePackageInterface - platform_pkg_version = - format!("{} ({})", platform_pkg_version, todo!("platform_pkg.get_description()")); + platform_pkg_version = format!( + "{} ({})", + platform_pkg_version, + todo!("platform_pkg.get_description()") + ); } details.push(format!( "{} {} requires {} {} which does not match your installed version {}.", @@ -905,15 +892,16 @@ pub trait PackageDiscoveryTrait { } if count(&PhpMixed::List( - details - .iter() - .map(|_| Box::new(PhpMixed::Null)) - .collect(), + details.iter().map(|_| Box::new(PhpMixed::Null)).collect(), )) == 0 { return String::new(); } - format!(":{} - {}", PHP_EOL, implode(&format!("{} - ", PHP_EOL), &details)) + format!( + ":{} - {}", + PHP_EOL, + implode(&format!("{} - ", PHP_EOL), &details) + ) } } diff --git a/crates/shirabe/src/command/prohibits_command.rs b/crates/shirabe/src/command/prohibits_command.rs index 216b050..accfcf0 100644 --- a/crates/shirabe/src/command/prohibits_command.rs +++ b/crates/shirabe/src/command/prohibits_command.rs @@ -1,11 +1,11 @@ //! ref: composer/src/Composer/Command/ProhibitsCommand.php -use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; -use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; use crate::command::base_dependency_command::BaseDependencyCommand; use crate::command::completion_trait::CompletionTrait; use crate::console::input::input_argument::InputArgument; use crate::console::input::input_option::InputOption; +use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; +use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; pub struct ProhibitsCommand { inner: BaseDependencyCommand, @@ -57,7 +57,7 @@ impl ProhibitsCommand { .set_help( "Displays detailed information about why a package cannot be installed.\n\n\ <info>php composer.phar prohibits composer/composer</info>\n\n\ - Read more at https://getcomposer.org/doc/03-cli.md#prohibits-why-not" + Read more at https://getcomposer.org/doc/03-cli.md#prohibits-why-not", ); } diff --git a/crates/shirabe/src/command/reinstall_command.rs b/crates/shirabe/src/command/reinstall_command.rs index 59bb2ec..3fb26a2 100644 --- a/crates/shirabe/src/command/reinstall_command.rs +++ b/crates/shirabe/src/command/reinstall_command.rs @@ -68,7 +68,9 @@ impl ReinstallCommand { let composer = self.inner.require_composer()?; let local_repo = composer.get_repository_manager().get_local_repository(); - let mut packages_to_reinstall: Vec<Box<dyn crate::package::package_interface::PackageInterface>> = vec![]; + let mut packages_to_reinstall: Vec< + Box<dyn crate::package::package_interface::PackageInterface>, + > = vec![]; let mut package_names_to_reinstall: Vec<String> = vec![]; let type_option = input.get_option("type"); @@ -79,12 +81,20 @@ impl ReinstallCommand { if type_count > 0 { if packages_count > 0 { return Err(InvalidArgumentException { - message: "You cannot specify package names and filter by type at the same time.".to_string(), + message: + "You cannot specify package names and filter by type at the same time." + .to_string(), code: 0, - }.into()); + } + .into()); } - let filter_types: Vec<String> = type_option.as_list() - .map(|l| l.iter().filter_map(|v| v.as_string().map(|s| s.to_string())).collect()) + let filter_types: Vec<String> = type_option + .as_list() + .map(|l| { + l.iter() + .filter_map(|v| v.as_string().map(|s| s.to_string())) + .collect() + }) .unwrap_or_default(); for package in local_repo.get_canonical_packages() { if filter_types.contains(&package.get_type().to_string()) { @@ -95,12 +105,19 @@ impl ReinstallCommand { } else { if packages_count == 0 { return Err(InvalidArgumentException { - message: "You must pass one or more package names to be reinstalled.".to_string(), + message: "You must pass one or more package names to be reinstalled." + .to_string(), code: 0, - }.into()); + } + .into()); } - let patterns: Vec<String> = packages_arg.as_list() - .map(|l| l.iter().filter_map(|v| v.as_string().map(|s| s.to_string())).collect()) + let patterns: Vec<String> = packages_arg + .as_list() + .map(|l| { + l.iter() + .filter_map(|v| v.as_string().map(|s| s.to_string())) + .collect() + }) .unwrap_or_default(); for pattern in &patterns { let pattern_regexp = BasePackage::package_name_to_regexp(pattern); @@ -130,7 +147,8 @@ impl ReinstallCommand { let present_packages = local_repo.get_packages(); let result_packages = present_packages.clone(); - let present_packages: Vec<_> = present_packages.into_iter() + let present_packages: Vec<_> = present_packages + .into_iter() .filter(|package| !package_names_to_reinstall.contains(&package.get_name().to_string())) .collect(); @@ -140,15 +158,24 @@ impl ReinstallCommand { let mut install_order = indexmap::IndexMap::new(); for (index, op) in install_operations.iter().enumerate() { if let Some(install_op) = (op.as_any() as &dyn Any).downcast_ref::<InstallOperation>() { - if (install_op.get_package().as_any() as &dyn Any).downcast_ref::<AliasPackage>().is_none() { + if (install_op.get_package().as_any() as &dyn Any) + .downcast_ref::<AliasPackage>() + .is_none() + { install_order.insert(install_op.get_package().get_name().to_string(), index); } } } uninstall_operations.sort_by(|a, b| { - let a_order = install_order.get(a.get_package().get_name()).copied().unwrap_or(0); - let b_order = install_order.get(b.get_package().get_name()).copied().unwrap_or(0); + let a_order = install_order + .get(a.get_package().get_name()) + .copied() + .unwrap_or(0); + let b_order = install_order + .get(b.get_package().get_name()) + .copied() + .unwrap_or(0); b_order.cmp(&a_order) }); @@ -165,13 +192,15 @@ impl ReinstallCommand { event_dispatcher.dispatch(command_event.get_name(), &command_event); let config = composer.get_config(); - let (prefer_source, prefer_dist) = self.inner.get_preferred_install_options(config, input)?; + let (prefer_source, prefer_dist) = + self.inner.get_preferred_install_options(config, input)?; let installation_manager = composer.get_installation_manager(); let download_manager = composer.get_download_manager(); let package = composer.get_package(); - installation_manager.set_output_progress(!input.get_option("no-progress").as_bool().unwrap_or(false)); + installation_manager + .set_output_progress(!input.get_option("no-progress").as_bool().unwrap_or(false)); if input.get_option("no-plugins").as_bool().unwrap_or(false) { installation_manager.disable_plugins(); } @@ -188,19 +217,36 @@ impl ReinstallCommand { installation_manager.execute(local_repo, install_operations, dev_mode); if !input.get_option("no-autoloader").as_bool().unwrap_or(false) { - let optimize = input.get_option("optimize-autoloader").as_bool().unwrap_or(false) + let optimize = input + .get_option("optimize-autoloader") + .as_bool() + .unwrap_or(false) || config.get("optimize-autoloader").as_bool().unwrap_or(false); - let authoritative = input.get_option("classmap-authoritative").as_bool().unwrap_or(false) - || config.get("classmap-authoritative").as_bool().unwrap_or(false); - let apcu_prefix = input.get_option("apcu-autoloader-prefix").as_string_opt().map(|s| s.to_string()); + let authoritative = input + .get_option("classmap-authoritative") + .as_bool() + .unwrap_or(false) + || config + .get("classmap-authoritative") + .as_bool() + .unwrap_or(false); + let apcu_prefix = input + .get_option("apcu-autoloader-prefix") + .as_string_opt() + .map(|s| s.to_string()); let apcu = apcu_prefix.is_some() - || input.get_option("apcu-autoloader").as_bool().unwrap_or(false) + || input + .get_option("apcu-autoloader") + .as_bool() + .unwrap_or(false) || config.get("apcu-autoloader").as_bool().unwrap_or(false); let generator = composer.get_autoload_generator(); generator.set_class_map_authoritative(authoritative); generator.set_apcu(apcu, apcu_prefix.as_deref()); - generator.set_platform_requirement_filter(self.inner.get_platform_requirement_filter(input)?); + generator.set_platform_requirement_filter( + self.inner.get_platform_requirement_filter(input)?, + ); generator.dump( config, local_repo, diff --git a/crates/shirabe/src/command/remove_command.rs b/crates/shirabe/src/command/remove_command.rs index 3ad3c74..b7f8378 100644 --- a/crates/shirabe/src/command/remove_command.rs +++ b/crates/shirabe/src/command/remove_command.rs @@ -5,7 +5,7 @@ use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::symfony::component::console::exception::invalid_argument_exception::InvalidArgumentException; use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface; -use shirabe_php_shim::{array_map, strtolower, PhpMixed, UnexpectedValueException}; +use shirabe_php_shim::{PhpMixed, UnexpectedValueException, array_map, strtolower}; use crate::advisory::auditor::Auditor; use crate::command::base_command::BaseCommand; @@ -221,7 +221,11 @@ impl RemoveCommand { input: &dyn InputInterface, output: &dyn OutputInterface, ) -> anyhow::Result<i64> { - if input.get_argument("packages").as_list().map(|l| l.is_empty()).unwrap_or(true) + if input + .get_argument("packages") + .as_list() + .map(|l| l.is_empty()) + .unwrap_or(true) && !input.get_option("unused").as_bool().unwrap_or(false) { return Err(anyhow::anyhow!(InvalidArgumentException { @@ -245,7 +249,9 @@ impl RemoveCommand { let locker = composer.get_locker(); if !locker.is_locked() { return Err(anyhow::anyhow!(UnexpectedValueException { - message: "A valid composer.lock file is required to run this command with --unused".to_string(), + message: + "A valid composer.lock file is required to run this command with --unused" + .to_string(), code: 0, })); } @@ -293,7 +299,8 @@ impl RemoveCommand { packages.extend(unused); if packages.is_empty() { - self.get_io().write_error("<info>No unused packages to remove</info>"); + self.get_io() + .write_error("<info>No unused packages to remove</info>"); return Ok(0); } } @@ -339,10 +346,7 @@ impl RemoveCommand { .filter_map(|(k, v)| v.as_string().map(|_| (k.clone(), k.clone()))) .collect(); for (name, canonical) in entries { - section.insert( - strtolower(&name), - Box::new(PhpMixed::String(canonical)), - ); + section.insert(strtolower(&name), Box::new(PhpMixed::String(canonical))); } } } @@ -381,10 +385,13 @@ impl RemoveCommand { canonical_name, r#type, alt_type )); if io.is_interactive() { - if io.ask_confirmation(&format!( - "Do you want to remove it from {} [<comment>yes</comment>]? ", - alt_type - ), true) { + if io.ask_confirmation( + &format!( + "Do you want to remove it from {} [<comment>yes</comment>]? ", + alt_type + ), + true, + ) { if dry_run { to_remove .entry(alt_type.to_string()) @@ -402,11 +409,9 @@ impl RemoveCommand { .and_then(|v| v.as_array()) .map(|m| m.keys().cloned().collect()) .unwrap_or_default(); - let matches_in_type = Preg::grep( - &BasePackage::package_name_to_regexp(package), - &type_keys, - ) - .unwrap_or_default(); + let matches_in_type = + Preg::grep(&BasePackage::package_name_to_regexp(package), &type_keys) + .unwrap_or_default(); let alt_type_keys: Vec<String> = composer_data .as_array() @@ -438,10 +443,13 @@ impl RemoveCommand { matched_package, r#type, alt_type )); if io.is_interactive() { - if io.ask_confirmation(&format!( - "Do you want to remove it from {} [<comment>yes</comment>]? ", - alt_type - ), true) { + if io.ask_confirmation( + &format!( + "Do you want to remove it from {} [<comment>yes</comment>]? ", + alt_type + ), + true, + ) { if dry_run { to_remove .entry(alt_type.to_string()) @@ -470,7 +478,9 @@ impl RemoveCommand { // TODO(plugin): deactivate installed plugins if let Some(composer_opt) = self.try_composer() { - composer_opt.get_plugin_manager().deactivate_installed_plugins(); + composer_opt + .get_plugin_manager() + .deactivate_installed_plugins(); } self.reset_composer(); @@ -507,17 +517,16 @@ impl RemoveCommand { .dispatch(command_event.get_name(), command_event); let allow_plugins = composer.get_config().get("allow-plugins"); - let removed_plugins: Vec<String> = if let Some(allow_map) = - allow_plugins.as_ref().and_then(|v| v.as_array()) - { - packages - .iter() - .filter(|p| allow_map.contains_key(p.as_str())) - .cloned() - .collect() - } else { - vec![] - }; + let removed_plugins: Vec<String> = + if let Some(allow_map) = allow_plugins.as_ref().and_then(|v| v.as_array()) { + packages + .iter() + .filter(|p| allow_map.contains_key(p.as_str())) + .cloned() + .collect() + } else { + vec![] + }; if !dry_run && allow_plugins.as_ref().and_then(|v| v.as_array()).is_some() @@ -544,26 +553,67 @@ impl RemoveCommand { let mut install = Installer::create(io, &composer); let update_dev_mode = !input.get_option("update-no-dev").as_bool().unwrap_or(false); - let optimize = input.get_option("optimize-autoloader").as_bool().unwrap_or(false) - || composer.get_config().get("optimize-autoloader").and_then(|v| v.as_bool()).unwrap_or(false); - let authoritative = input.get_option("classmap-authoritative").as_bool().unwrap_or(false) - || composer.get_config().get("classmap-authoritative").and_then(|v| v.as_bool()).unwrap_or(false); - let apcu_prefix = input.get_option("apcu-autoloader-prefix").as_string().map(|s| s.to_string()); + let optimize = input + .get_option("optimize-autoloader") + .as_bool() + .unwrap_or(false) + || composer + .get_config() + .get("optimize-autoloader") + .and_then(|v| v.as_bool()) + .unwrap_or(false); + let authoritative = input + .get_option("classmap-authoritative") + .as_bool() + .unwrap_or(false) + || composer + .get_config() + .get("classmap-authoritative") + .and_then(|v| v.as_bool()) + .unwrap_or(false); + let apcu_prefix = input + .get_option("apcu-autoloader-prefix") + .as_string() + .map(|s| s.to_string()); let apcu = apcu_prefix.is_some() - || input.get_option("apcu-autoloader").as_bool().unwrap_or(false) - || composer.get_config().get("apcu-autoloader").and_then(|v| v.as_bool()).unwrap_or(false); - let minimal_changes = input.get_option("minimal-changes").as_bool().unwrap_or(false) - || composer.get_config().get("update-with-minimal-changes").and_then(|v| v.as_bool()).unwrap_or(false); + || input + .get_option("apcu-autoloader") + .as_bool() + .unwrap_or(false) + || composer + .get_config() + .get("apcu-autoloader") + .and_then(|v| v.as_bool()) + .unwrap_or(false); + let minimal_changes = input + .get_option("minimal-changes") + .as_bool() + .unwrap_or(false) + || composer + .get_config() + .get("update-with-minimal-changes") + .and_then(|v| v.as_bool()) + .unwrap_or(false); let mut update_allow_transitive_dependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE; let mut flags = String::new(); - if input.get_option("update-with-all-dependencies").as_bool().unwrap_or(false) - || input.get_option("with-all-dependencies").as_bool().unwrap_or(false) + if input + .get_option("update-with-all-dependencies") + .as_bool() + .unwrap_or(false) + || input + .get_option("with-all-dependencies") + .as_bool() + .unwrap_or(false) { update_allow_transitive_dependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS; flags += " --with-all-dependencies"; - } else if input.get_option("no-update-with-dependencies").as_bool().unwrap_or(false) { + } else if input + .get_option("no-update-with-dependencies") + .as_bool() + .unwrap_or(false) + { update_allow_transitive_dependencies = Request::UPDATE_ONLY_LISTED; flags += " --with-dependencies"; } @@ -582,9 +632,7 @@ impl RemoveCommand { install.set_update(true); install.set_install(!input.get_option("no-install").as_bool().unwrap_or(false)); install.set_update_allow_transitive_dependencies(update_allow_transitive_dependencies); - install.set_platform_requirement_filter( - self.get_platform_requirement_filter(input), - ); + install.set_platform_requirement_filter(self.get_platform_requirement_filter(input)); install.set_dry_run(dry_run); install.set_audit_config(self.create_audit_config(composer.get_config(), input)); install.set_minimal_update(minimal_changes); diff --git a/crates/shirabe/src/command/repository_command.rs b/crates/shirabe/src/command/repository_command.rs index 0c72c2a..cfe9065 100644 --- a/crates/shirabe/src/command/repository_command.rs +++ b/crates/shirabe/src/command/repository_command.rs @@ -5,8 +5,9 @@ use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput; use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; -use shirabe_php_shim::{parse_url, strtolower, InvalidArgumentException, PhpMixed, RuntimeException, - PHP_URL_HOST}; +use shirabe_php_shim::{ + InvalidArgumentException, PHP_URL_HOST, PhpMixed, RuntimeException, parse_url, strtolower, +}; use crate::command::base_config_command::BaseConfigCommand; use crate::console::input::input_argument::InputArgument; @@ -59,15 +60,44 @@ impl RepositoryCommand { ); } - pub fn execute(&mut self, input: &dyn InputInterface, _output: &dyn OutputInterface) -> anyhow::Result<i64> { - let action = strtolower(&input.get_argument("action").as_string().unwrap_or("").to_string()); - let name = input.get_argument("name").as_string().map(|s| s.to_string()); - let arg1 = input.get_argument("arg1").as_string().map(|s| s.to_string()); - let arg2 = input.get_argument("arg2").as_string().map(|s| s.to_string()); + pub fn execute( + &mut self, + input: &dyn InputInterface, + _output: &dyn OutputInterface, + ) -> anyhow::Result<i64> { + let action = strtolower( + &input + .get_argument("action") + .as_string() + .unwrap_or("") + .to_string(), + ); + let name = input + .get_argument("name") + .as_string() + .map(|s| s.to_string()); + let arg1 = input + .get_argument("arg1") + .as_string() + .map(|s| s.to_string()); + let arg2 = input + .get_argument("arg2") + .as_string() + .map(|s| s.to_string()); let config_data = self.inner.config_file.as_ref().unwrap().read()?; - let config_file_path = self.inner.config_file.as_ref().unwrap().get_path().to_string(); - self.inner.config.as_mut().unwrap().merge(config_data, &config_file_path); + let config_file_path = self + .inner + .config_file + .as_ref() + .unwrap() + .get_path() + .to_string(); + self.inner + .config + .as_mut() + .unwrap() + .merge(config_data, &config_file_path); let repos = self.inner.config.as_ref().unwrap().get_repositories(); match action.as_str() { @@ -89,7 +119,8 @@ impl RepositoryCommand { })); } let arg1_str = arg1.as_deref().unwrap(); - let repo_config: PhpMixed = if Preg::is_match(r"^\s*\{", arg1_str).unwrap_or(false) { + let repo_config: PhpMixed = if Preg::is_match(r"^\s*\{", arg1_str).unwrap_or(false) + { JsonFile::parse_json(Some(arg1_str), None)? } else { if arg2.is_none() { @@ -99,12 +130,18 @@ impl RepositoryCommand { })); } let mut m = IndexMap::new(); - m.insert("type".to_string(), Box::new(PhpMixed::String(arg1_str.to_string()))); + m.insert( + "type".to_string(), + Box::new(PhpMixed::String(arg1_str.to_string())), + ); m.insert("url".to_string(), Box::new(PhpMixed::String(arg2.unwrap()))); PhpMixed::Array(m) }; - let before = input.get_option("before").as_string().map(|s| s.to_string()); + let before = input + .get_option("before") + .as_string() + .map(|s| s.to_string()); let after = input.get_option("after").as_string().map(|s| s.to_string()); if before.is_some() && after.is_some() { return Err(anyhow::anyhow!(RuntimeException { @@ -116,18 +153,23 @@ impl RepositoryCommand { if before.is_some() || after.is_some() { if matches!(repo_config, PhpMixed::Bool(false)) { return Err(anyhow::anyhow!(RuntimeException { - message: "Cannot use --before/--after with boolean repository values".to_string(), + message: "Cannot use --before/--after with boolean repository values" + .to_string(), code: 0, })); } let reference_name = before.as_deref().or(after.as_deref()).unwrap(); let offset: i64 = if after.is_some() { 1 } else { 0 }; - self.inner.config_source.as_mut().unwrap().insert_repository( - name.as_deref().unwrap(), - repo_config, - reference_name, - offset, - ); + self.inner + .config_source + .as_mut() + .unwrap() + .insert_repository( + name.as_deref().unwrap(), + repo_config, + reference_name, + offset, + ); return Ok(0); } @@ -147,7 +189,11 @@ impl RepositoryCommand { })); } let name_str = name.as_deref().unwrap(); - self.inner.config_source.as_mut().unwrap().remove_repository(name_str); + self.inner + .config_source + .as_mut() + .unwrap() + .remove_repository(name_str); if ["packagist", "packagist.org"].contains(&name_str) { self.inner.config_source.as_mut().unwrap().add_repository( "packagist.org", @@ -164,10 +210,11 @@ impl RepositoryCommand { code: 0, })); } - self.inner.config_source.as_mut().unwrap().set_repository_url( - name.as_deref().unwrap(), - arg1.as_deref().unwrap(), - ); + self.inner + .config_source + .as_mut() + .unwrap() + .set_repository_url(name.as_deref().unwrap(), arg1.as_deref().unwrap()); Ok(0) } "get-url" | "geturl" => { @@ -201,7 +248,10 @@ impl RepositoryCommand { return Ok(0); } return Err(anyhow::anyhow!(InvalidArgumentException { - message: format!("The {} repository does not have a URL", name_str), + message: format!( + "The {} repository does not have a URL", + name_str + ), code: 0, })); } @@ -244,16 +294,24 @@ impl RepositoryCommand { } let name_str = name.as_deref().unwrap(); if ["packagist", "packagist.org"].contains(&name_str) { - self.inner.config_source.as_mut().unwrap().remove_repository("packagist.org"); + self.inner + .config_source + .as_mut() + .unwrap() + .remove_repository("packagist.org"); return Ok(0); } Err(anyhow::anyhow!(RuntimeException { - message: "Only packagist.org can be enabled/disabled using this command.".to_string(), + message: "Only packagist.org can be enabled/disabled using this command." + .to_string(), code: 0, })) } _ => Err(anyhow::anyhow!(InvalidArgumentException { - message: format!("Unknown action \"{}\". Use list, add, remove, set-url, get-url, enable, disable", action), + message: format!( + "Unknown action \"{}\". Use list, add, remove, set-url, get-url, enable, disable", + action + ), code: 0, })), } @@ -265,8 +323,10 @@ impl RepositoryCommand { let mut packagist_present = false; for (_key, repo) in &repos { if let PhpMixed::Array(ref repo_map) = *repo { - let has_type_and_url = repo_map.contains_key("type") && repo_map.contains_key("url"); - let is_composer_type = repo_map.get("type").and_then(|v| v.as_string()) == Some("composer"); + let has_type_and_url = + repo_map.contains_key("type") && repo_map.contains_key("url"); + let is_composer_type = + repo_map.get("type").and_then(|v| v.as_string()) == Some("composer"); let url_host_ends_with_packagist = repo_map .get("url") .and_then(|v| v.as_string()) @@ -346,7 +406,11 @@ impl RepositoryCommand { fn suggest_repo_names(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> { Box::new(move |input: &CompletionInput| { - let action = input.get_argument("action").as_string().unwrap_or("").to_string(); + let action = input + .get_argument("action") + .as_string() + .unwrap_or("") + .to_string(); if ["enable", "disable"].contains(&action.as_str()) { return vec!["packagist.org".to_string()]; } diff --git a/crates/shirabe/src/command/require_command.rs b/crates/shirabe/src/command/require_command.rs index 1b966f2..738790d 100644 --- a/crates/shirabe/src/command/require_command.rs +++ b/crates/shirabe/src/command/require_command.rs @@ -7,10 +7,10 @@ use shirabe_external_packages::seld::signal::signal_handler::SignalHandler; use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface; use shirabe_php_shim::{ - array_fill_keys, array_intersect, array_keys, array_map, array_merge, array_merge_recursive, - array_unique, count, empty, file_exists, file_get_contents, file_put_contents, filesize, - implode, is_writable, sprintf, strtolower, unlink, PhpMixed, RuntimeException, - UnexpectedValueException, + PhpMixed, RuntimeException, UnexpectedValueException, array_fill_keys, array_intersect, + array_keys, array_map, array_merge, array_merge_recursive, array_unique, count, empty, + file_exists, file_get_contents, file_put_contents, filesize, implode, is_writable, sprintf, + strtolower, unlink, }; use crate::advisory::auditor::Auditor; @@ -21,8 +21,8 @@ use crate::console::input::input_argument::InputArgument; use crate::console::input::input_option::InputOption; use crate::dependency_resolver::request::Request; use crate::factory::Factory; -use crate::installer::installer_events::InstallerEvents; use crate::installer::Installer; +use crate::installer::installer_events::InstallerEvents; use crate::io::io_interface::IOInterface; use crate::json::json_file::JsonFile; use crate::json::json_manipulator::JsonManipulator; @@ -63,7 +63,8 @@ impl PackageDiscoveryTrait for RequireCommand {} impl RequireCommand { pub fn configure(&mut self) { - let suggest_available_package_incl_platform = self.suggest_available_package_incl_platform(); + let suggest_available_package_incl_platform = + self.suggest_available_package_incl_platform(); let suggest_prefer_install = self.suggest_prefer_install(); self.inner .set_name("require") @@ -137,7 +138,10 @@ impl RequireCommand { self.newly_created = !file_exists(&self.file); if self.newly_created && !file_put_contents(&self.file, "{\n}\n") { io.write_error( - PhpMixed::String(format!("<error>{} could not be created.</error>", self.file)), + PhpMixed::String(format!( + "<error>{} could not be created.</error>", + self.file + )), true, IOInterface::NORMAL, ); @@ -160,8 +164,8 @@ impl RequireCommand { self.json = Some(JsonFile::new(&self.file, None, None)); self.lock = Factory::get_lock_file(&self.file); - self.composer_backup = file_get_contents(self.json.as_ref().unwrap().get_path()) - .unwrap_or_default(); + self.composer_backup = + file_get_contents(self.json.as_ref().unwrap().get_path()).unwrap_or_default(); self.lock_backup = if file_exists(&self.lock) { file_get_contents(&self.lock) } else { @@ -220,9 +224,7 @@ impl RequireCommand { }; /// @see https://github.com/composer/composer/pull/8313#issuecomment-532637955 - if package_type != "project" - && !input.get_option("dev").as_bool().unwrap_or(false) - { + if package_type != "project" && !input.get_option("dev").as_bool().unwrap_or(false) { io.write_error( PhpMixed::String( "<error>The \"--fixed\" option is only allowed for packages with a \"project\" type or for dev dependencies to prevent possible misuses.</error>" @@ -253,9 +255,9 @@ impl RequireCommand { let platform_overrides = composer.get_config().get("platform"); // initialize self.repos as it is used by the PackageDiscoveryTrait let platform_repo = PlatformRepository::new(vec![], platform_overrides); - let mut combined: Vec<Box<dyn crate::repository::repository_interface::RepositoryInterface>> = vec![ - Box::new(platform_repo.clone()), - ]; + let mut combined: Vec< + Box<dyn crate::repository::repository_interface::RepositoryInterface>, + > = vec![Box::new(platform_repo.clone())]; for repo in repos { combined.push(repo); } @@ -273,7 +275,11 @@ impl RequireCommand { input .get_argument("packages") .as_list() - .map(|l| l.iter().filter_map(|v| v.as_string().map(|s| s.to_string())).collect()) + .map(|l| { + l.iter() + .filter_map(|v| v.as_string().map(|s| s.to_string())) + .collect() + }) .unwrap_or_default(), &platform_repo, &preferred_stability, @@ -327,10 +333,8 @@ impl RequireCommand { // TODO(phase-b): instanceof CompletePackageInterface downcast let pkg_as_complete: Option<&dyn CompletePackageInterface> = None; if let Some(pkg_complete) = pkg_as_complete { - let lowered: Vec<String> = array_map( - |s: &String| strtolower(s), - &pkg_complete.get_keywords(), - ); + let lowered: Vec<String> = + array_map(|s: &String| strtolower(s), &pkg_complete.get_keywords()); let pkg_dev_tags: Vec<String> = array_intersect(&dev_tags, &lowered); if (pkg_dev_tags.len() as i64) > 0 { dev_packages.push(pkg_dev_tags); @@ -340,8 +344,16 @@ impl RequireCommand { } if (dev_packages.len() as i64) == (requirements.len() as i64) { - let plural = if (requirements.len() as i64) > 1 { "s" } else { "" }; - let plural2 = if (requirements.len() as i64) > 1 { "are" } else { "is" }; + let plural = if (requirements.len() as i64) > 1 { + "s" + } else { + "" + }; + let plural2 = if (requirements.len() as i64) > 1 { + "are" + } else { + "is" + }; let plural3 = if (requirements.len() as i64) > 1 { "they are" } else { @@ -520,7 +532,11 @@ impl RequireCommand { PhpMixed::String(format!( "<info>{} has been {}</info>", self.file, - if self.newly_created { "created" } else { "updated" } + if self.newly_created { + "created" + } else { + "updated" + } )), true, IOInterface::NORMAL, @@ -533,24 +549,24 @@ impl RequireCommand { composer.get_plugin_manager().deactivate_installed_plugins(); // try/catch/finally - let do_update_result = self.do_update(input, output, io, &requirements, require_key, remove_key); + let do_update_result = + self.do_update(input, output, io, &requirements, require_key, remove_key); let dry_run = input.get_option("dry-run").as_bool().unwrap_or(false); let result = match do_update_result { Ok(result) => { - let final_result = - if result == 0 && (requirements_to_guess.len() as i64) > 0 { - self.update_requirements_after_resolution( - &requirements_to_guess, - require_key, - remove_key, - sort_packages, - dry_run, - input.get_option("fixed").as_bool().unwrap_or(false), - )? - } else { - result - }; + let final_result = if result == 0 && (requirements_to_guess.len() as i64) > 0 { + self.update_requirements_after_resolution( + &requirements_to_guess, + require_key, + remove_key, + sort_packages, + dry_run, + input.get_option("fixed").as_bool().unwrap_or(false), + )? + } else { + result + }; Ok(final_result) } Err(e) => { @@ -598,7 +614,10 @@ impl RequireCommand { let mut require: IndexMap<String, PhpMixed> = IndexMap::new(); let mut require_dev: IndexMap<String, PhpMixed> = IndexMap::new(); - if let Some(r) = composer_definition.get("require").and_then(|v| v.as_array()) { + if let Some(r) = composer_definition + .get("require") + .and_then(|v| v.as_array()) + { for (k, v) in r { require.insert(k.clone(), (**v).clone()); } @@ -712,7 +731,10 @@ impl RequireCommand { } let update_dev_mode = !input.get_option("update-no-dev").as_bool().unwrap_or(false); - let optimize = input.get_option("optimize-autoloader").as_bool().unwrap_or(false) + let optimize = input + .get_option("optimize-autoloader") + .as_bool() + .unwrap_or(false) || composer .get_config() .get("optimize-autoloader") @@ -732,13 +754,19 @@ impl RequireCommand { .as_string() .map(|s| s.to_string()); let apcu = apcu_prefix.is_some() - || input.get_option("apcu-autoloader").as_bool().unwrap_or(false) + || input + .get_option("apcu-autoloader") + .as_bool() + .unwrap_or(false) || composer .get_config() .get("apcu-autoloader") .as_bool() .unwrap_or(false); - let minimal_changes = input.get_option("minimal-changes").as_bool().unwrap_or(false) + let minimal_changes = input + .get_option("minimal-changes") + .as_bool() + .unwrap_or(false) || composer .get_config() .get("update-with-minimal-changes") @@ -751,7 +779,10 @@ impl RequireCommand { .get_option("update-with-all-dependencies") .as_bool() .unwrap_or(false) - || input.get_option("with-all-dependencies").as_bool().unwrap_or(false) + || input + .get_option("with-all-dependencies") + .as_bool() + .unwrap_or(false) { update_allow_transitive_dependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS; flags += " --with-all-dependencies"; @@ -759,7 +790,10 @@ impl RequireCommand { .get_option("update-with-dependencies") .as_bool() .unwrap_or(false) - || input.get_option("with-dependencies").as_bool().unwrap_or(false) + || input + .get_option("with-dependencies") + .as_bool() + .unwrap_or(false) { update_allow_transitive_dependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE; @@ -781,12 +815,7 @@ impl RequireCommand { IOInterface::NORMAL, ); - let command_event = CommandEvent::new( - PluginEvents::COMMAND, - "require", - input, - output, - ); + let command_event = CommandEvent::new(PluginEvents::COMMAND, "require", input, output); composer .get_event_dispatcher() .dispatch(command_event.get_name(), &command_event); @@ -813,19 +842,22 @@ impl RequireCommand { .set_update(true) .set_install(!input.get_option("no-install").as_bool().unwrap_or(false)) .set_update_allow_transitive_dependencies(update_allow_transitive_dependencies) - .set_platform_requirement_filter( - self.inner.get_platform_requirement_filter(input)?, - ) + .set_platform_requirement_filter(self.inner.get_platform_requirement_filter(input)?) .set_prefer_stable(input.get_option("prefer-stable").as_bool().unwrap_or(false)) .set_prefer_lowest(input.get_option("prefer-lowest").as_bool().unwrap_or(false)) - .set_audit_config(self.inner.create_audit_config(composer.get_config(), input)?) + .set_audit_config( + self.inner + .create_audit_config(composer.get_config(), input)?, + ) .set_minimal_update(minimal_changes); // if no lock is present, or the file is brand new, we do not do a // partial update as this is not supported by the Installer if !self.first_require && composer.get_locker().is_locked() { install.set_update_allow_list( - array_keys(requirements).into_iter().collect::<Vec<String>>(), + array_keys(requirements) + .into_iter() + .collect::<Vec<String>>(), ); } @@ -887,8 +919,7 @@ impl RequireCommand { // TODO(phase-b): `$package instanceof AliasPackage` downcast let mut package_as_alias: Option<&AliasPackage> = None; while let Some(alias) = package_as_alias { - package = Some(Box::new(alias.get_alias_of().clone()) - as Box<dyn PackageInterface>); + package = Some(Box::new(alias.get_alias_of().clone()) as Box<dyn PackageInterface>); package_as_alias = None; } @@ -924,7 +955,10 @@ impl RequireCommand { if Preg::is_match( r"{^dev-(?!main$|master$|trunk$|latest$)}", - requirements.get(package_name).map(|s| s.as_str()).unwrap_or(""), + requirements + .get(package_name) + .map(|s| s.as_str()) + .unwrap_or(""), ) .unwrap_or(false) { @@ -967,14 +1001,9 @@ impl RequireCommand { for (package_name, flag) in &stability_flags_clone { let entry = lock_data .entry("stability-flags".to_string()) - .or_insert_with(|| { - PhpMixed::Array(IndexMap::new()) - }); + .or_insert_with(|| PhpMixed::Array(IndexMap::new())); if let PhpMixed::Array(m) = entry { - m.insert( - package_name.clone(), - Box::new(PhpMixed::Int(*flag)), - ); + m.insert(package_name.clone(), Box::new(PhpMixed::Int(*flag))); } } @@ -1000,12 +1029,7 @@ impl RequireCommand { return; } - let mut composer_definition = self - .json - .as_ref() - .unwrap() - .read() - .unwrap_or_default(); + let mut composer_definition = self.json.as_ref().unwrap().read().unwrap_or_default(); for (package, version) in new { if let Some(section) = composer_definition .entry(require_key.to_string()) @@ -1029,15 +1053,12 @@ impl RequireCommand { composer_definition.shift_remove(remove_key); } } - self.json - .as_ref() - .unwrap() - .write(&PhpMixed::Array( - composer_definition - .into_iter() - .map(|(k, v)| (k, Box::new(v))) - .collect(), - )); + self.json.as_ref().unwrap().write(&PhpMixed::Array( + composer_definition + .into_iter() + .map(|(k, v)| (k, Box::new(v))) + .collect(), + )); } /// @param array<string, string> $new @@ -1069,12 +1090,7 @@ impl RequireCommand { true } - pub(crate) fn interact( - &self, - _input: &dyn InputInterface, - _output: &dyn OutputInterface, - ) { - } + pub(crate) fn interact(&self, _input: &dyn InputInterface, _output: &dyn OutputInterface) {} fn revert_composer_file(&mut self) { let io = self.inner.get_io(); diff --git a/crates/shirabe/src/command/run_script_command.rs b/crates/shirabe/src/command/run_script_command.rs index 155a2f1..cbf85c0 100644 --- a/crates/shirabe/src/command/run_script_command.rs +++ b/crates/shirabe/src/command/run_script_command.rs @@ -47,27 +47,73 @@ impl RunScriptCommand { .set_description("Runs the scripts defined in composer.json") .set_definition(vec![ // completion callback (runtime script names) is deferred to Phase B - InputArgument::new("script", Some(InputArgument::OPTIONAL), "Script name to run.", None, vec![]), - InputArgument::new("args", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "", None, vec![]), - InputOption::new("timeout", None, Some(InputOption::VALUE_REQUIRED), "Sets script timeout in seconds, or 0 for never.", None, vec![]), - InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Sets the dev mode.", None, vec![]), - InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables the dev mode.", None, vec![]), - InputOption::new("list", Some(PhpMixed::String("l".to_string())), Some(InputOption::VALUE_NONE), "List scripts.", None, vec![]), + InputArgument::new( + "script", + Some(InputArgument::OPTIONAL), + "Script name to run.", + None, + vec![], + ), + InputArgument::new( + "args", + Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), + "", + None, + vec![], + ), + InputOption::new( + "timeout", + None, + Some(InputOption::VALUE_REQUIRED), + "Sets script timeout in seconds, or 0 for never.", + None, + vec![], + ), + InputOption::new( + "dev", + None, + Some(InputOption::VALUE_NONE), + "Sets the dev mode.", + None, + vec![], + ), + InputOption::new( + "no-dev", + None, + Some(InputOption::VALUE_NONE), + "Disables the dev mode.", + None, + vec![], + ), + InputOption::new( + "list", + Some(PhpMixed::String("l".to_string())), + Some(InputOption::VALUE_NONE), + "List scripts.", + None, + vec![], + ), ]) .set_help( "The <info>run-script</info> command runs scripts defined in composer.json:\n\n\ <info>php composer.phar run-script post-update-cmd</info>\n\n\ - Read more at https://getcomposer.org/doc/03-cli.md#run-script-run" + Read more at https://getcomposer.org/doc/03-cli.md#run-script-run", ); } - pub fn interact(&self, input: &mut dyn InputInterface, _output: &dyn OutputInterface) -> Result<()> { + pub fn interact( + &self, + input: &mut dyn InputInterface, + _output: &dyn OutputInterface, + ) -> Result<()> { let scripts = self.get_scripts()?; if scripts.is_empty() { return Ok(()); } - if input.get_argument("script").as_string_opt().is_some() || input.get_option("list").as_bool().unwrap_or(false) { + if input.get_argument("script").as_string_opt().is_some() + || input.get_option("list").as_bool().unwrap_or(false) + { return Ok(()); } @@ -103,7 +149,8 @@ impl RunScriptCommand { return Err(RuntimeException { message: "Missing required argument \"script\"".to_string(), code: 0, - }.into()); + } + .into()); } Some(s) => s.to_string(), }; @@ -114,33 +161,44 @@ impl RunScriptCommand { return Err(InvalidArgumentException { message: format!("Script \"{}\" cannot be run with this command", script), code: 0, - }.into()); + } + .into()); } } let composer = self.inner.require_composer()?; - let dev_mode = input.get_option("dev").as_bool().unwrap_or(false) || !input.get_option("no-dev").as_bool().unwrap_or(false); + let dev_mode = input.get_option("dev").as_bool().unwrap_or(false) + || !input.get_option("no-dev").as_bool().unwrap_or(false); let event = ScriptEvent::new(script.clone(), &composer, self.inner.get_io(), dev_mode); let has_listeners = composer.get_event_dispatcher().has_event_listeners(&event); if !has_listeners { return Err(InvalidArgumentException { message: format!("Script \"{}\" is not defined in this package", script), code: 0, - }.into()); + } + .into()); } - let args: Vec<String> = input.get_argument("args") + let args: Vec<String> = input + .get_argument("args") .as_list() - .map(|l| l.iter().filter_map(|v| v.as_string().map(|s| s.to_string())).collect()) + .map(|l| { + l.iter() + .filter_map(|v| v.as_string().map(|s| s.to_string())) + .collect() + }) .unwrap_or_default(); if let Some(timeout_val) = input.get_option("timeout").as_string_opt() { let timeout_str = timeout_val.to_string(); if !timeout_str.chars().all(|c| c.is_ascii_digit()) { return Err(RuntimeException { - message: "Timeout value must be numeric and positive if defined, or 0 for forever".to_string(), + message: + "Timeout value must be numeric and positive if defined, or 0 for forever" + .to_string(), code: 0, - }.into()); + } + .into()); } let timeout: i64 = timeout_str.parse().unwrap_or(0); ProcessExecutor::set_timeout(timeout); @@ -148,7 +206,9 @@ impl RunScriptCommand { Platform::put_env("COMPOSER_DEV_MODE", if dev_mode { "1" } else { "0" }); - Ok(composer.get_event_dispatcher().dispatch_script(&script, dev_mode, args)?) + Ok(composer + .get_event_dispatcher() + .dispatch_script(&script, dev_mode, args)?) } fn list_scripts(&self, output: &dyn OutputInterface) -> Result<i64> { @@ -159,7 +219,8 @@ impl RunScriptCommand { let io = self.inner.get_io(); io.write_error("<info>scripts:</info>"); - let table: Vec<Vec<String>> = scripts.iter() + let table: Vec<Vec<String>> = scripts + .iter() .map(|(name, desc)| vec![format!(" {}", name), desc.clone()]) .collect(); @@ -176,7 +237,10 @@ impl RunScriptCommand { let mut result: Vec<(String, String)> = vec![]; for (name, _script) in scripts { - let description = self.inner.get_application().find(&name) + let description = self + .inner + .get_application() + .find(&name) .map(|cmd| cmd.get_description().unwrap_or("").to_string()) .unwrap_or_default(); result.push((name, description)); diff --git a/crates/shirabe/src/command/script_alias_command.rs b/crates/shirabe/src/command/script_alias_command.rs index ad26cd7..c5bacc4 100644 --- a/crates/shirabe/src/command/script_alias_command.rs +++ b/crates/shirabe/src/command/script_alias_command.rs @@ -1,14 +1,14 @@ //! ref: composer/src/Composer/Command/ScriptAliasCommand.php -use anyhow::Result; -use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; -use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; -use shirabe_php_shim::{is_string, InvalidArgumentException, LogicException, PhpMixed}; use crate::command::base_command::BaseCommand; use crate::console::input::input_argument::InputArgument; use crate::console::input::input_option::InputOption; use crate::util::platform::Platform; +use anyhow::Result; +use shirabe_external_packages::composer::pcre::preg::Preg; +use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; +use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; +use shirabe_php_shim::{InvalidArgumentException, LogicException, PhpMixed, is_string}; pub struct ScriptAliasCommand { inner: BaseCommand, @@ -19,21 +19,30 @@ pub struct ScriptAliasCommand { impl ScriptAliasCommand { pub fn new(script: String, description: Option<String>, aliases: Vec<String>) -> Result<Self> { - let description = description.unwrap_or_else(|| format!("Runs the {} script as defined in composer.json", script)); + let description = description + .unwrap_or_else(|| format!("Runs the {} script as defined in composer.json", script)); for alias in &aliases { if !is_string(&PhpMixed::String(alias.clone())) { return Err(InvalidArgumentException { - message: r#""scripts-aliases" element array values should contain only strings"#.to_string(), + message: + r#""scripts-aliases" element array values should contain only strings"# + .to_string(), code: 0, - }.into()); + } + .into()); } } let mut inner = BaseCommand::new(); inner.ignore_validation_errors(); - Ok(Self { inner, script, description, aliases }) + Ok(Self { + inner, + script, + description, + aliases, + }) } pub fn configure(&mut self) { @@ -42,18 +51,42 @@ impl ScriptAliasCommand { .set_description(&self.description) .set_aliases(self.aliases.clone()) .set_definition(vec![ - InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Sets the dev mode.", None, vec![]), - InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables the dev mode.", None, vec![]), - InputArgument::new("args", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "", None, vec![]), + InputOption::new( + "dev", + None, + Some(InputOption::VALUE_NONE), + "Sets the dev mode.", + None, + vec![], + ), + InputOption::new( + "no-dev", + None, + Some(InputOption::VALUE_NONE), + "Disables the dev mode.", + None, + vec![], + ), + InputArgument::new( + "args", + Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), + "", + None, + vec![], + ), ]) .set_help( "The <info>run-script</info> command runs scripts defined in composer.json:\n\n\ <info>php composer.phar run-script post-update-cmd</info>\n\n\ - Read more at https://getcomposer.org/doc/03-cli.md#run-script-run" + Read more at https://getcomposer.org/doc/03-cli.md#run-script-run", ); } - pub fn execute(&mut self, input: &dyn InputInterface, _output: &dyn OutputInterface) -> Result<i64> { + pub fn execute( + &mut self, + input: &dyn InputInterface, + _output: &dyn OutputInterface, + ) -> Result<i64> { let composer = self.inner.require_composer()?; let args = input.get_arguments(); @@ -61,9 +94,13 @@ impl ScriptAliasCommand { // TODO remove for Symfony 6+ as it is then in the interface if !input.has_to_string() { return Err(LogicException { - message: format!("Expected an Input instance that is stringable, got {}", input.get_class_name()), + message: format!( + "Expected an Input instance that is stringable, got {}", + input.get_class_name() + ), code: 0, - }.into()); + } + .into()); } let dev_mode = input.get_option("dev").as_bool().unwrap_or(false) @@ -73,10 +110,18 @@ impl ScriptAliasCommand { let script_alias_input = Preg::replace_limit(r"^\S+ ?", "", &input.to_string(), 1); let mut flags = indexmap::IndexMap::new(); - flags.insert("script-alias-input".to_string(), PhpMixed::String(script_alias_input)); + flags.insert( + "script-alias-input".to_string(), + PhpMixed::String(script_alias_input), + ); let args_value = args.get("args").cloned().unwrap_or(PhpMixed::Null); - Ok(composer.get_event_dispatcher().dispatch_script(&self.script, dev_mode, args_value, flags)?) + Ok(composer.get_event_dispatcher().dispatch_script( + &self.script, + dev_mode, + args_value, + flags, + )?) } } diff --git a/crates/shirabe/src/command/search_command.rs b/crates/shirabe/src/command/search_command.rs index 87c9e72..2c727e5 100644 --- a/crates/shirabe/src/command/search_command.rs +++ b/crates/shirabe/src/command/search_command.rs @@ -1,10 +1,5 @@ //! ref: composer/src/Composer/Command/SearchCommand.php -use anyhow::Result; -use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter; -use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; -use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; -use shirabe_php_shim::{implode, in_array, preg_quote, InvalidArgumentException, PhpMixed}; use crate::command::base_command::BaseCommand; use crate::console::input::input_argument::InputArgument; use crate::console::input::input_option::InputOption; @@ -14,6 +9,11 @@ use crate::plugin::plugin_events::PluginEvents; use crate::repository::composite_repository::CompositeRepository; use crate::repository::platform_repository::PlatformRepository; use crate::repository::repository_interface::RepositoryInterface; +use anyhow::Result; +use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter; +use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; +use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; +use shirabe_php_shim::{InvalidArgumentException, PhpMixed, implode, in_array, preg_quote}; #[derive(Debug)] pub struct SearchCommand { @@ -39,23 +39,43 @@ impl SearchCommand { ); } - pub fn execute(&mut self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> { + pub fn execute( + &mut self, + input: &dyn InputInterface, + output: &dyn OutputInterface, + ) -> Result<i64> { let platform_repo = PlatformRepository; let io = self.inner.get_io(); - let format = input.get_option("format").as_string_opt().map(|s| s.to_string()).unwrap_or_else(|| "text".to_string()); - if !in_array(PhpMixed::String(format.clone()), &PhpMixed::List(vec![Box::new(PhpMixed::String("text".to_string())), Box::new(PhpMixed::String("json".to_string()))]), false) { - io.write_error(&format!("Unsupported format \"{}\". See help for supported formats.", format)); + let format = input + .get_option("format") + .as_string_opt() + .map(|s| s.to_string()) + .unwrap_or_else(|| "text".to_string()); + if !in_array( + PhpMixed::String(format.clone()), + &PhpMixed::List(vec![ + Box::new(PhpMixed::String("text".to_string())), + Box::new(PhpMixed::String("json".to_string())), + ]), + false, + ) { + io.write_error(&format!( + "Unsupported format \"{}\". See help for supported formats.", + format + )); return Ok(1); } let composer = if let Some(c) = self.inner.try_composer() { c } else { - self.inner.create_composer_instance(input, self.inner.get_io(), vec![])? + self.inner + .create_composer_instance(input, self.inner.get_io(), vec![])? }; let local_repo = composer.get_repository_manager().get_local_repository(); - let installed_repo = CompositeRepository::new(vec![Box::new(local_repo), Box::new(platform_repo)]); + let installed_repo = + CompositeRepository::new(vec![Box::new(local_repo), Box::new(platform_repo)]); let mut all_repos: Vec<Box<dyn RepositoryInterface>> = vec![Box::new(installed_repo)]; all_repos.extend(composer.get_repository_manager().get_repositories()); let repos = CompositeRepository::new(all_repos); @@ -69,7 +89,9 @@ impl SearchCommand { vec![], vec![], ); - composer.get_event_dispatcher().dispatch(command_event.get_name(), &command_event); + composer + .get_event_dispatcher() + .dispatch(command_event.get_name(), &command_event); let mut mode: i64 = RepositoryInterface::SEARCH_FULLTEXT; if input.get_option("only-name").as_bool().unwrap_or(false) { @@ -77,18 +99,27 @@ impl SearchCommand { return Err(InvalidArgumentException { message: "--only-name and --only-vendor cannot be used together".to_string(), code: 0, - }.into()); + } + .into()); } mode = RepositoryInterface::SEARCH_NAME; } else if input.get_option("only-vendor").as_bool().unwrap_or(false) { mode = RepositoryInterface::SEARCH_VENDOR; } - let r#type = input.get_option("type").as_string_opt().map(|s| s.to_string()); + let r#type = input + .get_option("type") + .as_string_opt() + .map(|s| s.to_string()); let tokens_arg = input.get_argument("tokens"); - let token_strings: Vec<String> = tokens_arg.as_array() - .map(|arr| arr.values().filter_map(|v| v.as_string().map(|s| s.to_string())).collect()) + let token_strings: Vec<String> = tokens_arg + .as_array() + .map(|arr| { + arr.values() + .filter_map(|v| v.as_string().map(|s| s.to_string())) + .collect() + }) .unwrap_or_default(); let mut query = implode(" ", &token_strings); if mode != RepositoryInterface::SEARCH_FULLTEXT { @@ -106,7 +137,11 @@ impl SearchCommand { name_length += 1; for result in &results { let description = result.description.clone().unwrap_or_default(); - let warning = if result.abandoned.is_some() { "<warning>! Abandoned !</warning> " } else { "" }; + let warning = if result.abandoned.is_some() { + "<warning>! Abandoned !</warning> " + } else { + "" + }; let remaining = width - name_length - warning.len() as i64 - 2; let description = if description.len() as i64 > remaining { format!("{}...", &description[..(remaining - 3) as usize]) @@ -125,7 +160,13 @@ impl SearchCommand { description )); } else { - io.write(&format!("{:<width$}{}{}", result.name, warning, description, width = name_length as usize)); + io.write(&format!( + "{:<width$}{}{}", + result.name, + warning, + description, + width = name_length as usize + )); } } } else if format == "json" { diff --git a/crates/shirabe/src/command/self_update_command.rs b/crates/shirabe/src/command/self_update_command.rs index 444e8fc..f69476d 100644 --- a/crates/shirabe/src/command/self_update_command.rs +++ b/crates/shirabe/src/command/self_update_command.rs @@ -6,15 +6,15 @@ use shirabe_external_packages::symfony::component::console::input::input_interfa use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface; use shirabe_external_packages::symfony::component::finder::finder::Finder; use shirabe_php_shim::{ - array_map, base64_decode, basename_with_suffix, chmod, class_exists, copy, defined, dirname, - end_arr, exec, extension_loaded, file_exists, file_get_contents, file_put_contents, - fileowner, fileperms, function_exists, hash_file, ini_get, in_array, is_array, is_file, - is_numeric, is_writable, iterator_to_array, json_decode, openssl_free_key, - openssl_get_md_methods, openssl_pkey_get_public, openssl_verify, posix_geteuid, - posix_getpwuid, random_int, rename, server_argv, sprintf, str_contains, str_replace, strpos, - strtolower, strtr, tempnam, unlink, usleep, version_compare, InvalidArgumentException, Phar, - PharException, PhpMixed, RuntimeException, UnexpectedValueException, OPENSSL_ALGO_SHA384, - PHP_EOL, PHP_VERSION_ID, + InvalidArgumentException, OPENSSL_ALGO_SHA384, PHP_EOL, PHP_VERSION_ID, Phar, PharException, + PhpMixed, RuntimeException, UnexpectedValueException, array_map, base64_decode, + basename_with_suffix, chmod, class_exists, copy, defined, dirname, end_arr, exec, + extension_loaded, file_exists, file_get_contents, file_put_contents, fileowner, fileperms, + function_exists, hash_file, in_array, ini_get, is_array, is_file, is_numeric, is_writable, + iterator_to_array, json_decode, openssl_free_key, openssl_get_md_methods, + openssl_pkey_get_public, openssl_verify, posix_geteuid, posix_getpwuid, random_int, rename, + server_argv, sprintf, str_contains, str_replace, strpos, strtolower, strtr, tempnam, unlink, + usleep, version_compare, }; use crate::command::base_command::BaseCommand; @@ -154,11 +154,19 @@ impl SelfUpdateCommand { } } - if input.get_option("set-channel-only").as_bool().unwrap_or(false) { + if input + .get_option("set-channel-only") + .as_bool() + .unwrap_or(false) + { return Ok(0); } - let cache_dir = config.get("cache-dir").as_string().unwrap_or("").to_string(); + let cache_dir = config + .get("cache-dir") + .as_string() + .unwrap_or("") + .to_string(); let rollback_dir = config.get("data-dir").as_string().unwrap_or("").to_string(); let home = config.get("home").as_string().unwrap_or("").to_string(); let local_filename = Phar::running(false); @@ -414,7 +422,8 @@ impl SelfUpdateCommand { ], ); - let updating_to_tag = !Preg::is_match(r"{^[0-9a-f]{40}$}", &update_version).unwrap_or(false); + let updating_to_tag = + !Preg::is_match(r"{^[0-9a-f]{40}$}", &update_version).unwrap_or(false); io.write( PhpMixed::String(sprintf( @@ -458,16 +467,9 @@ impl SelfUpdateCommand { IOInterface::NORMAL, ); http_downloader.copy(&remote_filename, &temp_filename)?; - io.write_error( - PhpMixed::String(String::new()), - true, - IOInterface::NORMAL, - ); + io.write_error(PhpMixed::String(String::new()), true, IOInterface::NORMAL); - if !file_exists(&temp_filename) - || signature.is_none() - || signature.as_deref() == Some("") - { + if !file_exists(&temp_filename) || signature.is_none() || signature.as_deref() == Some("") { io.write_error( PhpMixed::String( "<error>The download of the new composer version failed for an unexpected reason</error>" @@ -675,20 +677,21 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\ ); // TODO(phase-b): closure captures none; PHP throws inside the closure on bad input - let validator: Box<dyn Fn(PhpMixed) -> PhpMixed> = Box::new(|value: PhpMixed| -> PhpMixed { - let value_str = value.as_string().unwrap_or("").to_string(); - if !Preg::is_match(r"{^-----BEGIN PUBLIC KEY-----$}", &shirabe_php_shim::trim(&value_str, None)) + let validator: Box<dyn Fn(PhpMixed) -> PhpMixed> = + Box::new(|value: PhpMixed| -> PhpMixed { + let value_str = value.as_string().unwrap_or("").to_string(); + if !Preg::is_match( + r"{^-----BEGIN PUBLIC KEY-----$}", + &shirabe_php_shim::trim(&value_str, None), + ) .unwrap_or(false) - { - // TODO(phase-b): closure cannot throw - panic!("{}", "Invalid input"); - } + { + // TODO(phase-b): closure cannot throw + panic!("{}", "Invalid input"); + } - PhpMixed::String(format!( - "{}\n", - shirabe_php_shim::trim(&value_str, None) - )) - }); + PhpMixed::String(format!("{}\n", shirabe_php_shim::trim(&value_str, None))) + }); let mut dev_key = String::new(); let mut match_: Option<String> = None; @@ -717,17 +720,17 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\ if line_str.is_empty() { break; } - dev_key.push_str(&format!( - "{}\n", - shirabe_php_shim::trim(&line_str, None) - )); + dev_key.push_str(&format!("{}\n", shirabe_php_shim::trim(&line_str, None))); if shirabe_php_shim::trim(&line_str, None) == "-----END PUBLIC KEY-----" { break; } } } let _ = &validator; - let key_path = format!("{}/keys.dev.pub", config.get("home").as_string().unwrap_or("")); + let key_path = format!( + "{}/keys.dev.pub", + config.get("home").as_string().unwrap_or("") + ); file_put_contents(&key_path, match_.as_deref().unwrap_or("")); io.write( PhpMixed::String(format!( @@ -765,16 +768,16 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\ if line_str.is_empty() { break; } - tags_key.push_str(&format!( - "{}\n", - shirabe_php_shim::trim(&line_str, None) - )); + tags_key.push_str(&format!("{}\n", shirabe_php_shim::trim(&line_str, None))); if shirabe_php_shim::trim(&line_str, None) == "-----END PUBLIC KEY-----" { break; } } } - let key_path = format!("{}/keys.tags.pub", config.get("home").as_string().unwrap_or("")); + let key_path = format!( + "{}/keys.tags.pub", + config.get("home").as_string().unwrap_or("") + ); file_put_contents(&key_path, match_.as_deref().unwrap_or("")); io.write( PhpMixed::String(format!( @@ -821,12 +824,17 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\ let old_file = format!( "{}/{}{}", - rollback_dir, rollback_version, Self::OLD_INSTALL_EXT + rollback_dir, + rollback_version, + Self::OLD_INSTALL_EXT ); if !is_file(&old_file) { return Err(FilesystemException::new( - format!("Composer rollback failed: \"{}\" could not be found", old_file), + format!( + "Composer rollback failed: \"{}\" could not be found", + old_file + ), 0, ) .0 @@ -834,7 +842,10 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\ } if !Filesystem::is_readable(&old_file) { return Err(FilesystemException::new( - format!("Composer rollback failed: \"{}\" could not be read", old_file), + format!( + "Composer rollback failed: \"{}\" could not be read", + old_file + ), 0, ) .0 @@ -877,7 +888,11 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\ io.write_error( PhpMixed::String(format!( "<error>The {} file is corrupted ({})</error>", - if backup_target.is_some() { "update" } else { "backup" }, + if backup_target.is_some() { + "update" + } else { + "backup" + }, error.unwrap_or_default() )), true, @@ -931,15 +946,16 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\ let _ = unlink(new_filename); let action = format!( "Composer {}", - if backup_target.is_some() { "update" } else { "rollback" } + if backup_target.is_some() { + "update" + } else { + "rollback" + } ); Err(FilesystemException::new( format!( "{} failed: \"{}\" could not be written.{}{}", - action, - local_filename, - PHP_EOL, - e + action, local_filename, PHP_EOL, e ), 0, ) @@ -1061,7 +1077,8 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\ IOInterface::NORMAL, ); let help_message = "Please run the self-update command as an Administrator."; - let question = "Complete this operation with Administrator privileges [<comment>Y,n</comment>]? "; + let question = + "Complete this operation with Administrator privileges [<comment>Y,n</comment>]? "; if !io.ask_confirmation(question.to_string(), true) { io.write_error( @@ -1081,10 +1098,7 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\ Some(f) => f, None => { io.write_error( - PhpMixed::String(format!( - "<error>Operation failed. {}</error>", - help_message - )), + PhpMixed::String(format!("<error>Operation failed. {}</error>", help_message)), true, IOInterface::NORMAL, ); @@ -1095,7 +1109,11 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\ let mut output: Vec<String> = vec![]; let mut exit_code: i64 = 0; - exec("sudo config 2> NUL", Some(&mut output), Some(&mut exit_code)); + exec( + "sudo config 2> NUL", + Some(&mut output), + Some(&mut exit_code), + ); let using_sudo = exit_code == 0; let script = if using_sudo { @@ -1151,10 +1169,7 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\ ); } else { io.write_error( - PhpMixed::String(format!( - "<error>Operation failed. {}</error>", - help_message - )), + PhpMixed::String(format!("<error>Operation failed. {}</error>", help_message)), true, IOInterface::NORMAL, ); diff --git a/crates/shirabe/src/command/show_command.rs b/crates/shirabe/src/command/show_command.rs index da060f1..8b5e967 100644 --- a/crates/shirabe/src/command/show_command.rs +++ b/crates/shirabe/src/command/show_command.rs @@ -10,8 +10,8 @@ use shirabe_external_packages::symfony::console::formatter::output_formatter_sty use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; use shirabe_php_shim::{ - array_search, date, extension_loaded, in_array, realpath, strtolower, version_compare, - InvalidArgumentException, LogicException, PhpMixed, UnexpectedValueException, + InvalidArgumentException, LogicException, PhpMixed, UnexpectedValueException, array_search, + date, extension_loaded, in_array, realpath, strtolower, version_compare, }; use shirabe_semver::constraint::constraint_interface::ConstraintInterface; @@ -73,9 +73,7 @@ impl ShowCommand { ); } - pub fn suggest_package_based_on_mode( - &self, - ) -> Box<dyn Fn(&CompletionInput) -> Vec<String>> { + pub fn suggest_package_based_on_mode(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String>> { // return function (CompletionInput $input) { ... } Box::new(|_input: &CompletionInput| -> Vec<String> { // TODO(plugin): inspect $input->getOption() and dispatch to specific suggesters @@ -104,12 +102,7 @@ impl ShowCommand { if input.get_option("outdated").as_bool() == Some(true) { input.set_option("latest", PhpMixed::Bool(true)); - } else if input - .get_option("ignore") - .as_list() - .map_or(0, |l| l.len()) - > 0 - { + } else if input.get_option("ignore").as_list().map_or(0, |l| l.len()) > 0 { io.write_error("<warning>You are using the option \"ignore\" for action other than \"outdated\", it will be ignored.</warning>"); } @@ -141,7 +134,9 @@ impl ShowCommand { .filter(|b| **b) .count(); if only_count > 1 { - io.write_error("Only one of --major-only, --minor-only or --patch-only can be used at once"); + io.write_error( + "Only one of --major-only, --minor-only or --patch-only can be used at once", + ); return Ok(1); } @@ -149,7 +144,9 @@ impl ShowCommand { if input.get_option("tree").as_bool() == Some(true) && input.get_option("latest").as_bool() == Some(true) { - io.write_error("The --tree (-t) option is not usable in combination with --latest (-l)"); + io.write_error( + "The --tree (-t) option is not usable in combination with --latest (-l)", + ); return Ok(1); } @@ -226,8 +223,9 @@ impl ShowCommand { )])); single_package = package.into_complete_package_interface(); } else if input.get_option("platform").as_bool() == Some(true) { - installed_repo = - Box::new(InstalledRepository::new(vec![Box::new(platform_repo.clone())])); + installed_repo = Box::new(InstalledRepository::new(vec![Box::new( + platform_repo.clone(), + )])); repos = Box::new(InstalledRepository::new(vec![Box::new( platform_repo.clone(), )])); @@ -240,8 +238,7 @@ impl ShowCommand { ir.add_repository(composer.get_repository_manager().get_local_repository()); installed_repo = Box::new(ir); } else { - let default_repos = - RepositoryFactory::default_repos_with_default_manager(io); + let default_repos = RepositoryFactory::default_repos_with_default_manager(io); let names: Vec<String> = default_repos.keys().cloned().collect(); repos = Box::new(CompositeRepository::new( default_repos.into_values().collect(), @@ -270,15 +267,13 @@ impl ShowCommand { Box::new(platform_repo.clone()), ])); } - let mut composite_input: Vec<Box<dyn RepositoryInterface>> = - vec![Box::new(FilterRepository::new( - installed_repo.as_repository_interface().clone_box(), - { - let mut m = IndexMap::new(); - m.insert("canonical".to_string(), PhpMixed::Bool(false)); - m - }, - ))]; + let mut composite_input: Vec<Box<dyn RepositoryInterface>> = vec![Box::new( + FilterRepository::new(installed_repo.as_repository_interface().clone_box(), { + let mut m = IndexMap::new(); + m.insert("canonical".to_string(), PhpMixed::Bool(false)); + m + }), + )]; for r in composer_ref.get_repository_manager().get_repositories() { composite_input.push(r); } @@ -389,12 +384,16 @@ impl ShowCommand { } if input.get_option("latest").as_bool() == Some(true) && composer.is_none() { - io.write_error("No composer.json found in the current directory, disabling \"latest\" option"); + io.write_error( + "No composer.json found in the current directory, disabling \"latest\" option", + ); input.set_option("latest", PhpMixed::Bool(false)); } - let package_filter: Option<String> = - input.get_argument("package").as_string().map(|s| s.to_string()); + let package_filter: Option<String> = input + .get_argument("package") + .as_string() + .map(|s| s.to_string()); // show single package or single version if let Some(ref pkg) = single_package { @@ -404,12 +403,8 @@ impl ShowCommand { ); } else if let Some(ref pf) = package_filter { if !pf.contains('*') { - let (matched_package, vers) = self.get_package( - &*installed_repo, - &*repos, - pf, - input.get_argument("version"), - )?; + let (matched_package, vers) = + self.get_package(&*installed_repo, &*repos, pf, input.get_argument("version"))?; if let Some(ref pkg) = matched_package { if input.get_option("direct").as_bool() == Some(true) { @@ -458,7 +453,9 @@ impl ShowCommand { if input.get_option("all").as_bool() != Some(true) && input.get_option("available").as_bool() != Some(true) { - hint.push_str(", try using --available (-a) to show all available packages"); + hint.push_str( + ", try using --available (-a) to show all available packages", + ); } return Err(InvalidArgumentException { @@ -541,8 +538,7 @@ impl ShowCommand { .get_install_path(package.as_package_interface()); if let Some(path) = path { let real = realpath(&path).unwrap_or_default(); - let trimmed = - real.split(|c| c == '\r' || c == '\n').next().unwrap_or(""); + let trimmed = real.split(|c| c == '\r' || c == '\n').next().unwrap_or(""); io.write(&format!(" {}", trimmed)); } else { io.write(" null"); @@ -615,9 +611,7 @@ impl ShowCommand { ), ); io.write(&JsonFile::encode( - &PhpMixed::Array( - wrapper.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), - ), + &PhpMixed::Array(wrapper.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), 0, )?); } else { @@ -632,8 +626,7 @@ impl ShowCommand { let mut package_filter_regex: Option<String> = None; if let Some(ref pf) = package_filter { let escaped = shirabe_php_shim::preg_quote(pf, None); - package_filter_regex = - Some(format!("{{^{}$}}i", escaped.replace("\\*", ".*?"))); + package_filter_regex = Some(format!("{{^{}$}}i", escaped.replace("\\*", ".*?"))); } let mut package_list_filter: Option<Vec<String>> = None; @@ -642,7 +635,9 @@ impl ShowCommand { } if input.get_option("path").as_bool() == Some(true) && composer.is_none() { - io.write_error("No composer.json found in the current directory, disabling \"path\" option"); + io.write_error( + "No composer.json found in the current directory, disabling \"path\" option", + ); input.set_option("path", PhpMixed::Bool(false)); } @@ -652,11 +647,13 @@ impl ShowCommand { } else if let Some(ref lr) = locked_repo { if Self::same_repository_dyn(&*repo, &**lr) { "locked" - } else if Self::same_repository_dyn(&*repo, installed_repo.as_repository_interface()) - || installed_repo - .get_repositories() - .iter() - .any(|r| Self::same_repository_dyn(&*repo, &**r)) + } else if Self::same_repository_dyn( + &*repo, + installed_repo.as_repository_interface(), + ) || installed_repo + .get_repositories() + .iter() + .any(|r| Self::same_repository_dyn(&*repo, &**r)) { "installed" } else { @@ -688,11 +685,9 @@ impl ShowCommand { let need_replace = match existing { None => true, Some(PackageOrName::Name(_)) => true, - Some(PackageOrName::Pkg(existing)) => version_compare( - existing.get_version(), - package.get_version(), - "<", - ), + Some(PackageOrName::Pkg(existing)) => { + version_compare(existing.get_version(), package.get_version(), "<") + } }; if need_replace { let mut p: Box<dyn PackageInterface> = package.clone_box(); @@ -720,10 +715,7 @@ impl ShowCommand { packages .entry(type_owned.clone()) .or_insert_with(IndexMap::new) - .insert( - p.get_name().to_string(), - PackageOrName::Pkg(p), - ); + .insert(p.get_name().to_string(), PackageOrName::Pkg(p)); } } } @@ -782,10 +774,8 @@ impl ShowCommand { if show_latest && *show_version { for package_or_name in type_packages.values() { if let PackageOrName::Pkg(package) = package_or_name { - if !Preg::is_match( - &ignored_packages_regex, - package.get_pretty_name(), - )? { + if !Preg::is_match(&ignored_packages_regex, package.get_pretty_name())? + { let latest = self.find_latest_package( &**package, composer.as_ref().unwrap(), @@ -799,10 +789,8 @@ impl ShowCommand { continue; } - latest_packages.insert( - package.get_pretty_name().to_string(), - latest.unwrap(), - ); + latest_packages + .insert(package.get_pretty_name().to_string(), latest.unwrap()); } } } @@ -845,8 +833,7 @@ impl ShowCommand { // Determine if Composer is checking outdated dependencies and if current package should trigger non-default exit code let mut package_is_up_to_date = if let Some(latest) = latest_package { - latest.get_full_pretty_version() - == package.get_full_pretty_version() + latest.get_full_pretty_version() == package.get_full_pretty_version() && latest .as_complete_package_interface() .map_or(true, |c| !c.is_abandoned()) @@ -854,12 +841,10 @@ impl ShowCommand { false }; // When using --major-only, and no bigger version than current major is found then it is considered up to date - package_is_up_to_date = package_is_up_to_date - || (latest_package.is_none() && show_major_only); - let package_is_ignored = Preg::is_match( - &ignored_packages_regex, - package.get_pretty_name(), - )?; + package_is_up_to_date = + package_is_up_to_date || (latest_package.is_none() && show_major_only); + let package_is_ignored = + Preg::is_match(&ignored_packages_regex, package.get_pretty_name())?; if input.get_option("outdated").as_bool() == Some(true) && (package_is_up_to_date || package_is_ignored) { @@ -889,8 +874,7 @@ impl ShowCommand { true, )), ); - if format != "json" - || input.get_option("name-only").as_bool() != Some(true) + if format != "json" || input.get_option("name-only").as_bool() != Some(true) { package_view_data.insert( "homepage".to_string(), @@ -917,10 +901,8 @@ impl ShowCommand { version_str = version_str.trim_start_matches('v').to_string(); } version_length = version_length.max(version_str.len()); - package_view_data.insert( - "version".to_string(), - PhpMixed::String(version_str), - ); + package_view_data + .insert("version".to_string(), PhpMixed::String(version_str)); } if write_release_date { if let Some(release_date) = package.get_release_date() { @@ -958,10 +940,8 @@ impl ShowCommand { } let update_status = Self::get_update_status(&**latest, &**package); latest_length = latest_length.max(latest_version_str.len()); - package_view_data.insert( - "latest".to_string(), - PhpMixed::String(latest_version_str), - ); + package_view_data + .insert("latest".to_string(), PhpMixed::String(latest_version_str)); package_view_data.insert( "latest-status".to_string(), PhpMixed::String(update_status), @@ -1012,8 +992,7 @@ impl ShowCommand { PhpMixed::String(trimmed.to_string()), ); } else { - package_view_data - .insert("path".to_string(), PhpMixed::Null); + package_view_data.insert("path".to_string(), PhpMixed::Null); } } @@ -1045,8 +1024,7 @@ impl ShowCommand { } } - package_view_data - .insert("abandoned".to_string(), package_is_abandoned); + package_view_data.insert("abandoned".to_string(), package_is_abandoned); } else if let PackageOrName::Name(name) = package_or_name { package_view_data .insert("name".to_string(), PhpMixed::String(name.clone())); @@ -1093,7 +1071,10 @@ impl ShowCommand { } io.write(&JsonFile::encode( &PhpMixed::Array( - json_map.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), + json_map + .into_iter() + .map(|(k, v)| (k, Box::new(v))) + .collect(), ), 0, )?); @@ -1111,7 +1092,9 @@ impl ShowCommand { } else { io.write_error("<info>Color legend:</info>"); io.write_error("- <highlight>patch or minor</highlight> release available - update recommended"); - io.write_error("- <comment>major</comment> release available - update possible"); + io.write_error( + "- <comment>major</comment> release available - update possible", + ); if input.get_option("outdated").as_bool() != Some(true) { io.write_error("- <info>up to date</info> version"); } @@ -1134,18 +1117,11 @@ impl ShowCommand { let version_fits = name_length + version_length + 3 <= width; let latest_fits = name_length + version_length + latest_length + 3 <= width; - let release_date_fits = name_length - + version_length - + latest_length - + release_date_length - + 3 - <= width; - let description_fits = name_length - + version_length - + latest_length - + release_date_length - + 24 - <= width; + let release_date_fits = + name_length + version_length + latest_length + release_date_length + 3 <= width; + let description_fits = + name_length + version_length + latest_length + release_date_length + 24 + <= width; if latest_fits && !io.is_decorated() { latest_length += 2; @@ -1195,7 +1171,9 @@ impl ShowCommand { io.write_error("Everything up to date"); } io.write_error(""); - io.write_error("<info>Transitive dependencies not required in composer.json:</>"); + io.write_error( + "<info>Transitive dependencies not required in composer.json:</>", + ); if !transitive_deps.is_empty() { self.print_packages( io, @@ -1259,8 +1237,7 @@ impl ShowCommand { write_release_date: bool, release_date_length: usize, ) { - let pad_name = - write_version || write_latest || write_release_date || write_description; + let pad_name = write_version || write_latest || write_release_date || write_description; let pad_version = write_latest || write_release_date || write_description; let pad_latest = write_description || write_release_date; let pad_release_date = write_description; @@ -1298,21 +1275,12 @@ impl ShowCommand { )); } else { let width_pad = if pad_name { name_length } else { 0 }; - io.write_no_newline(&format!( - "{}{:<width$}", - indent, - name, - width = width_pad - )); + io.write_no_newline(&format!("{}{:<width$}", indent, name, width = width_pad)); } if let Some(version) = package.get("version").and_then(|v| v.as_string()) { if write_version { let width_pad = if pad_version { version_length } else { 0 }; - io.write_no_newline(&format!( - " {:<width$}", - version, - width = width_pad - )); + io.write_no_newline(&format!(" {:<width$}", version, width = width_pad)); } } if let (Some(latest_version), Some(update_status)) = ( @@ -1338,19 +1306,13 @@ impl ShowCommand { width = width_pad )); if write_release_date { - if let Some(age) = - package.get("release-age").and_then(|v| v.as_string()) - { + if let Some(age) = package.get("release-age").and_then(|v| v.as_string()) { let width_pad = if pad_release_date { release_date_length } else { 0 }; - io.write_no_newline(&format!( - " {:<width$}", - age, - width = width_pad - )); + io.write_no_newline(&format!(" {:<width$}", age, width = width_pad)); } } } @@ -1440,11 +1402,8 @@ impl ShowCommand { latest_package: &dyn PackageInterface, package: &dyn PackageInterface, ) -> String { - Self::update_status_to_version_style(&Self::get_update_status( - latest_package, - package, - )) - .to_string() + Self::update_status_to_version_style(&Self::get_update_status(latest_package, package)) + .to_string() } /// finds a package by name and version if provided @@ -1454,8 +1413,10 @@ impl ShowCommand { repos: &dyn RepositoryInterface, name: &str, version: PhpMixed, - ) -> anyhow::Result<(Option<Box<dyn CompletePackageInterface>>, IndexMap<String, String>)> - { + ) -> anyhow::Result<( + Option<Box<dyn CompletePackageInterface>>, + IndexMap<String, String>, + )> { let name = strtolower(name); let constraint: Option<Box<dyn ConstraintInterface>> = match &version { PhpMixed::String(s) => Some(self.version_parser.parse_constraints(s)?), @@ -1562,7 +1523,10 @@ impl ShowCommand { && installed_repo.has_package(package.as_package_interface()); let io = self.inner.get_io(); - io.write(&format!("<info>name</info> : {}", package.get_pretty_name())); + io.write(&format!( + "<info>name</info> : {}", + package.get_pretty_name() + )); io.write(&format!( "<info>descrip.</info> : {}", package.get_description() @@ -1603,7 +1567,10 @@ impl ShowCommand { } else { package.as_package_interface() }; - io.write(&format!("<info>type</info> : {}", package.get_type_field())); + io.write(&format!( + "<info>type</info> : {}", + package.get_type_field() + )); self.print_licenses(package); io.write(&format!( "<info>homepage</info> : {}", @@ -1643,10 +1610,7 @@ impl ShowCommand { if let Some(c) = latest.as_complete_package_interface() { if c.is_abandoned() { let replacement = match c.get_replacement_package() { - Some(rp) => format!( - " The author suggests using the {} package instead.", - rp - ), + Some(rp) => format!(" The author suggests using the {} package instead.", rp), None => String::new(), }; @@ -1726,8 +1690,7 @@ impl ShowCommand { .collect(); if let Some(found) = array_search(&installed_version, &key_map) { if let Some(idx) = versions_keys.iter().position(|v| v == &found) { - versions_keys[idx] = - format!("<info>* {}</info>", installed_version); + versions_keys[idx] = format!("<info>* {}</info>", installed_version); } } } @@ -1783,10 +1746,7 @@ impl ShowCommand { license.fullname, license_id, license.url ) } else { - format!( - "{} ({}) {}", - license.fullname, license_id, license.url - ) + format!("{} ({}) {}", license.fullname, license_id, license.url) } } }; @@ -1917,10 +1877,7 @@ impl ShowCommand { } if let Some(rd) = package.get_release_date() { - json.insert( - "released".to_string(), - PhpMixed::String(rd.to_rfc3339()), - ); + json.insert("released".to_string(), PhpMixed::String(rd.to_rfc3339())); } } @@ -1976,9 +1933,7 @@ impl ShowCommand { json = Self::append_links(json, package); self.inner.get_io().write(&JsonFile::encode( - &PhpMixed::Array( - json.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), - ), + &PhpMixed::Array(json.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), 0, )?); Ok(()) @@ -1988,8 +1943,10 @@ impl ShowCommand { mut json: IndexMap<String, PhpMixed>, versions: &IndexMap<String, String>, ) -> IndexMap<String, PhpMixed> { - let mut versions_pairs: Vec<(String, String)> = - versions.iter().map(|(k, v)| (k.clone(), v.clone())).collect(); + let mut versions_pairs: Vec<(String, String)> = versions + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect(); // uasort($versions, 'version_compare'); versions_pairs.sort_by(|a, b| { if version_compare(&a.1, &b.1, "<") { @@ -2032,9 +1989,7 @@ impl ShowCommand { m.insert("name".to_string(), PhpMixed::String(l.fullname)); m.insert("osi".to_string(), PhpMixed::String(license_id)); m.insert("url".to_string(), PhpMixed::String(l.url)); - PhpMixed::Array( - m.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), - ) + PhpMixed::Array(m.into_iter().map(|(k, v)| (k, Box::new(v))).collect()) } } }) @@ -2083,9 +2038,7 @@ impl ShowCommand { autoload.insert( r#type.clone(), - PhpMixed::Array( - psr.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), - ), + PhpMixed::Array(psr.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), ); } else if r#type == "classmap" { autoload.insert("classmap".to_string(), autoloads.clone()); @@ -2095,7 +2048,10 @@ impl ShowCommand { json.insert( "autoload".to_string(), PhpMixed::Array( - autoload.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), + autoload + .into_iter() + .map(|(k, v)| (k, Box::new(v))) + .collect(), ), ); } @@ -2155,10 +2111,7 @@ impl ShowCommand { } /// Display the tree - pub(crate) fn display_package_tree( - &self, - array_tree: Vec<IndexMap<String, PhpMixed>>, - ) { + pub(crate) fn display_package_tree(&self, array_tree: Vec<IndexMap<String, PhpMixed>>) { let io = self.inner.get_io(); for package in array_tree.iter() { let name = package @@ -2259,8 +2212,7 @@ impl ShowCommand { ]; let mut tree_child_desc: IndexMap<String, PhpMixed> = IndexMap::new(); - tree_child_desc - .insert("name".to_string(), PhpMixed::String(require_name.clone())); + tree_child_desc.insert("name".to_string(), PhpMixed::String(require_name.clone())); tree_child_desc.insert( "version".to_string(), PhpMixed::String(require.get_pretty_constraint().to_string()), @@ -2374,12 +2326,7 @@ impl ShowCommand { let circular_warn = if in_array( PhpMixed::String(require_name.clone()), - &PhpMixed::List( - current_tree - .iter() - .map(|v| Box::new(v.clone())) - .collect(), - ), + &PhpMixed::List(current_tree.iter().map(|v| Box::new(v.clone())).collect()), true, ) { "(circular dependency aborted here)" @@ -2425,10 +2372,7 @@ impl ShowCommand { let mut current_tree = packages_in_tree.to_vec(); let mut tree_child_desc: IndexMap<String, PhpMixed> = IndexMap::new(); - tree_child_desc.insert( - "name".to_string(), - PhpMixed::String(require_name.clone()), - ); + tree_child_desc.insert("name".to_string(), PhpMixed::String(require_name.clone())); tree_child_desc.insert( "version".to_string(), PhpMixed::String(require.get_pretty_constraint().to_string()), @@ -2436,9 +2380,7 @@ impl ShowCommand { if !in_array( PhpMixed::String(require_name.clone()), - &PhpMixed::List( - current_tree.iter().map(|v| Box::new(v.clone())).collect(), - ), + &PhpMixed::List(current_tree.iter().map(|v| Box::new(v.clone())).collect()), true, ) { current_tree.push(PhpMixed::String(require_name.clone())); @@ -2457,9 +2399,7 @@ impl ShowCommand { .into_iter() .map(|m| { Box::new(PhpMixed::Array( - m.into_iter() - .map(|(k, v)| (k, Box::new(v))) - .collect(), + m.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), )) }) .collect(), @@ -2595,9 +2535,12 @@ impl ShowCommand { } if patch_only { - let trimmed_version = - Preg::replace(r"{(\.0)+$}D", "", package.get_version())?; - let parts_needed = if trimmed_version.starts_with('0') { 4 } else { 3 }; + let trimmed_version = Preg::replace(r"{(\.0)+$}D", "", package.get_version())?; + let parts_needed = if trimmed_version.starts_with('0') { + 4 + } else { + 3 + }; let mut trimmed_version = trimmed_version; while trimmed_version.chars().filter(|&c| c == '.').count() + 1 < parts_needed { trimmed_version.push_str(".0"); @@ -2641,10 +2584,7 @@ impl ShowCommand { Ok(candidate) } - fn get_repository_set( - &mut self, - composer: &Composer, - ) -> anyhow::Result<&mut RepositorySet> { + fn get_repository_set(&mut self, composer: &Composer) -> anyhow::Result<&mut RepositorySet> { if self.repository_set.is_none() { let mut rs = RepositorySet::with_stability_and_flags( composer.get_package().get_minimum_stability(), diff --git a/crates/shirabe/src/command/status_command.rs b/crates/shirabe/src/command/status_command.rs index 341e1e4..ff6675d 100644 --- a/crates/shirabe/src/command/status_command.rs +++ b/crates/shirabe/src/command/status_command.rs @@ -49,13 +49,19 @@ impl StatusCommand { vec![], vec![], ); - composer.get_event_dispatcher().dispatch(command_event.get_name(), &command_event); + composer + .get_event_dispatcher() + .dispatch(command_event.get_name(), &command_event); - composer.get_event_dispatcher().dispatch_script(ScriptEvents::PRE_STATUS_CMD, true); + composer + .get_event_dispatcher() + .dispatch_script(ScriptEvents::PRE_STATUS_CMD, true); let exit_code = self.do_execute(input)?; - composer.get_event_dispatcher().dispatch_script(ScriptEvents::POST_STATUS_CMD, true); + composer + .get_event_dispatcher() + .dispatch_script(ScriptEvents::POST_STATUS_CMD, true); Ok(exit_code) } @@ -71,10 +77,13 @@ impl StatusCommand { let mut errors: IndexMap<String, String> = IndexMap::new(); let io = self.inner.get_io(); let mut unpushed_changes: IndexMap<String, String> = IndexMap::new(); - let mut vcs_version_changes: IndexMap<String, IndexMap<String, IndexMap<String, String>>> = IndexMap::new(); + let mut vcs_version_changes: IndexMap<String, IndexMap<String, IndexMap<String, String>>> = + IndexMap::new(); let parser = VersionParser::new(); - let process_executor = composer.get_loop().get_process_executor() + let process_executor = composer + .get_loop() + .get_process_executor() .cloned() .unwrap_or_else(|| ProcessExecutor::new(io)); let guesser = VersionGuesser::new(composer.get_config(), &process_executor, &parser, io); @@ -91,35 +100,57 @@ impl StatusCommand { // TODO(phase-b): isinstance checks using ChangeReportInterface/VcsCapableDownloaderInterface/DvcsDownloaderInterface if let Some(change_reporter) = downloader.as_change_report_interface() { if std::path::Path::new(&target_dir).is_symlink() { - errors.insert(target_dir.clone(), format!("{} is a symbolic link.", target_dir)); + errors.insert( + target_dir.clone(), + format!("{} is a symbolic link.", target_dir), + ); } - if let Some(changes) = change_reporter.get_local_changes(package.as_ref(), target_dir.clone()) { + if let Some(changes) = + change_reporter.get_local_changes(package.as_ref(), target_dir.clone()) + { errors.insert(target_dir.clone(), changes); } } if let Some(vcs_downloader) = downloader.as_vcs_capable_downloader_interface() { - if vcs_downloader.get_vcs_reference(package.as_ref(), target_dir.clone()).is_some() { + if vcs_downloader + .get_vcs_reference(package.as_ref(), target_dir.clone()) + .is_some() + { let previous_ref = match package.get_installation_source().as_deref() { Some("source") => package.get_source_reference().map(|s| s.to_string()), Some("dist") => package.get_dist_reference().map(|s| s.to_string()), _ => None, }; - let current_version = guesser.guess_version(&dumper.dump(package.as_ref()), &target_dir); + let current_version = + guesser.guess_version(&dumper.dump(package.as_ref()), &target_dir); if let (Some(prev_ref), Some(cur_version)) = (&previous_ref, ¤t_version) { if cur_version.get("commit").map(|s| s.as_str()) != Some(prev_ref.as_str()) - && cur_version.get("pretty_version").map(|s| s.as_str()) != Some(prev_ref.as_str()) + && cur_version.get("pretty_version").map(|s| s.as_str()) + != Some(prev_ref.as_str()) { let mut previous = IndexMap::new(); - previous.insert("version".to_string(), package.get_pretty_version().to_string()); + previous.insert( + "version".to_string(), + package.get_pretty_version().to_string(), + ); previous.insert("ref".to_string(), prev_ref.clone()); let mut current = IndexMap::new(); - current.insert("version".to_string(), cur_version.get("pretty_version").cloned().unwrap_or_default()); - current.insert("ref".to_string(), cur_version.get("commit").cloned().unwrap_or_default()); + current.insert( + "version".to_string(), + cur_version + .get("pretty_version") + .cloned() + .unwrap_or_default(), + ); + current.insert( + "ref".to_string(), + cur_version.get("commit").cloned().unwrap_or_default(), + ); let mut change = IndexMap::new(); change.insert("previous".to_string(), previous); @@ -132,7 +163,9 @@ impl StatusCommand { } if let Some(dvcs_downloader) = downloader.as_dvcs_downloader_interface() { - if let Some(unpushed) = dvcs_downloader.get_unpushed_changes(package.as_ref(), target_dir.clone()) { + if let Some(unpushed) = + dvcs_downloader.get_unpushed_changes(package.as_ref(), target_dir.clone()) + { unpushed_changes.insert(target_dir, unpushed); } } @@ -148,7 +181,8 @@ impl StatusCommand { for (path, changes) in &errors { if input.get_option("verbose").as_bool().unwrap_or(false) { - let indented_changes = changes.lines() + let indented_changes = changes + .lines() .map(|line| format!(" {}", line.trim_start())) .collect::<Vec<_>>() .join("\n"); @@ -165,7 +199,8 @@ impl StatusCommand { for (path, changes) in &unpushed_changes { if input.get_option("verbose").as_bool().unwrap_or(false) { - let indented_changes = changes.lines() + let indented_changes = changes + .lines() .map(|line| format!(" {}", line.trim_start())) .collect::<Vec<_>>() .join("\n"); @@ -178,24 +213,52 @@ impl StatusCommand { } if !vcs_version_changes.is_empty() { - io.write_error("<warning>You have version variations in the following dependencies:</warning>"); + io.write_error( + "<warning>You have version variations in the following dependencies:</warning>", + ); for (path, changes) in &vcs_version_changes { if input.get_option("verbose").as_bool().unwrap_or(false) { let current_version = { - let v = changes["current"].get("version").map(|s| s.as_str()).unwrap_or(""); - let r = changes["current"].get("ref").map(|s| s.as_str()).unwrap_or(""); - if v.is_empty() { r.to_string() } else { v.to_string() } + let v = changes["current"] + .get("version") + .map(|s| s.as_str()) + .unwrap_or(""); + let r = changes["current"] + .get("ref") + .map(|s| s.as_str()) + .unwrap_or(""); + if v.is_empty() { + r.to_string() + } else { + v.to_string() + } }; let previous_version = { - let v = changes["previous"].get("version").map(|s| s.as_str()).unwrap_or(""); - let r = changes["previous"].get("ref").map(|s| s.as_str()).unwrap_or(""); - if v.is_empty() { r.to_string() } else { v.to_string() } + let v = changes["previous"] + .get("version") + .map(|s| s.as_str()) + .unwrap_or(""); + let r = changes["previous"] + .get("ref") + .map(|s| s.as_str()) + .unwrap_or(""); + if v.is_empty() { + r.to_string() + } else { + v.to_string() + } }; let (current_display, previous_display) = if io.is_very_verbose() { - let cur_ref = changes["current"].get("ref").map(|s| s.as_str()).unwrap_or(""); - let prev_ref = changes["previous"].get("ref").map(|s| s.as_str()).unwrap_or(""); + let cur_ref = changes["current"] + .get("ref") + .map(|s| s.as_str()) + .unwrap_or(""); + let prev_ref = changes["previous"] + .get("ref") + .map(|s| s.as_str()) + .unwrap_or(""); ( format!("{} ({})", current_version, cur_ref), format!("{} ({})", previous_version, prev_ref), @@ -205,7 +268,10 @@ impl StatusCommand { }; io.write(&format!("<info>{}</info>:", path)); - io.write(&format!(" From <comment>{}</comment> to <comment>{}</comment>", previous_display, current_display)); + io.write(&format!( + " From <comment>{}</comment> to <comment>{}</comment>", + previous_display, current_display + )); } else { io.write(path); } @@ -218,9 +284,19 @@ impl StatusCommand { io.write_error("Use --verbose (-v) to see a list of files"); } - let exit_code = (if !errors.is_empty() { Self::EXIT_CODE_ERRORS } else { 0 }) - + (if !unpushed_changes.is_empty() { Self::EXIT_CODE_UNPUSHED_CHANGES } else { 0 }) - + (if !vcs_version_changes.is_empty() { Self::EXIT_CODE_VERSION_CHANGES } else { 0 }); + let exit_code = (if !errors.is_empty() { + Self::EXIT_CODE_ERRORS + } else { + 0 + }) + (if !unpushed_changes.is_empty() { + Self::EXIT_CODE_UNPUSHED_CHANGES + } else { + 0 + }) + (if !vcs_version_changes.is_empty() { + Self::EXIT_CODE_VERSION_CHANGES + } else { + 0 + }); Ok(exit_code) } diff --git a/crates/shirabe/src/command/suggests_command.rs b/crates/shirabe/src/command/suggests_command.rs index 5f0fa59..efa0ce2 100644 --- a/crates/shirabe/src/command/suggests_command.rs +++ b/crates/shirabe/src/command/suggests_command.rs @@ -1,6 +1,5 @@ //! ref: composer/src/Composer/Command/SuggestsCommand.php -use anyhow::Result; use crate::command::base_command::BaseCommand; use crate::command::completion_trait::CompletionTrait; use crate::console::input::input_argument::InputArgument; @@ -9,9 +8,10 @@ use crate::installer::suggested_packages_reporter::SuggestedPackagesReporter; use crate::repository::installed_repository::InstalledRepository; use crate::repository::platform_repository::PlatformRepository; use crate::repository::root_package_repository::RootPackageRepository; +use anyhow::Result; use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; -use shirabe_php_shim::{empty, in_array, PhpMixed}; +use shirabe_php_shim::{PhpMixed, empty, in_array}; #[derive(Debug)] pub struct SuggestsCommand { @@ -38,20 +38,34 @@ impl SuggestsCommand { ); } - pub fn execute(&mut self, input: &dyn InputInterface, _output: &dyn OutputInterface) -> Result<i64> { + pub fn execute( + &mut self, + input: &dyn InputInterface, + _output: &dyn OutputInterface, + ) -> Result<i64> { let composer = self.inner.require_composer()?; - let mut installed_repos = vec![ - Box::new(RootPackageRepository::new(composer.get_package().clone())), - ]; + let mut installed_repos = vec![Box::new(RootPackageRepository::new( + composer.get_package().clone(), + ))]; let locker = composer.get_locker(); if locker.is_locked() { - installed_repos.push(Box::new(PlatformRepository::new(vec![], locker.get_platform_overrides()))); - installed_repos.push(Box::new(locker.get_locked_repository(!input.get_option("no-dev").as_bool().unwrap_or(false)))); + installed_repos.push(Box::new(PlatformRepository::new( + vec![], + locker.get_platform_overrides(), + ))); + installed_repos.push(Box::new(locker.get_locked_repository( + !input.get_option("no-dev").as_bool().unwrap_or(false), + ))); } else { - installed_repos.push(Box::new(PlatformRepository::new(vec![], composer.get_config().get("platform")))); - installed_repos.push(Box::new(composer.get_repository_manager().get_local_repository())); + installed_repos.push(Box::new(PlatformRepository::new( + vec![], + composer.get_config().get("platform"), + ))); + installed_repos.push(Box::new( + composer.get_repository_manager().get_local_repository(), + )); } let installed_repo = InstalledRepository::new(installed_repos); @@ -61,7 +75,13 @@ impl SuggestsCommand { let mut packages = installed_repo.get_packages(); packages.push(composer.get_package()); for package in &packages { - if !empty(&filter) && !in_array(PhpMixed::String(package.get_name().to_string()), &filter, false) { + if !empty(&filter) + && !in_array( + PhpMixed::String(package.get_name().to_string()), + &filter, + false, + ) + { continue; } reporter.add_suggestions_from_package(package); diff --git a/crates/shirabe/src/command/update_command.rs b/crates/shirabe/src/command/update_command.rs index 14848fb..0311cec 100644 --- a/crates/shirabe/src/command/update_command.rs +++ b/crates/shirabe/src/command/update_command.rs @@ -7,8 +7,8 @@ use shirabe_external_packages::symfony::component::console::helper::table::Table use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; use shirabe_php_shim::{ - array_filter, array_intersect, array_keys, array_merge, array_search, count, empty, in_array, - sprintf, strtolower, InvalidArgumentException, PhpMixed, RuntimeException, + InvalidArgumentException, PhpMixed, RuntimeException, array_filter, array_intersect, + array_keys, array_merge, array_search, count, empty, in_array, sprintf, strtolower, }; use shirabe_semver::constraint::multi_constraint::MultiConstraint; use shirabe_semver::intervals::Intervals; @@ -20,9 +20,7 @@ use crate::command::completion_trait::CompletionTrait; use crate::composer::Composer; use crate::console::input::input_argument::InputArgument; use crate::console::input::input_option::InputOption; -use crate::dependency_resolver::request::{ - self, Request, UpdateAllowTransitiveDeps, -}; +use crate::dependency_resolver::request::{self, Request, UpdateAllowTransitiveDeps}; use crate::installer::Installer; use crate::io::io_interface::IOInterface; use crate::package::base_package::BasePackage; @@ -167,10 +165,10 @@ impl UpdateCommand { // extract --with shorthands from the allowlist if packages.len() > 0 { - let allowlist_packages_with_requirements: Vec<String> = array_filter( - &packages, - |pkg: &String| -> bool { Preg::is_match(r"{\S+[ =:]\S+}", pkg) }, - ); + let allowlist_packages_with_requirements: Vec<String> = + array_filter(&packages, |pkg: &String| -> bool { + Preg::is_match(r"{\S+[ =:]\S+}", pkg) + }); for (package, constraint) in self .inner .format_requirements(allowlist_packages_with_requirements.clone()) @@ -267,9 +265,15 @@ impl UpdateCommand { let Some(matches) = matches else { continue; }; - let constraint = parser.parse_constraints(&format!("~{}", matches.get(1).cloned().unwrap_or_default()))?; + let constraint = parser.parse_constraints(&format!( + "~{}", + matches.get(1).cloned().unwrap_or_default() + ))?; if temporary_constraints.contains_key(package.get_name()) { - let existing = temporary_constraints.get(package.get_name()).cloned().unwrap(); + let existing = temporary_constraints + .get(package.get_name()) + .cloned() + .unwrap(); temporary_constraints.insert( package.get_name().to_string(), // TODO(phase-b): MultiConstraint::create signature @@ -351,22 +355,38 @@ impl UpdateCommand { let mut install = Installer::create(io, &composer); let config = composer.get_config(); - let (prefer_source, prefer_dist) = - self.inner.get_preferred_install_options(config, input, false); + let (prefer_source, prefer_dist) = self + .inner + .get_preferred_install_options(config, input, false); - let optimize = input.get_option("optimize-autoloader").as_bool().unwrap_or(false) + let optimize = input + .get_option("optimize-autoloader") + .as_bool() + .unwrap_or(false) || config.get("optimize-autoloader").as_bool().unwrap_or(false); let authoritative = input .get_option("classmap-authoritative") .as_bool() .unwrap_or(false) - || config.get("classmap-authoritative").as_bool().unwrap_or(false); + || config + .get("classmap-authoritative") + .as_bool() + .unwrap_or(false); let apcu_prefix = input.get_option("apcu-autoloader-prefix"); let apcu = !matches!(apcu_prefix, PhpMixed::Null) - || input.get_option("apcu-autoloader").as_bool().unwrap_or(false) + || input + .get_option("apcu-autoloader") + .as_bool() + .unwrap_or(false) || config.get("apcu-autoloader").as_bool().unwrap_or(false); - let minimal_changes = input.get_option("minimal-changes").as_bool().unwrap_or(false) - || config.get("update-with-minimal-changes").as_bool().unwrap_or(false); + let minimal_changes = input + .get_option("minimal-changes") + .as_bool() + .unwrap_or(false) + || config + .get("update-with-minimal-changes") + .as_bool() + .unwrap_or(false); let mut update_allow_transitive_dependencies = UpdateAllowTransitiveDeps::UpdateOnlyListed; if input @@ -404,7 +424,10 @@ impl UpdateCommand { .set_prefer_stable(input.get_option("prefer-stable").as_bool().unwrap_or(false)) .set_prefer_lowest(input.get_option("prefer-lowest").as_bool().unwrap_or(false)) .set_temporary_constraints(temporary_constraints) - .set_audit_config(self.inner.create_audit_config(composer.get_config(), input)?) + .set_audit_config( + self.inner + .create_audit_config(composer.get_config(), input)?, + ) .set_minimal_update(minimal_changes); if input.get_option("no-plugins").as_bool().unwrap_or(false) { @@ -490,9 +513,15 @@ impl UpdateCommand { ); let mut autocompleter_values: IndexMap<String, String> = IndexMap::new(); let installed_packages = if composer.get_locker().is_locked() { - composer.get_locker().get_locked_repository(true)?.get_packages() + composer + .get_locker() + .get_locked_repository(true)? + .get_packages() } else { - composer.get_repository_manager().get_local_repository().get_packages() + composer + .get_repository_manager() + .get_local_repository() + .get_packages() }; let version_selector = self.create_version_selector(composer); for package in &installed_packages { @@ -502,7 +531,8 @@ impl UpdateCommand { } } let current_version = package.get_pretty_version(); - let constraint = todo!("requires[package.get_name()].get_pretty_constraint() if present"); + let constraint = + todo!("requires[package.get_name()].get_pretty_constraint() if present"); let stability = todo!( "if stabilityFlags[package_name] use array_search(BasePackage::STABILITIES) else minimum_stability" ); @@ -579,18 +609,16 @@ impl UpdateCommand { fn create_version_selector(&self, composer: &Composer) -> VersionSelector { let mut repository_set = RepositorySet::new(); - repository_set.add_repository(Box::new(CompositeRepository::new( - array_filter( - &composer.get_repository_manager().get_repositories(), - |repository: &Box<dyn RepositoryInterface>| -> bool { - // PHP: !$repository instanceof PlatformRepository - repository - .as_any() - .downcast_ref::<PlatformRepository>() - .is_none() - }, - ), - ))); + repository_set.add_repository(Box::new(CompositeRepository::new(array_filter( + &composer.get_repository_manager().get_repositories(), + |repository: &Box<dyn RepositoryInterface>| -> bool { + // PHP: !$repository instanceof PlatformRepository + repository + .as_any() + .downcast_ref::<PlatformRepository>() + .is_none() + }, + )))); VersionSelector::new(repository_set) } diff --git a/crates/shirabe/src/command/validate_command.rs b/crates/shirabe/src/command/validate_command.rs index d2f726a..e95e5ca 100644 --- a/crates/shirabe/src/command/validate_command.rs +++ b/crates/shirabe/src/command/validate_command.rs @@ -26,14 +26,69 @@ impl ValidateCommand { .set_name("validate") .set_description("Validates a composer.json and composer.lock") .set_definition(vec![ - InputOption::new("no-check-all", None, Some(InputOption::VALUE_NONE), "Do not validate requires for overly strict/loose constraints", None, vec![]), - InputOption::new("check-lock", None, Some(InputOption::VALUE_NONE), "Check if lock file is up to date (even when config.lock is false)", None, vec![]), - InputOption::new("no-check-lock", None, Some(InputOption::VALUE_NONE), "Do not check if lock file is up to date", None, vec![]), - InputOption::new("no-check-publish", None, Some(InputOption::VALUE_NONE), "Do not check for publish errors", None, vec![]), - InputOption::new("no-check-version", None, Some(InputOption::VALUE_NONE), "Do not report a warning if the version field is present", None, vec![]), - InputOption::new("with-dependencies", Some(shirabe_php_shim::PhpMixed::String("A".to_string())), Some(InputOption::VALUE_NONE), "Also validate the composer.json of all installed dependencies", None, vec![]), - InputOption::new("strict", None, Some(InputOption::VALUE_NONE), "Return a non-zero exit code for warnings as well as errors", None, vec![]), - InputArgument::new("file", Some(InputArgument::OPTIONAL), "path to composer.json file", None, vec![]), + InputOption::new( + "no-check-all", + None, + Some(InputOption::VALUE_NONE), + "Do not validate requires for overly strict/loose constraints", + None, + vec![], + ), + InputOption::new( + "check-lock", + None, + Some(InputOption::VALUE_NONE), + "Check if lock file is up to date (even when config.lock is false)", + None, + vec![], + ), + InputOption::new( + "no-check-lock", + None, + Some(InputOption::VALUE_NONE), + "Do not check if lock file is up to date", + None, + vec![], + ), + InputOption::new( + "no-check-publish", + None, + Some(InputOption::VALUE_NONE), + "Do not check for publish errors", + None, + vec![], + ), + InputOption::new( + "no-check-version", + None, + Some(InputOption::VALUE_NONE), + "Do not report a warning if the version field is present", + None, + vec![], + ), + InputOption::new( + "with-dependencies", + Some(shirabe_php_shim::PhpMixed::String("A".to_string())), + Some(InputOption::VALUE_NONE), + "Also validate the composer.json of all installed dependencies", + None, + vec![], + ), + InputOption::new( + "strict", + None, + Some(InputOption::VALUE_NONE), + "Return a non-zero exit code for warnings as well as errors", + None, + vec![], + ), + InputArgument::new( + "file", + Some(InputArgument::OPTIONAL), + "path to composer.json file", + None, + vec![], + ), ]) .set_help( "The validate command validates a given composer.json and composer.lock\n\n\ @@ -41,12 +96,14 @@ impl ValidateCommand { 1 validation warning(s), only when --strict is given\n\ 2 validation error(s)\n\ 3 file unreadable or missing\n\n\ - Read more at https://getcomposer.org/doc/03-cli.md#validate" + Read more at https://getcomposer.org/doc/03-cli.md#validate", ); } pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> { - let file = input.get_argument("file").as_string_opt() + let file = input + .get_argument("file") + .as_string_opt() .map(|s| s.to_string()) .unwrap_or_else(|| Factory::get_composer_file()); let io = self.inner.get_io(); @@ -66,19 +123,29 @@ impl ValidateCommand { } else { ValidatingArrayLoader::CHECK_ALL }; - let check_publish = !input.get_option("no-check-publish").as_bool().unwrap_or(false); + let check_publish = !input + .get_option("no-check-publish") + .as_bool() + .unwrap_or(false); let check_lock = !input.get_option("no-check-lock").as_bool().unwrap_or(false); - let check_version = if input.get_option("no-check-version").as_bool().unwrap_or(false) { + let check_version = if input + .get_option("no-check-version") + .as_bool() + .unwrap_or(false) + { 0 } else { ConfigValidator::CHECK_VERSION }; let is_strict = input.get_option("strict").as_bool().unwrap_or(false); - let (mut errors, mut publish_errors, mut warnings) = validator.validate(&file, check_all, check_version)?; + let (mut errors, mut publish_errors, mut warnings) = + validator.validate(&file, check_all, check_version)?; let mut lock_errors: Vec<String> = vec![]; let composer = self.inner.create_composer_instance(input, io, vec![])?; - let check_lock = (check_lock && composer.get_config().get("lock").as_bool().unwrap_or(true)) || input.get_option("check-lock").as_bool().unwrap_or(false); + let check_lock = (check_lock + && composer.get_config().get("lock").as_bool().unwrap_or(true)) + || input.get_option("check-lock").as_bool().unwrap_or(false); let locker = composer.get_locker(); if locker.is_locked() && !locker.is_fresh() { lock_errors.push("- The lock file is not up to date with the latest changes in composer.json, it is recommended that you run `composer update` or `composer update <package name>`.".to_string()); @@ -88,7 +155,17 @@ impl ValidateCommand { lock_errors.extend(locker.get_missing_requirement_info(composer.get_package(), true)); } - self.output_result(io, &file, &mut errors, &mut warnings, check_publish, &mut publish_errors, check_lock, &mut lock_errors, true); + self.output_result( + io, + &file, + &mut errors, + &mut warnings, + check_publish, + &mut publish_errors, + check_lock, + &mut lock_errors, + true, + ); let exit_code = if !errors.is_empty() { 2 @@ -100,19 +177,37 @@ impl ValidateCommand { let mut exit_code = exit_code; - if input.get_option("with-dependencies").as_bool().unwrap_or(false) { + if input + .get_option("with-dependencies") + .as_bool() + .unwrap_or(false) + { let local_repo = composer.get_repository_manager().get_local_repository(); for package in local_repo.get_packages() { - let path = composer.get_installation_manager().get_install_path(package.as_ref()); + let path = composer + .get_installation_manager() + .get_install_path(package.as_ref()); let path = match path { Some(p) => p, None => continue, }; let dep_file = format!("{}/composer.json", path); - if std::path::Path::new(&path).is_dir() && std::path::Path::new(&dep_file).exists() { - let (mut dep_errors, mut dep_publish_errors, mut dep_warnings) = validator.validate(&dep_file, check_all, check_version)?; + if std::path::Path::new(&path).is_dir() && std::path::Path::new(&dep_file).exists() + { + let (mut dep_errors, mut dep_publish_errors, mut dep_warnings) = + validator.validate(&dep_file, check_all, check_version)?; - self.output_result(io, package.get_pretty_name(), &mut dep_errors, &mut dep_warnings, check_publish, &mut dep_publish_errors, false, &mut vec![], false); + self.output_result( + io, + package.get_pretty_name(), + &mut dep_errors, + &mut dep_warnings, + check_publish, + &mut dep_publish_errors, + false, + &mut vec![], + false, + ); let dep_code = if !dep_errors.is_empty() { 2 @@ -135,7 +230,9 @@ impl ValidateCommand { vec![], vec![], ); - let event_code = composer.get_event_dispatcher().dispatch(command_event.get_name(), &command_event); + let event_code = composer + .get_event_dispatcher() + .dispatch(command_event.get_name(), &command_event); Ok(exit_code.max(event_code)) } @@ -155,16 +252,31 @@ impl ValidateCommand { let mut do_print_schema_url = false; if !errors.is_empty() { - io.write_error(&format!("<error>{} is invalid, the following errors/warnings were found:</error>", name)); + io.write_error(&format!( + "<error>{} is invalid, the following errors/warnings were found:</error>", + name + )); } else if !publish_errors.is_empty() && check_publish { - io.write_error(&format!("<info>{} is valid for simple usage with Composer but has</info>", name)); - io.write_error("<info>strict errors that make it unable to be published as a package</info>"); + io.write_error(&format!( + "<info>{} is valid for simple usage with Composer but has</info>", + name + )); + io.write_error( + "<info>strict errors that make it unable to be published as a package</info>", + ); do_print_schema_url = print_schema_url; } else if !warnings.is_empty() { - io.write_error(&format!("<info>{} is valid, but with a few warnings</info>", name)); + io.write_error(&format!( + "<info>{} is valid, but with a few warnings</info>", + name + )); do_print_schema_url = print_schema_url; } else if !lock_errors.is_empty() { - io.write(&format!("<info>{} is valid but your composer.lock has some {}</info>", name, if check_lock { "errors" } else { "warnings" })); + io.write(&format!( + "<info>{} is valid but your composer.lock has some {}</info>", + name, + if check_lock { "errors" } else { "warnings" } + )); } else { io.write(&format!("<info>{} is valid</info>", name)); } |
