diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-17 02:53:53 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-17 02:53:53 +0900 |
| commit | a1c7e6908a26e10f6e1f23a51721664b5e2d838d (patch) | |
| tree | c575c76f1b43359ed74913da4c6a2636643f1ba0 /crates/shirabe | |
| parent | 7f606f36fef0c0467c3c0db3d0da33af486dae8a (diff) | |
| download | php-shirabe-a1c7e6908a26e10f6e1f23a51721664b5e2d838d.tar.gz php-shirabe-a1c7e6908a26e10f6e1f23a51721664b5e2d838d.tar.zst php-shirabe-a1c7e6908a26e10f6e1f23a51721664b5e2d838d.zip | |
chore(style): cargo fmt
Diffstat (limited to 'crates/shirabe')
255 files changed, 11156 insertions, 5635 deletions
diff --git a/crates/shirabe/src/advisory/audit_config.rs b/crates/shirabe/src/advisory/audit_config.rs index da01a2c..f75d499 100644 --- a/crates/shirabe/src/advisory/audit_config.rs +++ b/crates/shirabe/src/advisory/audit_config.rs @@ -59,7 +59,10 @@ impl AuditConfig { /// Detailed format: ['CVE-123' => ['apply' => 'audit|block|all', 'reason' => '...']] fn parse_ignore_with_apply( config: &PhpMixed, - ) -> anyhow::Result<(IndexMap<String, Option<String>>, IndexMap<String, Option<String>>)> { + ) -> anyhow::Result<( + IndexMap<String, Option<String>>, + IndexMap<String, Option<String>>, + )> { let mut for_audit: IndexMap<String, Option<String>> = IndexMap::new(); let mut for_block: IndexMap<String, Option<String>> = IndexMap::new(); @@ -83,11 +86,13 @@ impl AuditConfig { (key.clone(), "all".to_string(), Some(reason_str.clone())) } PhpMixed::Array(detail) => { - let apply = detail.get("apply") + let apply = detail + .get("apply") .and_then(|v| v.as_string()) .unwrap_or("all") .to_string(); - let reason = detail.get("reason") + let reason = detail + .get("reason") .and_then(|v| v.as_string()) .map(|s| s.to_string()); diff --git a/crates/shirabe/src/advisory/auditor.rs b/crates/shirabe/src/advisory/auditor.rs index d96a474..37c86d9 100644 --- a/crates/shirabe/src/advisory/auditor.rs +++ b/crates/shirabe/src/advisory/auditor.rs @@ -5,8 +5,8 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter; use shirabe_php_shim::{ - array_all, array_any, array_key_exists, array_keys, array_reduce, get_class, is_string, - sprintf, str_starts_with, InvalidArgumentException, PhpMixed, DATE_ATOM, + DATE_ATOM, InvalidArgumentException, PhpMixed, array_all, array_any, array_key_exists, + array_keys, array_reduce, get_class, is_string, sprintf, str_starts_with, }; use crate::advisory::ignored_security_advisory::IgnoredSecurityAdvisory; @@ -96,16 +96,12 @@ impl Auditor { && self.needs_complete_advisory_load(&all_advisories, &ignore_list) { // TODO(phase-b): $packages reused here; see note above - let result = repo_set.get_matching_security_advisories( - vec![], - false, - ignore_unreachable, - )?; + let result = + repo_set.get_matching_security_advisories(vec![], false, ignore_unreachable)?; all_advisories = result.advisories; unreachable_repos.extend(result.unreachable_repos); } - let processed = - self.process_advisories(all_advisories, &ignore_list, &ignored_severities); + let processed = self.process_advisories(all_advisories, &ignore_list, &ignored_severities); let advisories = processed.advisories; let ignored_advisories = processed.ignored_advisories; @@ -175,9 +171,7 @@ impl Auditor { io.write( PhpMixed::String(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()), shirabe_php_shim::JSON_UNESCAPED_SLASHES | shirabe_php_shim::JSON_PRETTY_PRINT | shirabe_php_shim::JSON_UNESCAPED_UNICODE, @@ -210,10 +204,13 @@ impl Auditor { ), ]; for (advisories_to_output, message) in passes { - let (pkg_count, total_advisory_count) = - self.count_advisories(advisories_to_output); + let (pkg_count, total_advisory_count) = self.count_advisories(advisories_to_output); if pkg_count > 0 { - let plurality = if total_advisory_count == 1 { "y" } else { "ies" }; + let plurality = if total_advisory_count == 1 { + "y" + } else { + "ies" + }; let pkg_plurality = if pkg_count == 1 { "" } else { "s" }; let punctuation = if format == "summary" { "." } else { ":" }; io.write_error( @@ -290,8 +287,7 @@ impl Auditor { } // no partial advisories present - let advisories_values: Vec<&Vec<PartialSecurityAdvisory>> = - advisories.values().collect(); + let advisories_values: Vec<&Vec<PartialSecurityAdvisory>> = advisories.values().collect(); if array_all( &advisories_values, |pkg_advisories: &&Vec<PartialSecurityAdvisory>| { @@ -382,12 +378,12 @@ impl Auditor { // only holds PartialSecurityAdvisory let advisory_as_full: Option<&SecurityAdvisory> = None; if let Some(full) = advisory_as_full { - if is_string(&PhpMixed::String( - full.severity.clone().unwrap_or_default(), - )) && array_key_exists( - full.severity.as_deref().unwrap_or(""), - ignored_severities, - ) { + if is_string(&PhpMixed::String(full.severity.clone().unwrap_or_default())) + && array_key_exists( + full.severity.as_deref().unwrap_or(""), + ignored_severities, + ) + { is_active = false; let sev = full.severity.as_deref().unwrap_or(""); ignore_reason = ignored_severities @@ -397,10 +393,7 @@ impl Auditor { } if is_string(&PhpMixed::String(full.cve.clone().unwrap_or_default())) - && array_key_exists( - full.cve.as_deref().unwrap_or(""), - ignore_list, - ) + && array_key_exists(full.cve.as_deref().unwrap_or(""), ignore_list) { is_active = false; ignore_reason = ignore_list @@ -413,8 +406,7 @@ impl Auditor { let remote_id = source.get("remoteId").cloned().unwrap_or_default(); if array_key_exists(&remote_id, ignore_list) { is_active = false; - ignore_reason = - ignore_list.get(&remote_id).cloned().unwrap_or(None); + ignore_reason = ignore_list.get(&remote_id).cloned().unwrap_or(None); break; } } @@ -584,7 +576,9 @@ impl Auditor { error.push(format!("URL: {}", /* self.get_url(advisory) */ "")); error.push(format!( "Affected versions: {}", - OutputFormatter::escape(/* advisory.affectedVersions.getPrettyString() */ "") + OutputFormatter::escape( + /* advisory.affectedVersions.getPrettyString() */ "" + ) )); error.push(format!( "Reported at: {}", diff --git a/crates/shirabe/src/advisory/ignored_security_advisory.rs b/crates/shirabe/src/advisory/ignored_security_advisory.rs index b260644..7ed3a4c 100644 --- a/crates/shirabe/src/advisory/ignored_security_advisory.rs +++ b/crates/shirabe/src/advisory/ignored_security_advisory.rs @@ -1,10 +1,10 @@ //! ref: composer/src/Composer/Advisory/IgnoredSecurityAdvisory.php +use crate::advisory::security_advisory::SecurityAdvisory; use chrono::{DateTime, Utc}; use indexmap::IndexMap; use shirabe_php_shim::PhpMixed; use shirabe_semver::constraint::constraint_interface::ConstraintInterface; -use crate::advisory::security_advisory::SecurityAdvisory; #[derive(Debug)] pub struct IgnoredSecurityAdvisory { @@ -25,7 +25,17 @@ impl IgnoredSecurityAdvisory { ignore_reason: Option<String>, severity: Option<String>, ) -> Self { - let inner = SecurityAdvisory::new(package_name, advisory_id, affected_versions, title, sources, reported_at, cve, link, severity); + let inner = SecurityAdvisory::new( + package_name, + advisory_id, + affected_versions, + title, + sources, + reported_at, + cve, + link, + severity, + ); Self { inner, ignore_reason, diff --git a/crates/shirabe/src/advisory/mod.rs b/crates/shirabe/src/advisory/mod.rs new file mode 100644 index 0000000..783e9b7 --- /dev/null +++ b/crates/shirabe/src/advisory/mod.rs @@ -0,0 +1,5 @@ +pub mod audit_config; +pub mod auditor; +pub mod ignored_security_advisory; +pub mod partial_security_advisory; +pub mod security_advisory; diff --git a/crates/shirabe/src/advisory/partial_security_advisory.rs b/crates/shirabe/src/advisory/partial_security_advisory.rs index cd64dc8..e7aa96e 100644 --- a/crates/shirabe/src/advisory/partial_security_advisory.rs +++ b/crates/shirabe/src/advisory/partial_security_advisory.rs @@ -1,5 +1,6 @@ //! ref: composer/src/Composer/Advisory/PartialSecurityAdvisory.php +use crate::advisory::security_advisory::SecurityAdvisory; use anyhow::Result; use chrono::{DateTime, TimeZone, Utc}; use indexmap::IndexMap; @@ -8,7 +9,6 @@ use shirabe_php_shim::{PhpMixed, UnexpectedValueException}; use shirabe_semver::constraint::constraint::Constraint; use shirabe_semver::constraint::constraint_interface::ConstraintInterface; use shirabe_semver::version_parser::VersionParser; -use crate::advisory::security_advisory::SecurityAdvisory; fn serialize_constraint<S: serde::Serializer>( c: &Box<dyn ConstraintInterface>, @@ -34,16 +34,18 @@ impl PartialSecurityAdvisory { ) -> Result<Box<dyn std::any::Any>> { let affected_versions_str = data["affectedVersions"].as_string().unwrap_or(""); - let constraint: Box<dyn ConstraintInterface> = match parser.parse_constraints(affected_versions_str) { - Ok(c) => c, - Err(_) => { - let affected_version = Preg::replace(r"(^[>=<^~]*[\d.]+).*", "$1", affected_versions_str); - match parser.parse_constraints(&affected_version) { - Ok(c) => c, - Err(_) => Box::new(Constraint::new("==", "0.0.0-invalid-version")), + let constraint: Box<dyn ConstraintInterface> = + match parser.parse_constraints(affected_versions_str) { + Ok(c) => c, + Err(_) => { + let affected_version = + Preg::replace(r"(^[>=<^~]*[\d.]+).*", "$1", affected_versions_str); + match parser.parse_constraints(&affected_version) { + Ok(c) => c, + Err(_) => Box::new(Constraint::new("==", "0.0.0-invalid-version")), + } } - } - }; + }; let has_full_data = data.contains_key("title") && data.contains_key("sources") @@ -63,9 +65,15 @@ impl PartialSecurityAdvisory { data["title"].as_string().unwrap_or("").to_string(), data["sources"].clone(), reported_at, - data.get("cve").and_then(|v| v.as_string()).map(|s| s.to_string()), - data.get("link").and_then(|v| v.as_string()).map(|s| s.to_string()), - data.get("severity").and_then(|v| v.as_string()).map(|s| s.to_string()), + data.get("cve") + .and_then(|v| v.as_string()) + .map(|s| s.to_string()), + data.get("link") + .and_then(|v| v.as_string()) + .map(|s| s.to_string()), + data.get("severity") + .and_then(|v| v.as_string()) + .map(|s| s.to_string()), ); return Ok(Box::new(advisory)); } @@ -82,6 +90,10 @@ impl PartialSecurityAdvisory { advisory_id: String, affected_versions: Box<dyn ConstraintInterface>, ) -> Self { - Self { advisory_id, package_name, affected_versions } + Self { + advisory_id, + package_name, + affected_versions, + } } } diff --git a/crates/shirabe/src/autoload/autoload_generator.rs b/crates/shirabe/src/autoload/autoload_generator.rs index 023afd6..df8b16b 100644 --- a/crates/shirabe/src/autoload/autoload_generator.rs +++ b/crates/shirabe/src/autoload/autoload_generator.rs @@ -7,15 +7,16 @@ use shirabe_class_map_generator::class_map_generator::ClassMapGenerator; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::symfony::component::console::formatter::output_formatter::OutputFormatter; use shirabe_php_shim::{ - array_filter, array_keys, array_map, array_merge, array_merge_recursive, array_reverse, - array_shift, array_slice, array_unique, bin2hex, explode, file_exists, file_get_contents, - hash, implode, in_array, is_array, krsort, ksort, ltrim, preg_quote, random_bytes, realpath, - sprintf, str_replace, str_starts_with, str_contains, strlen, strpos, strtr, substr, - substr_count, trim, trigger_error, unlink, var_export, E_USER_DEPRECATED, - InvalidArgumentException, PhpMixed, RuntimeException, + E_USER_DEPRECATED, InvalidArgumentException, PhpMixed, RuntimeException, array_filter, + array_keys, array_map, array_merge, array_merge_recursive, array_reverse, array_shift, + array_slice, array_unique, bin2hex, explode, file_exists, file_get_contents, hash, implode, + in_array, is_array, krsort, ksort, ltrim, preg_quote, random_bytes, realpath, sprintf, + str_contains, str_replace, str_starts_with, strlen, strpos, strtr, substr, substr_count, + trigger_error, trim, unlink, var_export, }; use shirabe_semver::constraint::bound::Bound; +use crate::autoload::class_loader::ClassLoader; use crate::config::Config; use crate::event_dispatcher::event_dispatcher::EventDispatcher; use crate::filter::platform_requirement_filter::ignore_all_platform_requirement_filter::IgnoreAllPlatformRequirementFilter; @@ -34,7 +35,6 @@ use crate::script::script_events::ScriptEvents; use crate::util::filesystem::Filesystem; use crate::util::package_sorter::PackageSorter; use crate::util::platform::Platform; -use crate::autoload::class_loader::ClassLoader; #[derive(Debug)] pub struct AutoloadGenerator { @@ -104,7 +104,9 @@ impl AutoloadGenerator { E_USER_DEPRECATED, ); - self.set_platform_requirement_filter(PlatformRequirementFilterFactory::from_bool_or_list(ignore_platform_reqs)); + self.set_platform_requirement_filter(PlatformRequirementFilterFactory::from_bool_or_list( + ignore_platform_reqs, + )); } pub fn set_platform_requirement_filter( @@ -160,7 +162,11 @@ impl AutoloadGenerator { if shirabe_php_shim::server_get("COMPOSER_DEV_MODE").is_none() { Platform::put_env( "COMPOSER_DEV_MODE", - if self.dev_mode.unwrap_or(false) { "1" } else { "0" }, + if self.dev_mode.unwrap_or(false) { + "1" + } else { + "0" + }, ); } @@ -174,7 +180,8 @@ impl AutoloadGenerator { ); } - let mut class_map_generator = ClassMapGenerator::new(vec!["php".to_string(), "inc".to_string(), "hh".to_string()]); + let mut class_map_generator = + ClassMapGenerator::new(vec!["php".to_string(), "inc".to_string(), "hh".to_string()]); class_map_generator.avoid_duplicate_scans(); let filesystem = Filesystem::new(None); @@ -182,8 +189,15 @@ impl AutoloadGenerator { // Do not remove double realpath() calls. // Fixes failing Windows realpath() implementation. // See https://bugs.php.net/bug.php?id=72738 - let base_path = filesystem.normalize_path(&realpath(&realpath(&Platform::get_cwd()).unwrap_or_default()).unwrap_or_default()); - let vendor_path = filesystem.normalize_path(&realpath(&realpath(config.get("vendor-dir").as_string().unwrap_or("")).unwrap_or_default()).unwrap_or_default()); + let base_path = filesystem.normalize_path( + &realpath(&realpath(&Platform::get_cwd()).unwrap_or_default()).unwrap_or_default(), + ); + let vendor_path = filesystem.normalize_path( + &realpath( + &realpath(config.get("vendor-dir").as_string().unwrap_or("")).unwrap_or_default(), + ) + .unwrap_or_default(), + ); let use_global_include_path = config.get("use-include-path").as_bool().unwrap_or(false); let prepend_autoloader = if config.get("prepend-autoloader").as_bool() == Some(false) { "false" @@ -193,10 +207,21 @@ impl AutoloadGenerator { let target_dir = format!("{}/{}", vendor_path, target_dir); filesystem.ensure_directory_exists(&target_dir)?; - let vendor_path_code = filesystem.find_shortest_path_code(&realpath(&target_dir).unwrap_or_default(), &vendor_path, true, false); - let vendor_path_to_target_dir_code = filesystem.find_shortest_path_code(&vendor_path, &realpath(&target_dir).unwrap_or_default(), true, false); + let vendor_path_code = filesystem.find_shortest_path_code( + &realpath(&target_dir).unwrap_or_default(), + &vendor_path, + true, + false, + ); + let vendor_path_to_target_dir_code = filesystem.find_shortest_path_code( + &vendor_path, + &realpath(&target_dir).unwrap_or_default(), + true, + false, + ); - let app_base_dir_code = filesystem.find_shortest_path_code(&vendor_path, &base_path, true, false); + let app_base_dir_code = + filesystem.find_shortest_path_code(&vendor_path, &base_path, true, false); let app_base_dir_code = str_replace("__DIR__", "$vendorDir", &app_base_dir_code); let mut namespaces_file = format!( @@ -211,14 +236,23 @@ impl AutoloadGenerator { // Collect information from all packages. let dev_package_names = local_repo.get_dev_package_names(); - let package_map = self.build_package_map(installation_manager, root_package, local_repo.get_canonical_packages())?; + let package_map = self.build_package_map( + installation_manager, + root_package, + local_repo.get_canonical_packages(), + )?; let filtered_dev_packages: PhpMixed = if self.dev_mode.unwrap_or(false) { // if dev mode is enabled, then we do not filter any dev packages out so disable this entirely PhpMixed::Bool(false) } else { // if the list of dev package names is available we use that straight, otherwise pass true which means use legacy algo to figure them out if !dev_package_names.is_empty() { - PhpMixed::List(dev_package_names.iter().map(|s| Box::new(PhpMixed::String(s.clone()))).collect()) + PhpMixed::List( + dev_package_names + .iter() + .map(|s| Box::new(PhpMixed::String(s.clone()))) + .collect(), + ) } else { PhpMixed::Bool(true) } @@ -226,12 +260,20 @@ impl AutoloadGenerator { let autoloads = self.parse_autoloads(&package_map, root_package, filtered_dev_packages); // Process the 'psr-0' base directories. - let psr0_map = autoloads.get("psr-0").and_then(|v| v.as_array().cloned()).unwrap_or_default(); + let psr0_map = autoloads + .get("psr-0") + .and_then(|v| v.as_array().cloned()) + .unwrap_or_default(); for (namespace, paths) in &psr0_map { let mut exported_paths: Vec<String> = vec![]; if let Some(p_list) = paths.as_list() { for path in p_list { - exported_paths.push(self.get_path_code(&filesystem, &base_path, &vendor_path, path.as_string().unwrap_or(""))); + exported_paths.push(self.get_path_code( + &filesystem, + &base_path, + &vendor_path, + path.as_string().unwrap_or(""), + )); } } let exported_prefix = var_export(&PhpMixed::String(namespace.clone()), true); @@ -241,12 +283,20 @@ impl AutoloadGenerator { namespaces_file.push_str(");\n"); // Process the 'psr-4' base directories. - let psr4_map = autoloads.get("psr-4").and_then(|v| v.as_array().cloned()).unwrap_or_default(); + let psr4_map = autoloads + .get("psr-4") + .and_then(|v| v.as_array().cloned()) + .unwrap_or_default(); for (namespace, paths) in &psr4_map { let mut exported_paths: Vec<String> = vec![]; if let Some(p_list) = paths.as_list() { for path in p_list { - exported_paths.push(self.get_path_code(&filesystem, &base_path, &vendor_path, path.as_string().unwrap_or(""))); + exported_paths.push(self.get_path_code( + &filesystem, + &base_path, + &vendor_path, + path.as_string().unwrap_or(""), + )); } } let exported_prefix = var_export(&PhpMixed::String(namespace.clone()), true); @@ -261,8 +311,15 @@ impl AutoloadGenerator { if root_package.get_target_dir().is_some() && main_autoload.get("psr-0").map_or(false, |v| !v.is_empty()) { - let levels = substr_count(&filesystem.normalize_path(&root_package.get_target_dir().unwrap_or_default()), "/") + 1; - let psr0_keys = main_autoload.get("psr-0").and_then(|v| v.as_array()).cloned().unwrap_or_default(); + let levels = substr_count( + &filesystem.normalize_path(&root_package.get_target_dir().unwrap_or_default()), + "/", + ) + 1; + let psr0_keys = main_autoload + .get("psr-0") + .and_then(|v| v.as_array()) + .cloned() + .unwrap_or_default(); let prefixes = implode( ", ", &array_map( @@ -280,29 +337,52 @@ impl AutoloadGenerator { } let mut excluded: Vec<String> = vec![]; - if let Some(ex) = autoloads.get("exclude-from-classmap").and_then(|v| v.as_list()) { + if let Some(ex) = autoloads + .get("exclude-from-classmap") + .and_then(|v| v.as_list()) + { if !ex.is_empty() { - excluded = ex.iter().filter_map(|v| v.as_string().map(|s| s.to_string())).collect(); + excluded = ex + .iter() + .filter_map(|v| v.as_string().map(|s| s.to_string())) + .collect(); } } - let classmap_list = autoloads.get("classmap").and_then(|v| v.as_list()).cloned().unwrap_or_default(); + let classmap_list = autoloads + .get("classmap") + .and_then(|v| v.as_list()) + .cloned() + .unwrap_or_default(); for dir in &classmap_list { let dir_str = dir.as_string().unwrap_or(""); - class_map_generator.scan_paths(dir_str, self.build_exclusion_regex(dir_str, excluded.clone()), "classmap", ""); + class_map_generator.scan_paths( + dir_str, + self.build_exclusion_regex(dir_str, excluded.clone()), + "classmap", + "", + ); } if scan_psr_packages { - let mut namespaces_to_scan: IndexMap<String, Vec<IndexMap<String, PhpMixed>>> = IndexMap::new(); + let mut namespaces_to_scan: IndexMap<String, Vec<IndexMap<String, PhpMixed>>> = + IndexMap::new(); // Scan the PSR-0/4 directories for class files, and add them to the class map for psr_type in &["psr-4", "psr-0"] { - let map = autoloads.get(*psr_type).and_then(|v| v.as_array()).cloned().unwrap_or_default(); + let map = autoloads + .get(*psr_type) + .and_then(|v| v.as_array()) + .cloned() + .unwrap_or_default(); for (namespace, paths) in &map { let mut entry: IndexMap<String, PhpMixed> = IndexMap::new(); entry.insert("paths".to_string(), (**paths).clone()); entry.insert("type".to_string(), PhpMixed::String(psr_type.to_string())); - namespaces_to_scan.entry(namespace.clone()).or_insert_with(Vec::new).push(entry); + namespaces_to_scan + .entry(namespace.clone()) + .or_insert_with(Vec::new) + .push(entry); } } @@ -310,30 +390,48 @@ impl AutoloadGenerator { for (namespace, groups) in &namespaces_to_scan { for group in groups { - let paths = group.get("paths").and_then(|v| v.as_list()).cloned().unwrap_or_default(); - let group_type = group.get("type").and_then(|v| v.as_string()).unwrap_or("").to_string(); + let paths = group + .get("paths") + .and_then(|v| v.as_list()) + .cloned() + .unwrap_or_default(); + let group_type = group + .get("type") + .and_then(|v| v.as_string()) + .unwrap_or("") + .to_string(); for dir in &paths { let dir_str = dir.as_string().unwrap_or("").to_string(); - let dir_str = filesystem.normalize_path(if filesystem.is_absolute_path(&dir_str) { - &dir_str - } else { - &format!("{}/{}", base_path, dir_str) - }); + let dir_str = + filesystem.normalize_path(if filesystem.is_absolute_path(&dir_str) { + &dir_str + } else { + &format!("{}/{}", base_path, dir_str) + }); if !shirabe_php_shim::is_dir(&dir_str) { continue; } // if the vendor dir is contained within a psr-0/psr-4 dir being scanned we exclude it - let exclusion_regex = if str_contains(&vendor_path, &format!("{}/", dir_str)) { - self.build_exclusion_regex( - &dir_str, - array_merge(excluded.clone(), vec![format!("{}/", vendor_path)]), - ) - } else { - self.build_exclusion_regex(&dir_str, excluded.clone()) - }; + let exclusion_regex = + if str_contains(&vendor_path, &format!("{}/", dir_str)) { + self.build_exclusion_regex( + &dir_str, + array_merge( + excluded.clone(), + vec![format!("{}/", vendor_path)], + ), + ) + } else { + self.build_exclusion_regex(&dir_str, excluded.clone()) + }; - class_map_generator.scan_paths(&dir_str, exclusion_regex, &group_type, namespace); + class_map_generator.scan_paths( + &dir_str, + exclusion_regex, + &group_type, + namespace, + ); } } } @@ -376,7 +474,10 @@ impl AutoloadGenerator { self.io.write_error(&format!("<warning>{}</warning>", msg)); } - class_map.add_class("Composer\\InstalledVersions".to_string(), format!("{}/composer/InstalledVersions.php", vendor_path)); + class_map.add_class( + "Composer\\InstalledVersions".to_string(), + format!("{}/composer/InstalledVersions.php", vendor_path), + ); class_map.sort(); let mut classmap_file = format!( @@ -384,8 +485,15 @@ impl AutoloadGenerator { vendor_path_code, app_base_dir_code ); for (class_name, path) in class_map.get_map() { - let path_code = format!("{},\n", self.get_path_code(&filesystem, &base_path, &vendor_path, &path)); - classmap_file.push_str(&format!(" {} => {}", var_export(&PhpMixed::String(class_name.clone()), true), path_code)); + let path_code = format!( + "{},\n", + self.get_path_code(&filesystem, &base_path, &vendor_path, &path) + ); + classmap_file.push_str(&format!( + " {} => {}", + var_export(&PhpMixed::String(class_name.clone()), true), + path_code + )); } classmap_file.push_str(");\n"); @@ -394,13 +502,24 @@ impl AutoloadGenerator { suffix = None; } if suffix.is_none() { - suffix = config.get("autoloader-suffix").as_string().map(|s| s.to_string()); + suffix = config + .get("autoloader-suffix") + .as_string() + .map(|s| s.to_string()); // carry over existing autoload.php's suffix if possible and none is configured - if suffix.is_none() && Filesystem::is_readable(&format!("{}/autoload.php", vendor_path)) { - let content = file_get_contents(&format!("{}/autoload.php", vendor_path)).unwrap_or_default(); + if suffix.is_none() && Filesystem::is_readable(&format!("{}/autoload.php", vendor_path)) + { + let content = + file_get_contents(&format!("{}/autoload.php", vendor_path)).unwrap_or_default(); let mut matches: Vec<String> = vec![]; - if Preg::is_match("{ComposerAutoloaderInit([^:\\s]+)::}", &content, Some(&mut matches)).unwrap_or(false) { + if Preg::is_match( + "{ComposerAutoloaderInit([^:\\s]+)::}", + &content, + Some(&mut matches), + ) + .unwrap_or(false) + { suffix = matches.get(1).cloned(); } } @@ -408,7 +527,11 @@ impl AutoloadGenerator { if suffix.is_none() { suffix = Some(if let Some(l) = locker { if l.is_locked() { - l.get_lock_data().get("content-hash").and_then(|v| v.as_string()).unwrap_or("").to_string() + l.get_lock_data() + .get("content-hash") + .and_then(|v| v.as_string()) + .unwrap_or("") + .to_string() } else { bin2hex(&random_bytes(16)) } @@ -423,23 +546,50 @@ impl AutoloadGenerator { return Ok(class_map); } - filesystem.file_put_contents_if_modified(&format!("{}/autoload_namespaces.php", target_dir), &namespaces_file)?; - filesystem.file_put_contents_if_modified(&format!("{}/autoload_psr4.php", target_dir), &psr4_file)?; - filesystem.file_put_contents_if_modified(&format!("{}/autoload_classmap.php", target_dir), &classmap_file)?; + filesystem.file_put_contents_if_modified( + &format!("{}/autoload_namespaces.php", target_dir), + &namespaces_file, + )?; + filesystem.file_put_contents_if_modified( + &format!("{}/autoload_psr4.php", target_dir), + &psr4_file, + )?; + filesystem.file_put_contents_if_modified( + &format!("{}/autoload_classmap.php", target_dir), + &classmap_file, + )?; let include_path_file_path = format!("{}/include_paths.php", target_dir); - let include_path_file_contents = self.get_include_paths_file(&package_map, &filesystem, &base_path, &vendor_path, &vendor_path_code, &app_base_dir_code); + let include_path_file_contents = self.get_include_paths_file( + &package_map, + &filesystem, + &base_path, + &vendor_path, + &vendor_path_code, + &app_base_dir_code, + ); if let Some(ref c) = include_path_file_contents { filesystem.file_put_contents_if_modified(&include_path_file_path, c)?; } else if file_exists(&include_path_file_path) { unlink(&include_path_file_path); } let include_files_file_path = format!("{}/autoload_files.php", target_dir); - let files_map = autoloads.get("files").and_then(|v| v.as_array()).cloned().unwrap_or_default(); + let files_map = autoloads + .get("files") + .and_then(|v| v.as_array()) + .cloned() + .unwrap_or_default(); let mut files_str_map: IndexMap<String, String> = IndexMap::new(); for (k, v) in &files_map { files_str_map.insert(k.clone(), v.as_string().unwrap_or("").to_string()); } - let include_files_file_contents = self.get_include_files_file(&files_str_map, &filesystem, &base_path, &vendor_path, &vendor_path_code, &app_base_dir_code); + let include_files_file_contents = self.get_include_files_file( + &files_str_map, + &filesystem, + &base_path, + &vendor_path, + &vendor_path_code, + &app_base_dir_code, + ); if let Some(ref c) = include_files_file_contents { filesystem.file_put_contents_if_modified(&include_files_file_path, c)?; } else if file_exists(&include_files_file_path) { @@ -535,9 +685,14 @@ impl AutoloadGenerator { let abs_dir = if fs.is_absolute_path(dir) { dir.to_string() } else { - format!("{}/{}", realpath(&Platform::get_cwd()).unwrap_or_default(), dir) + format!( + "{}/{}", + realpath(&Platform::get_cwd()).unwrap_or_default(), + dir + ) }; - let dir_match_normalized = preg_quote(&strtr(&fs.normalize_path(&abs_dir), "\\", "/"), None); + let dir_match_normalized = + preg_quote(&strtr(&fs.normalize_path(&abs_dir), "\\", "/"), None); let is_symlink = dir_match != dir_match_normalized; let mut new_excluded: Vec<String> = vec![]; @@ -575,8 +730,10 @@ impl AutoloadGenerator { packages: Vec<Box<dyn PackageInterface>>, ) -> anyhow::Result<Vec<(Box<dyn PackageInterface>, Option<String>)>> { // build package => install path map - let mut package_map: Vec<(Box<dyn PackageInterface>, Option<String>)> = - vec![(root_package.clone_as_package_interface(), Some(String::new()))]; + let mut package_map: Vec<(Box<dyn PackageInterface>, Option<String>)> = vec![( + root_package.clone_as_package_interface(), + Some(String::new()), + )]; for package in packages { if package.as_alias_package().is_some() { @@ -593,7 +750,9 @@ impl AutoloadGenerator { /// Throws InvalidArgumentException if the package has illegal settings. pub(crate) fn validate_package(&self, package: &dyn PackageInterface) -> anyhow::Result<()> { let autoload = package.get_autoload(); - if autoload.get("psr-4").map_or(false, |v| !v.is_empty()) && package.get_target_dir().is_some() { + if autoload.get("psr-4").map_or(false, |v| !v.is_empty()) + && package.get_target_dir().is_some() + { let name = package.get_name(); let _ = package.get_target_dir(); return Err(InvalidArgumentException { @@ -628,11 +787,18 @@ impl AutoloadGenerator { let package_map = if is_array(&filtered_dev_packages) { let dev_list = filtered_dev_packages .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(); - array_filter(package_map, |item: &(Box<dyn PackageInterface>, Option<String>)| -> bool { - !in_array(item.0.get_name(), &dev_list, true) - }) + array_filter( + package_map, + |item: &(Box<dyn PackageInterface>, Option<String>)| -> bool { + !in_array(item.0.get_name(), &dev_list, true) + }, + ) } else if filtered_dev_packages.as_bool() == Some(true) { self.filter_package_map(package_map, root_package) } else { @@ -651,7 +817,8 @@ impl AutoloadGenerator { // sorted (i.e. dependents first) for files to ensure that dependencies are loaded/available once a file is included let files = self.parse_autoloads_type(&sorted_package_map, "files", root_package); // using sorted here but it does not really matter as all are excluded equally - let exclude = self.parse_autoloads_type(&sorted_package_map, "exclude-from-classmap", root_package); + let exclude = + self.parse_autoloads_type(&sorted_package_map, "exclude-from-classmap", root_package); krsort(&mut psr0); krsort(&mut psr4); @@ -661,12 +828,19 @@ impl AutoloadGenerator { result.insert("psr-4".to_string(), PhpMixed::Array(psr4)); result.insert("classmap".to_string(), PhpMixed::Array(classmap)); result.insert("files".to_string(), PhpMixed::Array(files)); - result.insert("exclude-from-classmap".to_string(), PhpMixed::Array(exclude)); + result.insert( + "exclude-from-classmap".to_string(), + PhpMixed::Array(exclude), + ); result } /// Registers an autoloader based on an autoload-map returned by parseAutoloads - pub fn create_loader(&self, autoloads: &IndexMap<String, PhpMixed>, vendor_dir: Option<String>) -> ClassLoader { + pub fn create_loader( + &self, + autoloads: &IndexMap<String, PhpMixed>, + vendor_dir: Option<String>, + ) -> ClassLoader { let mut loader = ClassLoader::new(vendor_dir); if let Some(psr0) = autoloads.get("psr-0").and_then(|v| v.as_array()) { @@ -683,22 +857,38 @@ impl AutoloadGenerator { if let Some(classmap) = autoloads.get("classmap").and_then(|v| v.as_list()) { let mut excluded: Vec<String> = vec![]; - if let Some(ex) = autoloads.get("exclude-from-classmap").and_then(|v| v.as_list()) { + if let Some(ex) = autoloads + .get("exclude-from-classmap") + .and_then(|v| v.as_list()) + { if !ex.is_empty() { - excluded = ex.iter().filter_map(|v| v.as_string().map(|s| s.to_string())).collect(); + excluded = ex + .iter() + .filter_map(|v| v.as_string().map(|s| s.to_string())) + .collect(); } } - let mut class_map_generator = ClassMapGenerator::new(vec!["php".to_string(), "inc".to_string(), "hh".to_string()]); + let mut class_map_generator = ClassMapGenerator::new(vec![ + "php".to_string(), + "inc".to_string(), + "hh".to_string(), + ]); class_map_generator.avoid_duplicate_scans(); for dir in classmap { let dir_str = dir.as_string().unwrap_or(""); let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - class_map_generator.scan_paths(dir_str, self.build_exclusion_regex(dir_str, excluded.clone()), "classmap", ""); + class_map_generator.scan_paths( + dir_str, + self.build_exclusion_regex(dir_str, excluded.clone()), + "classmap", + "", + ); })); if let Err(_e) = res { - self.io.write_error(&format!("<warning>{}</warning>", "scan failure")); + self.io + .write_error(&format!("<warning>{}</warning>", "scan failure")); } } @@ -732,7 +922,8 @@ impl AutoloadGenerator { if let Some(target_dir) = package.get_target_dir() { if !target_dir.is_empty() { let suffix_to_remove = format!("/{}", target_dir); - install_path = substr(&install_path, 0, Some(-(suffix_to_remove.len() as isize))); + install_path = + substr(&install_path, 0, Some(-(suffix_to_remove.len() as isize))); } } @@ -777,7 +968,10 @@ impl AutoloadGenerator { let mut files: IndexMap<String, String> = files .iter() .map(|(k, function_file)| { - (k.clone(), self.get_path_code(filesystem, base_path, vendor_path, function_file)) + ( + k.clone(), + self.get_path_code(filesystem, base_path, vendor_path, function_file), + ) }) .collect(); let unique_files: Vec<String> = array_unique(files.values().cloned().collect()); @@ -792,7 +986,8 @@ impl AutoloadGenerator { } } for duplicate_file in array_unique(duplicates) { - self.io.write_error(&format!("<warning> - {}</warning>", duplicate_file)); + self.io + .write_error(&format!("<warning> - {}</warning>", duplicate_file)); } } let _ = unique_files; @@ -839,7 +1034,8 @@ impl AutoloadGenerator { path = substr(&path, vendor_path.len() as isize, None); base_dir = "$vendorDir . ".to_string(); } else { - path = filesystem.normalize_path(&filesystem.find_shortest_path(base_path, &path, true)); + path = + filesystem.normalize_path(&filesystem.find_shortest_path(base_path, &path, true)); if !filesystem.is_absolute_path(&path) { base_dir = "$baseDir . ".to_string(); path = format!("/{}", path); @@ -862,7 +1058,10 @@ impl AutoloadGenerator { let mut lowest_php_version = Bound::zero(); let mut required_php_64bit = false; let mut required_extensions: IndexMap<String, String> = IndexMap::new(); - let mut extension_providers: IndexMap<String, Vec<Box<dyn shirabe_semver::constraint::constraint_interface::ConstraintInterface>>> = IndexMap::new(); + let mut extension_providers: IndexMap< + String, + Vec<Box<dyn shirabe_semver::constraint::constraint_interface::ConstraintInterface>>, + > = IndexMap::new(); for item in package_map { let package = &item.0; @@ -872,7 +1071,9 @@ impl AutoloadGenerator { } for (_k, link) in &links { let mut matches: Vec<String> = vec![]; - if Preg::is_match("{^ext-(.+)$}iD", link.get_target(), Some(&mut matches)).unwrap_or(false) { + if Preg::is_match("{^ext-(.+)$}iD", link.get_target(), Some(&mut matches)) + .unwrap_or(false) + { extension_providers .entry(matches[1].clone()) .or_insert_with(Vec::new) @@ -889,13 +1090,23 @@ impl AutoloadGenerator { } for (_k, link) in &package.get_requires() { - if self.platform_requirement_filter.is_ignored(link.get_target()) { + if self + .platform_requirement_filter + .is_ignored(link.get_target()) + { continue; } - if in_array(link.get_target(), &vec!["php".to_string(), "php-64bit".to_string()], true) { + if in_array( + link.get_target(), + &vec!["php".to_string(), "php-64bit".to_string()], + true, + ) { let constraint = link.get_constraint(); - if constraint.get_lower_bound().compare_to(&lowest_php_version, ">") { + if constraint + .get_lower_bound() + .compare_to(&lowest_php_version, ">") + { lowest_php_version = constraint.get_lower_bound(); } } @@ -906,7 +1117,8 @@ impl AutoloadGenerator { let mut matches: Vec<String> = vec![]; if check_platform.as_bool() == Some(true) - && Preg::is_match("{^ext-(.+)$}iD", link.get_target(), Some(&mut matches)).unwrap_or(false) + && Preg::is_match("{^ext-(.+)$}iD", link.get_target(), Some(&mut matches)) + .unwrap_or(false) { // skip extension checks if they have a valid provider/replacer if let Some(provided_list) = extension_providers.get(&matches[1]) { @@ -984,8 +1196,16 @@ impl AutoloadGenerator { let mut required_php = String::new(); let mut required_php_error = String::new(); if !lowest_php_version.is_zero() { - let operator = if lowest_php_version.is_inclusive() { ">=" } else { ">" }; - required_php = format!("PHP_VERSION_ID {} {}", operator, format_to_php_version_id(&lowest_php_version)); + let operator = if lowest_php_version.is_inclusive() { + ">=" + } else { + ">" + }; + required_php = format!( + "PHP_VERSION_ID {} {}", + operator, + format_to_php_version_id(&lowest_php_version) + ); let human_readable = format_to_human_readable(&lowest_php_version); required_php_error = format!( "\"{} {}\"", @@ -1009,7 +1229,10 @@ impl AutoloadGenerator { required_php.push_str("\nif (PHP_INT_SIZE !== 8) {\n $issues[] = 'Your Composer dependencies require a 64-bit build of PHP.';\n}\n"); } - let required_extensions_str = implode("", &required_extensions.values().cloned().collect::<Vec<_>>()); + let required_extensions_str = implode( + "", + &required_extensions.values().cloned().collect::<Vec<_>>(), + ); let required_extensions_block = if !required_extensions_str.is_empty() { format!( "\n$missingExtensions = array();\n{}\nif ($missingExtensions) {{\n $issues[] = 'Your Composer dependencies require the following PHP extensions to be installed: ' . implode(', ', $missingExtensions) . '.';\n}}\n", @@ -1029,7 +1252,11 @@ impl AutoloadGenerator { )) } - pub(crate) fn get_autoload_file(&self, vendor_path_to_target_dir_code: &str, suffix: &str) -> String { + pub(crate) fn get_autoload_file( + &self, + vendor_path_to_target_dir_code: &str, + suffix: &str, + ) -> String { let last_char = vendor_path_to_target_dir_code .chars() .nth(vendor_path_to_target_dir_code.len() - 1) @@ -1101,7 +1328,10 @@ impl AutoloadGenerator { }), true, ); - file.push_str(&format!(" $loader->setApcuPrefix({});\n", apcu_prefix)); + file.push_str(&format!( + " $loader->setApcuPrefix({});\n", + apcu_prefix + )); } if use_global_include_path { @@ -1165,7 +1395,8 @@ impl AutoloadGenerator { } } - let class_map = shirabe_php_shim::php_require(&format!("{}/autoload_classmap.php", target_dir)); + let class_map = + shirabe_php_shim::php_require(&format!("{}/autoload_classmap.php", target_dir)); if class_map.as_bool() != Some(false) && !class_map.is_null() { if let Some(cm) = class_map.as_array() { let cm_str: IndexMap<String, String> = cm @@ -1180,26 +1411,49 @@ impl AutoloadGenerator { let vendor_path_code = format!( " => {} . '/", - filesystem.find_shortest_path_code(&realpath(target_dir).unwrap_or_default(), vendor_path, true, true) + filesystem.find_shortest_path_code( + &realpath(target_dir).unwrap_or_default(), + vendor_path, + true, + true + ) ); let vendor_phar_path_code = format!( " => 'phar://' . {} . '/", - filesystem.find_shortest_path_code(&realpath(target_dir).unwrap_or_default(), vendor_path, true, true) + filesystem.find_shortest_path_code( + &realpath(target_dir).unwrap_or_default(), + vendor_path, + true, + true + ) ); let app_base_dir_code = format!( " => {} . '/", - filesystem.find_shortest_path_code(&realpath(target_dir).unwrap_or_default(), base_path, true, true) + filesystem.find_shortest_path_code( + &realpath(target_dir).unwrap_or_default(), + base_path, + true, + true + ) ); let app_base_dir_phar_code = format!( " => 'phar://' . {} . '/", - filesystem.find_shortest_path_code(&realpath(target_dir).unwrap_or_default(), base_path, true, true) + filesystem.find_shortest_path_code( + &realpath(target_dir).unwrap_or_default(), + base_path, + true, + true + ) ); // PHP: ' => ' . substr(var_export(rtrim($vendorDir, '\\/') . '/', true), 0, -1) let absolute_vendor_path_code = format!( " => {}", substr( - &var_export(&PhpMixed::String(format!("{}/", shirabe_php_shim::rtrim(vendor_path, "\\/"))), true), + &var_export( + &PhpMixed::String(format!("{}/", shirabe_php_shim::rtrim(vendor_path, "\\/"))), + true + ), 0, Some(-1), ) @@ -1207,7 +1461,13 @@ impl AutoloadGenerator { let absolute_vendor_phar_path_code = format!( " => {}", substr( - &var_export(&PhpMixed::String(format!("{}/", shirabe_php_shim::rtrim(&format!("phar://{}", vendor_path), "\\/"))), true), + &var_export( + &PhpMixed::String(format!( + "{}/", + shirabe_php_shim::rtrim(&format!("phar://{}", vendor_path), "\\/") + )), + true + ), 0, Some(-1), ) @@ -1215,7 +1475,10 @@ impl AutoloadGenerator { let absolute_app_base_dir_code = format!( " => {}", substr( - &var_export(&PhpMixed::String(format!("{}/", shirabe_php_shim::rtrim(base_path, "\\/"))), true), + &var_export( + &PhpMixed::String(format!("{}/", shirabe_php_shim::rtrim(base_path, "\\/"))), + true + ), 0, Some(-1), ) @@ -1223,7 +1486,13 @@ impl AutoloadGenerator { let absolute_app_base_dir_phar_code = format!( " => {}", substr( - &var_export(&PhpMixed::String(format!("{}/", shirabe_php_shim::rtrim(&format!("phar://{}", base_path), "\\/"))), true), + &var_export( + &PhpMixed::String(format!( + "{}/", + shirabe_php_shim::rtrim(&format!("phar://{}", base_path), "\\/") + )), + true + ), 0, Some(-1), ) @@ -1242,7 +1511,8 @@ impl AutoloadGenerator { // PHP: foreach ((array) $loader as $prop => $value) — iterate over the loader's properties for (prop, value) in loader.as_array_iter() { - if !is_array(&value) || value.as_array().map_or(0, |a| a.len()) == 0 + if !is_array(&value) + || value.as_array().map_or(0, |a| a.len()) == 0 || !str_starts_with(&prop, prefix) { continue; @@ -1251,17 +1521,23 @@ impl AutoloadGenerator { } for (prop, value) in &maps { - let value = strtr( - &var_export(value, true), - &{ - let mut m: IndexMap<String, String> = IndexMap::new(); - m.insert(absolute_vendor_path_code.clone(), vendor_path_code.clone()); - m.insert(absolute_vendor_phar_path_code.clone(), vendor_phar_path_code.clone()); - m.insert(absolute_app_base_dir_code.clone(), app_base_dir_code.clone()); - m.insert(absolute_app_base_dir_phar_code.clone(), app_base_dir_phar_code.clone()); - m - }, - ); + let value = strtr(&var_export(value, true), &{ + let mut m: IndexMap<String, String> = IndexMap::new(); + m.insert(absolute_vendor_path_code.clone(), vendor_path_code.clone()); + m.insert( + absolute_vendor_phar_path_code.clone(), + vendor_phar_path_code.clone(), + ); + m.insert( + absolute_app_base_dir_code.clone(), + app_base_dir_code.clone(), + ); + m.insert( + absolute_app_base_dir_phar_code.clone(), + app_base_dir_phar_code.clone(), + ); + m + }); let value = shirabe_php_shim::ltrim(&Preg::replace("/^ */m", " $0$0", &value), None); let value = Preg::replace("/ +$/m", "", &value); @@ -1324,7 +1600,11 @@ impl AutoloadGenerator { let type_arr = type_value.as_array().cloned().unwrap_or_default(); for (namespace, paths) in type_arr { - let namespace = if in_array(r#type, &vec!["psr-4".to_string(), "psr-0".to_string()], true) { + let namespace = if in_array( + r#type, + &vec!["psr-4".to_string(), "psr-0".to_string()], + true, + ) { // normalize namespaces to ensure "\" becomes "" and others do not have leading separators as they are not needed ltrim(&namespace, "\\") } else { @@ -1338,7 +1618,9 @@ impl AutoloadGenerator { }; for path in path_list { let mut path_str = path.as_string().unwrap_or("").to_string(); - if (r#type == "files" || r#type == "classmap" || r#type == "exclude-from-classmap") + if (r#type == "files" + || r#type == "classmap" + || r#type == "exclude-from-classmap") && package.get_target_dir().is_some() && !Filesystem::is_readable(&format!("{}/{}", install_path, path_str)) { @@ -1348,23 +1630,38 @@ impl AutoloadGenerator { "\\<dirsep\\>", "[\\\\/]", &preg_quote( - &str_replace_multi(&package.get_target_dir().unwrap_or_default(), &[("/", "<dirsep>"), ("\\", "<dirsep>")]), + &str_replace_multi( + &package.get_target_dir().unwrap_or_default(), + &[("/", "<dirsep>"), ("\\", "<dirsep>")], + ), None, ), ); path_str = ltrim( - &Preg::replace(&format!("{{^{}}}", target_dir), "", <rim(&path_str, "\\/")), + &Preg::replace( + &format!("{{^{}}}", target_dir), + "", + <rim(&path_str, "\\/"), + ), "\\/", ); } else { // add target-dir from file paths that don't have it - path_str = format!("{}/{}", package.get_target_dir().unwrap_or_default(), path_str); + path_str = format!( + "{}/{}", + package.get_target_dir().unwrap_or_default(), + path_str + ); } } if r#type == "exclude-from-classmap" { // first escape user input - let p = Preg::replace("{/+}", "/", &preg_quote(&trim(&strtr(&path_str, "\\", "/"), "/"), None)); + let p = Preg::replace( + "{/+}", + "/", + &preg_quote(&trim(&strtr(&path_str, "\\", "/"), "/"), None), + ); // add support for wildcards * and ** let p = strtr(&p, &{ @@ -1392,7 +1689,11 @@ impl AutoloadGenerator { install_path.clone() }; - let resolved_path = realpath(&format!("{}/{}", install_path_for_resolve, updir.clone().unwrap_or_default())); + let resolved_path = realpath(&format!( + "{}/{}", + install_path_for_resolve, + updir.clone().unwrap_or_default() + )); let resolved_path = match resolved_path { Some(rp) => rp, None => continue, @@ -1402,13 +1703,18 @@ impl AutoloadGenerator { preg_quote(&strtr(&resolved_path, "\\", "/"), None), p ); - autoloads.insert(numeric_index.to_string(), Box::new(PhpMixed::String(entry))); + autoloads + .insert(numeric_index.to_string(), Box::new(PhpMixed::String(entry))); numeric_index += 1; continue; } let relative_path = if install_path.is_empty() { - if path_str.is_empty() { ".".to_string() } else { path_str.clone() } + if path_str.is_empty() { + ".".to_string() + } else { + path_str.clone() + } } else { format!("{}/{}", install_path, path_str) }; @@ -1421,7 +1727,10 @@ impl AutoloadGenerator { continue; } if r#type == "classmap" { - autoloads.insert(numeric_index.to_string(), Box::new(PhpMixed::String(relative_path))); + autoloads.insert( + numeric_index.to_string(), + Box::new(PhpMixed::String(relative_path)), + ); numeric_index += 1; continue; } @@ -1484,18 +1793,26 @@ impl AutoloadGenerator { } } } - add(root_package.as_package_interface(), &packages, &mut include, &replaced_by); + add( + root_package.as_package_interface(), + &packages, + &mut include, + &replaced_by, + ); - array_filter(package_map, |item: &(Box<dyn PackageInterface>, Option<String>)| -> bool { - let package = &item.0; - for name in package.get_names(true) { - if include.contains_key(&name) { - return true; + array_filter( + package_map, + |item: &(Box<dyn PackageInterface>, Option<String>)| -> bool { + let package = &item.0; + for name in package.get_names(true) { + if include.contains_key(&name) { + return true; + } } - } - false - }) + false + }, + ) } /// Sorts packages by dependency weight @@ -1515,13 +1832,19 @@ impl AutoloadGenerator { paths.insert(name, path.clone()); } - let sorted_packages = PackageSorter::sort_packages(packages.values().map(|p| p.clone_box()).collect(), IndexMap::new()); + let sorted_packages = PackageSorter::sort_packages( + packages.values().map(|p| p.clone_box()).collect(), + IndexMap::new(), + ); let mut sorted_package_map: Vec<(Box<dyn PackageInterface>, Option<String>)> = vec![]; for package in sorted_packages { let name = package.get_name().to_string(); - sorted_package_map.push((packages.get(&name).unwrap().clone_box(), paths.get(&name).cloned().flatten())); + sorted_package_map.push(( + packages.get(&name).unwrap().clone_box(), + paths.get(&name).cloned().flatten(), + )); } sorted_package_map @@ -1534,7 +1857,10 @@ pub fn composer_require(file_identifier: &str, file: &str) { .map(|v| v.as_bool().unwrap_or(false)) .unwrap_or(false) { - shirabe_php_shim::globals_set(&["__composer_autoload_files", file_identifier], PhpMixed::Bool(true)); + shirabe_php_shim::globals_set( + &["__composer_autoload_files", file_identifier], + PhpMixed::Bool(true), + ); let _ = shirabe_php_shim::php_require(file); } diff --git a/crates/shirabe/src/autoload/class_loader.rs b/crates/shirabe/src/autoload/class_loader.rs index 9727a45..2f4cfa0 100644 --- a/crates/shirabe/src/autoload/class_loader.rs +++ b/crates/shirabe/src/autoload/class_loader.rs @@ -4,10 +4,10 @@ use indexmap::IndexMap; use std::sync::{LazyLock, Mutex}; use shirabe_php_shim::{ - apcu_add, apcu_fetch, array_merge, array_values, call_user_func_array, defined, file_exists, - filter_var, function_exists, include_file, ini_get, spl_autoload_register, - spl_autoload_unregister, stream_resolve_include_path, strlen, strpos, strrpos, strtr, substr, - InvalidArgumentException, PhpMixed, DIRECTORY_SEPARATOR, FILTER_VALIDATE_BOOLEAN, + DIRECTORY_SEPARATOR, FILTER_VALIDATE_BOOLEAN, InvalidArgumentException, PhpMixed, apcu_add, + apcu_fetch, array_merge, array_values, call_user_func_array, defined, file_exists, filter_var, + function_exists, include_file, ini_get, spl_autoload_register, spl_autoload_unregister, + stream_resolve_include_path, strlen, strpos, strrpos, strtr, substr, }; /// @var array<string, self> @@ -314,8 +314,7 @@ impl ClassLoader { && filter_var( &ini_get("apc.enabled").unwrap_or_default(), FILTER_VALIDATE_BOOLEAN, - ) - { + ) { apcu_prefix } else { None @@ -344,8 +343,7 @@ impl ClassLoader { if prepend { let mut new_map: IndexMap<String, ClassLoader> = IndexMap::new(); new_map.insert(self.vendor_dir.clone().unwrap(), self.clone()); - let old_map: IndexMap<String, ClassLoader> = - std::mem::take(&mut *registered); + let old_map: IndexMap<String, ClassLoader> = std::mem::take(&mut *registered); for (k, v) in old_map { if !new_map.contains_key(&k) { new_map.insert(k, v); diff --git a/crates/shirabe/src/autoload/class_map_generator.rs b/crates/shirabe/src/autoload/class_map_generator.rs index 3cfe83a..df61795 100644 --- a/crates/shirabe/src/autoload/class_map_generator.rs +++ b/crates/shirabe/src/autoload/class_map_generator.rs @@ -29,7 +29,13 @@ impl ClassMapGenerator { .map(|(k, v)| (k, Box::new(PhpMixed::String(v)))) .collect(), ); - std::fs::write(file, format!("<?php return {};", shirabe_php_shim::var_export(&maps_php, true)))?; + std::fs::write( + file, + format!( + "<?php return {};", + shirabe_php_shim::var_export(&maps_php, true) + ), + )?; Ok(()) } @@ -41,12 +47,21 @@ impl ClassMapGenerator { autoload_type: Option<String>, scanned_files: &mut IndexMap<String, bool>, ) -> anyhow::Result<IndexMap<String, String>> { - let generator = ExternalClassMapGenerator::new(vec!["php".to_string(), "inc".to_string(), "hh".to_string()]); + let generator = ExternalClassMapGenerator::new(vec![ + "php".to_string(), + "inc".to_string(), + "hh".to_string(), + ]); let mut file_list = FileList::new(); file_list.files = scanned_files.clone(); generator.avoid_duplicate_scans(&file_list); - generator.scan_paths(path, excluded.as_deref(), autoload_type.as_deref().unwrap_or("classmap"), namespace.as_deref())?; + generator.scan_paths( + path, + excluded.as_deref(), + autoload_type.as_deref().unwrap_or("classmap"), + namespace.as_deref(), + )?; let class_map = generator.get_class_map(); diff --git a/crates/shirabe/src/autoload/mod.rs b/crates/shirabe/src/autoload/mod.rs new file mode 100644 index 0000000..e460f71 --- /dev/null +++ b/crates/shirabe/src/autoload/mod.rs @@ -0,0 +1,3 @@ +pub mod autoload_generator; +pub mod class_loader; +pub mod class_map_generator; diff --git a/crates/shirabe/src/cache.rs b/crates/shirabe/src/cache.rs index 301ddc5..fb05ce2 100644 --- a/crates/shirabe/src/cache.rs +++ b/crates/shirabe/src/cache.rs @@ -7,9 +7,9 @@ use chrono::Utc; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::symfony::component::finder::finder::Finder; use shirabe_php_shim::{ - abs, bin2hex, dirname, file_exists, file_get_contents, file_put_contents, filemtime, - function_exists, hash_file, is_dir, is_writable, mkdir, random_bytes, random_int, rename, - sprintf, time, unlink, ErrorException, PhpMixed, + ErrorException, PhpMixed, abs, bin2hex, dirname, file_exists, file_get_contents, + file_put_contents, filemtime, function_exists, hash_file, is_dir, is_writable, mkdir, + random_bytes, random_int, rename, sprintf, time, unlink, }; use crate::io::io_interface::IOInterface; @@ -71,10 +71,7 @@ impl Cache { } pub fn is_usable(path: &str) -> bool { - !Preg::is_match( - r"{(^|[\\\\/])(\$null|nul|NUL|/dev/null)([\\\\/]|$)}", - path, - ) + !Preg::is_match(r"{(^|[\\\\/])(\$null|nul|NUL|/dev/null)([\\\\/]|$)}", path) } pub fn is_enabled(&mut self) -> bool { @@ -136,12 +133,7 @@ impl Cache { IOInterface::DEBUG, ); - let temp_file_name = format!( - "{}{}{}.tmp", - self.root, - file, - bin2hex(&random_bytes(5)), - ); + let temp_file_name = format!("{}{}{}.tmp", self.root, file, bin2hex(&random_bytes(5)),); // TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch (ErrorException) let attempt: Result<bool> = { let put = file_put_contents(&temp_file_name, contents.as_bytes()); @@ -188,8 +180,10 @@ impl Cache { if function_exists("disk_free_space") { // TODO(phase-b): @disk_free_space suppresses errors PhpMixed::Float( - shirabe_php_shim::disk_free_space(&dirname(&temp_file_name)) - .unwrap_or(0.0), + shirabe_php_shim::disk_free_space(&dirname( + &temp_file_name, + )) + .unwrap_or(0.0), ) } else { PhpMixed::String("unknown".to_string()) @@ -197,11 +191,8 @@ impl Cache { ], ); - self.io.write_error( - PhpMixed::String(message), - true, - IOInterface::NORMAL, - ); + self.io + .write_error(PhpMixed::String(message), true, IOInterface::NORMAL); return Ok(false); } @@ -219,7 +210,8 @@ impl Cache { if self.is_enabled() && !self.read_only { let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file); let full_path = format!("{}{}", self.root, file); - self.filesystem.ensure_directory_exists(&dirname(&full_path)); + self.filesystem + .ensure_directory_exists(&dirname(&full_path)); if !file_exists(source) { self.io.write_error( @@ -232,10 +224,7 @@ impl Cache { ); } else if self.io.is_debug() { self.io.write_error( - PhpMixed::String(format!( - "Writing {} into cache from {}", - full_path, source, - )), + PhpMixed::String(format!("Writing {} into cache from {}", full_path, source,)), true, IOInterface::NORMAL, ); 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)); } diff --git a/crates/shirabe/src/compiler.rs b/crates/shirabe/src/compiler.rs index 4f5abc2..e8ce824 100644 --- a/crates/shirabe/src/compiler.rs +++ b/crates/shirabe/src/compiler.rs @@ -8,9 +8,9 @@ use shirabe_external_packages::seld::phar_utils::timestamps::Timestamps; use shirabe_external_packages::symfony::component::finder::finder::Finder; use shirabe_external_packages::symfony::component::finder::spl_file_info::SplFileInfo; use shirabe_php_shim::{ - array_search, file_exists, file_get_contents, strcmp, strtr, strtr_array, - token_get_all, PhpMixed, Phar, RuntimeException, UnexpectedValueException, - T_COMMENT, T_DOC_COMMENT, T_WHITESPACE, + Phar, PhpMixed, RuntimeException, T_COMMENT, T_DOC_COMMENT, T_WHITESPACE, + UnexpectedValueException, array_search, file_exists, file_get_contents, strcmp, strtr, + strtr_array, token_get_all, }; use crate::json::json_file::JsonFile; @@ -57,7 +57,9 @@ impl Compiler { code: 0, }.into()); } - self.version = Git::parse_rev_list_output(&output, &process).trim().to_string(); + self.version = Git::parse_rev_list_output(&output, &process) + .trim() + .to_string(); let command = Git::build_rev_list_command(&process, &["-n1", "--format=%ci", "HEAD"]); let mut output = String::new(); @@ -69,9 +71,10 @@ impl Compiler { } let version_date_str = Git::parse_rev_list_output(&output, &process); - self.version_date = chrono::DateTime::parse_from_str(version_date_str.trim(), "%Y-%m-%d %H:%M:%S %z") - .map(|dt| dt.with_timezone(&chrono::Utc)) - .unwrap_or_else(|_| chrono::Utc::now()); + self.version_date = + chrono::DateTime::parse_from_str(version_date_str.trim(), "%Y-%m-%d %H:%M:%S %z") + .map(|dt| dt.with_timezone(&chrono::Utc)) + .unwrap_or_else(|_| chrono::Utc::now()); let mut git_describe_output = String::new(); if process.execute( @@ -118,13 +121,12 @@ impl Compiler { phar.start_buffering(); - let finder_sort = - |a: &SplFileInfo, b: &SplFileInfo| -> i64 { - strcmp( - &strtr(a.get_real_path(), "\\", "/"), - &strtr(b.get_real_path(), "\\", "/"), - ) - }; + let finder_sort = |a: &SplFileInfo, b: &SplFileInfo| -> i64 { + strcmp( + &strtr(a.get_real_path(), "\\", "/"), + &strtr(b.get_real_path(), "\\", "/"), + ) + }; // Add Composer sources let mut finder = Finder::new(); @@ -143,7 +145,10 @@ impl Compiler { // Add runtime utilities separately to make sure they retain the docblocks as these will get copied into projects self.add_file( &mut phar, - &SplFileInfo::new(&format!("{}/src/Composer/Autoload/ClassLoader.php", repo_root)), + &SplFileInfo::new(&format!( + "{}/src/Composer/Autoload/ClassLoader.php", + repo_root + )), false, )?; self.add_file( @@ -218,14 +223,9 @@ impl Compiler { let mut unexpected_files: Vec<String> = vec![]; for file in finder.iter() { - if let Some(index) = - array_search(file.get_real_path(), &extra_files) - { + if let Some(index) = array_search(file.get_real_path(), &extra_files) { extra_files.shift_remove(&index); - } else if !Preg::is_match( - r"{(^LICENSE(?:\.txt)?$|\.php$)}", - file.get_filename(), - )? { + } else if !Preg::is_match(r"{(^LICENSE(?:\.txt)?$|\.php$)}", file.get_filename())? { unexpected_files.push(file.to_string()); } @@ -314,8 +314,7 @@ impl Compiler { fn add_file(&self, phar: &mut Phar, file: &SplFileInfo, strip: bool) -> anyhow::Result<()> { let path = self.get_relative_file_path(file); - let content = file_get_contents(file.get_path()) - .unwrap_or_default(); + let content = file_get_contents(file.get_path()).unwrap_or_default(); let mut content = if strip { self.strip_whitespace(&content) } else if file.get_filename() == "LICENSE" { @@ -326,10 +325,7 @@ impl Compiler { if path == "src/Composer/Composer.php" { let mut replacements: IndexMap<String, String> = IndexMap::new(); - replacements.insert( - "@package_version@".to_string(), - self.version.clone(), - ); + replacements.insert("@package_version@".to_string(), self.version.clone()); replacements.insert( "@package_branch_alias_version@".to_string(), self.branch_alias_version.clone(), @@ -340,8 +336,7 @@ impl Compiler { ); content = strtr_array(&content, &replacements); content = Preg::replace( - r"{SOURCE_VERSION = '[^']+';}" -, + r"{SOURCE_VERSION = '[^']+';}", "SOURCE_VERSION = '';", &content, )?; @@ -354,8 +349,7 @@ impl Compiler { fn add_composer_bin(&self, phar: &mut Phar) -> anyhow::Result<()> { let repo_root = shirabe_php_shim::dirname_levels(file!(), 2); - let content = file_get_contents(&format!("{}/bin/composer", repo_root)) - .unwrap_or_default(); + let content = file_get_contents(&format!("{}/bin/composer", repo_root)).unwrap_or_default(); let content = Preg::replace(r"{^#!/usr/bin/env php\s*}", "", &content)?; phar.add_from_string("bin/composer", &content); Ok(()) @@ -388,8 +382,8 @@ impl Compiler { let whitespace = Preg::replace(r"{(?:\r\n|\r|\n)}", "\n", &whitespace) .unwrap_or(whitespace); // trim leading spaces - let whitespace = Preg::replace(r"{\n +}", "\n", &whitespace) - .unwrap_or(whitespace); + let whitespace = + Preg::replace(r"{\n +}", "\n", &whitespace).unwrap_or(whitespace); output.push_str(&whitespace); } else { output.push_str(token_value); diff --git a/crates/shirabe/src/composer.rs b/crates/shirabe/src/composer.rs index 25e509c..7d1ad19 100644 --- a/crates/shirabe/src/composer.rs +++ b/crates/shirabe/src/composer.rs @@ -32,7 +32,9 @@ impl Composer { if Self::VERSION == "@package_version@" { return Self::SOURCE_VERSION.to_string(); } - if Self::BRANCH_ALIAS_VERSION != "" && Preg::is_match("{^[a-f0-9]{40}$}", Self::VERSION).unwrap_or(false) { + if Self::BRANCH_ALIAS_VERSION != "" + && Preg::is_match("{^[a-f0-9]{40}$}", Self::VERSION).unwrap_or(false) + { return format!("{}+{}", Self::BRANCH_ALIAS_VERSION, Self::VERSION); } Self::VERSION.to_string() diff --git a/crates/shirabe/src/config.rs b/crates/shirabe/src/config.rs index d6d9dcc..12755b0 100644 --- a/crates/shirabe/src/config.rs +++ b/crates/shirabe/src/config.rs @@ -1,13 +1,16 @@ //! ref: composer/src/Composer/Config.php +pub mod config_source_interface; +pub mod json_config_source; + use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - array_key_exists, array_merge_recursive, array_reverse, array_search_mixed, array_unique, - current, empty, filter_var, implode, in_array, is_array, is_int, is_string, key, max, parse_url, - reset, rtrim, strtolower, strtoupper, strtr, substr, trigger_error, PhpMixed, - RuntimeException, E_USER_DEPRECATED, FILTER_VALIDATE_URL, PHP_URL_HOST, PHP_URL_SCHEME, + E_USER_DEPRECATED, FILTER_VALIDATE_URL, PHP_URL_HOST, PHP_URL_SCHEME, PhpMixed, + RuntimeException, array_key_exists, array_merge_recursive, array_reverse, array_search_mixed, + array_unique, current, empty, filter_var, implode, in_array, is_array, is_int, is_string, key, + max, parse_url, reset, rtrim, strtolower, strtoupper, strtr, substr, trigger_error, }; use crate::advisory::auditor::Auditor; @@ -49,11 +52,23 @@ impl Config { let mut c: IndexMap<String, PhpMixed> = IndexMap::new(); c.insert("process-timeout".to_string(), PhpMixed::Int(300)); c.insert("use-include-path".to_string(), PhpMixed::Bool(false)); - c.insert("allow-plugins".to_string(), PhpMixed::Array(IndexMap::new())); - c.insert("use-parent-dir".to_string(), PhpMixed::String("prompt".to_string())); - c.insert("preferred-install".to_string(), PhpMixed::String("dist".to_string())); + c.insert( + "allow-plugins".to_string(), + PhpMixed::Array(IndexMap::new()), + ); + c.insert( + "use-parent-dir".to_string(), + PhpMixed::String("prompt".to_string()), + ); + c.insert( + "preferred-install".to_string(), + PhpMixed::String("dist".to_string()), + ); let mut audit: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); - audit.insert("ignore".to_string(), Box::new(PhpMixed::Array(IndexMap::new()))); + audit.insert( + "ignore".to_string(), + Box::new(PhpMixed::Array(IndexMap::new())), + ); audit.insert( "abandoned".to_string(), Box::new(PhpMixed::String(Auditor::ABANDONED_FAIL.to_string())), @@ -69,7 +84,10 @@ impl Config { ]), ); c.insert("gitlab-protocol".to_string(), PhpMixed::Null); - c.insert("vendor-dir".to_string(), PhpMixed::String("vendor".to_string())); + c.insert( + "vendor-dir".to_string(), + PhpMixed::String("vendor".to_string()), + ); c.insert( "bin-dir".to_string(), PhpMixed::String("{$vendor-dir}/bin".to_string()), @@ -78,7 +96,10 @@ impl Config { "cache-dir".to_string(), PhpMixed::String("{$home}/cache".to_string()), ); - c.insert("data-dir".to_string(), PhpMixed::String("{$home}".to_string())); + c.insert( + "data-dir".to_string(), + PhpMixed::String("{$home}".to_string()), + ); c.insert( "cache-files-dir".to_string(), PhpMixed::String("{$cache-dir}/files".to_string()), @@ -98,7 +119,10 @@ impl Config { PhpMixed::String("300MiB".to_string()), ); c.insert("cache-read-only".to_string(), PhpMixed::Bool(false)); - c.insert("bin-compat".to_string(), PhpMixed::String("auto".to_string())); + c.insert( + "bin-compat".to_string(), + PhpMixed::String("auto".to_string()), + ); c.insert("discard-changes".to_string(), PhpMixed::Bool(false)); c.insert("autoloader-suffix".to_string(), PhpMixed::Null); c.insert("sort-packages".to_string(), PhpMixed::Bool(false)); @@ -106,18 +130,21 @@ impl Config { c.insert("classmap-authoritative".to_string(), PhpMixed::Bool(false)); c.insert("apcu-autoloader".to_string(), PhpMixed::Bool(false)); c.insert("prepend-autoloader".to_string(), PhpMixed::Bool(true)); - c.insert("update-with-minimal-changes".to_string(), PhpMixed::Bool(false)); + c.insert( + "update-with-minimal-changes".to_string(), + PhpMixed::Bool(false), + ); c.insert( "github-domains".to_string(), PhpMixed::List(vec![Box::new(PhpMixed::String("github.com".to_string()))]), ); - c.insert("bitbucket-expose-hostname".to_string(), PhpMixed::Bool(true)); - c.insert("disable-tls".to_string(), PhpMixed::Bool(false)); - c.insert("secure-http".to_string(), PhpMixed::Bool(true)); c.insert( - "secure-svn-domains".to_string(), - PhpMixed::List(vec![]), + "bitbucket-expose-hostname".to_string(), + PhpMixed::Bool(true), ); + c.insert("disable-tls".to_string(), PhpMixed::Bool(false)); + c.insert("secure-http".to_string(), PhpMixed::Bool(true)); + c.insert("secure-svn-domains".to_string(), PhpMixed::List(vec![])); c.insert("cafile".to_string(), PhpMixed::Null); c.insert("capath".to_string(), PhpMixed::Null); c.insert("github-expose-hostname".to_string(), PhpMixed::Bool(true)); @@ -125,9 +152,15 @@ impl Config { "gitlab-domains".to_string(), PhpMixed::List(vec![Box::new(PhpMixed::String("gitlab.com".to_string()))]), ); - c.insert("store-auths".to_string(), PhpMixed::String("prompt".to_string())); + c.insert( + "store-auths".to_string(), + PhpMixed::String("prompt".to_string()), + ); c.insert("platform".to_string(), PhpMixed::Array(IndexMap::new())); - c.insert("archive-format".to_string(), PhpMixed::String("tar".to_string())); + c.insert( + "archive-format".to_string(), + PhpMixed::String("tar".to_string()), + ); c.insert("archive-dir".to_string(), PhpMixed::String(".".to_string())); c.insert("htaccess-protect".to_string(), PhpMixed::Bool(true)); c.insert("use-github-api".to_string(), PhpMixed::Bool(true)); @@ -136,21 +169,36 @@ impl Config { "platform-check".to_string(), PhpMixed::String("php-only".to_string()), ); - c.insert("bitbucket-oauth".to_string(), PhpMixed::Array(IndexMap::new())); + c.insert( + "bitbucket-oauth".to_string(), + PhpMixed::Array(IndexMap::new()), + ); c.insert("github-oauth".to_string(), PhpMixed::Array(IndexMap::new())); c.insert("gitlab-oauth".to_string(), PhpMixed::Array(IndexMap::new())); c.insert("gitlab-token".to_string(), PhpMixed::Array(IndexMap::new())); c.insert("http-basic".to_string(), PhpMixed::Array(IndexMap::new())); c.insert("bearer".to_string(), PhpMixed::Array(IndexMap::new())); - c.insert("custom-headers".to_string(), PhpMixed::Array(IndexMap::new())); + c.insert( + "custom-headers".to_string(), + PhpMixed::Array(IndexMap::new()), + ); c.insert("bump-after-update".to_string(), PhpMixed::Bool(false)); - c.insert("allow-missing-requirements".to_string(), PhpMixed::Bool(false)); - c.insert("client-certificate".to_string(), PhpMixed::Array(IndexMap::new())); + c.insert( + "allow-missing-requirements".to_string(), + PhpMixed::Bool(false), + ); + c.insert( + "client-certificate".to_string(), + PhpMixed::Array(IndexMap::new()), + ); c.insert( "forgejo-domains".to_string(), PhpMixed::List(vec![Box::new(PhpMixed::String("codeberg.org".to_string()))]), ); - c.insert("forgejo-token".to_string(), PhpMixed::Array(IndexMap::new())); + c.insert( + "forgejo-token".to_string(), + PhpMixed::Array(IndexMap::new()), + ); c } @@ -271,7 +319,9 @@ impl Config { self.set_source_of_config_value(&val, key, source); } else if in_array( PhpMixed::String(key.clone()), - &PhpMixed::List(vec![Box::new(PhpMixed::String("allow-plugins".to_string()))]), + &PhpMixed::List(vec![Box::new(PhpMixed::String( + "allow-plugins".to_string(), + ))]), true, ) && self.config.contains_key(key) && is_array(self.config.get(key).cloned().unwrap_or(PhpMixed::Null)) @@ -320,10 +370,7 @@ impl Config { if is_array(val.clone()) || is_array(existing.clone()) { if is_string(&val) { let mut m = IndexMap::new(); - m.insert( - "*".to_string(), - Box::new(val.clone()), - ); + m.insert("*".to_string(), Box::new(val.clone())); val = PhpMixed::Array(m); } let existing = self.config.get(key).cloned().unwrap_or(PhpMixed::Null); @@ -335,10 +382,8 @@ impl Config { .insert(format!("{}*", key), source.to_string()); } let cur = self.config.get(key).cloned().unwrap_or(PhpMixed::Null); - self.config.insert( - key.clone(), - array_merge_recursive(vec![cur, val.clone()]), - ); + self.config + .insert(key.clone(), array_merge_recursive(vec![cur, val.clone()])); self.set_source_of_config_value(&val, key, source); // the full match pattern needs to be last let has_wildcard = matches!( @@ -379,8 +424,7 @@ impl Config { .unwrap_or(PhpMixed::List(vec![])), _ => PhpMixed::List(vec![]), }; - let new_ignores = - array_merge_recursive(vec![current_ignores, val_ignore]); + let new_ignores = array_merge_recursive(vec![current_ignores, val_ignore]); if let Some(PhpMixed::Array(audit)) = self.config.get_mut("audit") { audit.insert("ignore".to_string(), Box::new(new_ignores)); } @@ -391,7 +435,10 @@ impl Config { } } - let repositories_section = config.get("repositories").cloned().unwrap_or(PhpMixed::Null); + let repositories_section = config + .get("repositories") + .cloned() + .unwrap_or(PhpMixed::Null); if !empty(&repositories_section) && is_array(repositories_section.clone()) { self.repositories = array_reverse(&self.repositories, true); let new_repos_map = match &repositories_section { @@ -448,7 +495,8 @@ impl Config { // PHP: $this->repositories[] = $repository // appending to numeric-keyed map let next_idx = self.repositories.len(); - self.repositories.insert(next_idx.to_string(), repository.clone()); + self.repositories + .insert(next_idx.to_string(), repository.clone()); } let found_key = array_search_mixed( repository, @@ -469,7 +517,8 @@ impl Config { ); } else if name == "packagist" { // BC support for default "packagist" named repo - self.repositories.insert(format!("{}.org", name), repository.clone()); + self.repositories + .insert(format!("{}.org", name), repository.clone()); self.set_source_of_config_value( repository, &format!("repositories.{}.org", name), @@ -506,16 +555,8 @@ impl Config { pub fn get_with_flags(&mut self, key: &str, flags: i64) -> Result<PhpMixed> { match key { // strings/paths with env var and {$refs} support - "vendor-dir" - | "bin-dir" - | "process-timeout" - | "data-dir" - | "cache-dir" - | "cache-files-dir" - | "cache-repo-dir" - | "cache-vcs-dir" - | "cafile" - | "capath" => { + "vendor-dir" | "bin-dir" | "process-timeout" | "data-dir" | "cache-dir" + | "cache-files-dir" | "cache-repo-dir" | "cache-vcs-dir" | "cafile" | "capath" => { // convert foo-bar to COMPOSER_FOO_BAR and check if it exists since it overrides the local config let env = format!("COMPOSER_{}", strtoupper(&strtr(key, "-", "_"))); @@ -592,10 +633,7 @@ impl Config { // ints without env var support "cache-ttl" => Ok(PhpMixed::Int(max( 0, - self.config - .get(key) - .and_then(|v| v.as_int()) - .unwrap_or(0), + self.config.get(key).and_then(|v| v.as_int()).unwrap_or(0), ))), // numbers with kb/mb/gb support, without env var support @@ -747,8 +785,7 @@ impl Config { } let val = self.config.get(key).cloned().unwrap_or(PhpMixed::Null); - let allowed = matches!(&val, PhpMixed::Bool(_)) - || val.as_string() == Some("stash"); + let allowed = matches!(&val, PhpMixed::Bool(_)) || val.as_string() == Some("stash"); if !allowed { return Err(RuntimeException { message: format!( @@ -795,7 +832,11 @@ impl Config { false, ); if let Some(idx_val) = found { - let idx = idx_val.as_string().unwrap_or("").parse::<usize>().unwrap_or(usize::MAX); + let idx = idx_val + .as_string() + .unwrap_or("") + .parse::<usize>() + .unwrap_or(usize::MAX); if idx < protos.len() { protos.remove(idx); } @@ -832,12 +873,9 @@ impl Config { let mut result = self.config.get(key).cloned().unwrap_or(PhpMixed::Null); let abandoned_env = self.get_composer_env("COMPOSER_AUDIT_ABANDONED"); if !matches!(abandoned_env, PhpMixed::Bool(false)) { - let abandoned_env_str = - abandoned_env.as_string().unwrap_or("").to_string(); - let valid_choices: Vec<String> = Auditor::ABANDONEDS - .iter() - .map(|s| s.to_string()) - .collect(); + let abandoned_env_str = abandoned_env.as_string().unwrap_or("").to_string(); + let valid_choices: Vec<String> = + Auditor::ABANDONEDS.iter().map(|s| s.to_string()).collect(); if !in_array( PhpMixed::String(abandoned_env_str.clone()), &PhpMixed::List( @@ -941,12 +979,7 @@ impl Config { } /// @param mixed $configValue - fn set_source_of_config_value( - &mut self, - config_value: &PhpMixed, - path: &str, - source: &str, - ) { + fn set_source_of_config_value(&mut self, config_value: &PhpMixed, path: &str, source: &str) { self.source_of_config_value .insert(path.to_string(), source.to_string()); diff --git a/crates/shirabe/src/config/config_source_interface.rs b/crates/shirabe/src/config/config_source_interface.rs index 9f3321c..2a48635 100644 --- a/crates/shirabe/src/config/config_source_interface.rs +++ b/crates/shirabe/src/config/config_source_interface.rs @@ -4,9 +4,20 @@ use indexmap::IndexMap; use shirabe_php_shim::PhpMixed; pub trait ConfigSourceInterface { - fn add_repository(&mut self, name: &str, config: Option<IndexMap<String, PhpMixed>>, append: bool) -> anyhow::Result<()>; + fn add_repository( + &mut self, + name: &str, + config: Option<IndexMap<String, PhpMixed>>, + append: bool, + ) -> anyhow::Result<()>; - fn insert_repository(&mut self, name: &str, config: Option<IndexMap<String, PhpMixed>>, reference_name: &str, offset: i64) -> anyhow::Result<()>; + fn insert_repository( + &mut self, + name: &str, + config: Option<IndexMap<String, PhpMixed>>, + reference_name: &str, + offset: i64, + ) -> anyhow::Result<()>; fn set_repository_url(&mut self, name: &str, url: &str) -> anyhow::Result<()>; diff --git a/crates/shirabe/src/config/json_config_source.rs b/crates/shirabe/src/config/json_config_source.rs index 264b0e1..5dc5e8d 100644 --- a/crates/shirabe/src/config/json_config_source.rs +++ b/crates/shirabe/src/config/json_config_source.rs @@ -3,8 +3,8 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_php_shim::{ - array_unshift, call_user_func_array, chmod, explode, file_get_contents, file_put_contents, - implode, is_writable, sprintf, PhpMixed, RuntimeException, Silencer, PHP_EOL, + PHP_EOL, PhpMixed, RuntimeException, Silencer, array_unshift, call_user_func_array, chmod, + explode, file_get_contents, file_put_contents, implode, is_writable, sprintf, }; use crate::config::config_source_interface::ConfigSourceInterface; @@ -198,9 +198,12 @@ impl JsonConfigSource { } } } - self.file.write(config, shirabe_php_shim::JSON_UNESCAPED_SLASHES - | shirabe_php_shim::JSON_PRETTY_PRINT - | shirabe_php_shim::JSON_UNESCAPED_UNICODE)?; + self.file.write( + config, + shirabe_php_shim::JSON_UNESCAPED_SLASHES + | shirabe_php_shim::JSON_PRETTY_PRINT + | shirabe_php_shim::JSON_UNESCAPED_UNICODE, + )?; } // TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch @@ -320,7 +323,10 @@ impl ConfigSourceInterface for JsonConfigSource { let _ = (cfg, args); todo!("setRepositoryUrl fallback closure body"); }), - vec![PhpMixed::String(name.to_string()), PhpMixed::String(url.to_string())], + vec![ + PhpMixed::String(name.to_string()), + PhpMixed::String(url.to_string()), + ], ) } diff --git a/crates/shirabe/src/console/application.rs b/crates/shirabe/src/console/application.rs index 25bea8d..8fb84dc 100644 --- a/crates/shirabe/src/console/application.rs +++ b/crates/shirabe/src/console/application.rs @@ -112,7 +112,9 @@ impl Application { ini_set("xdebug.scream", "0"); } - if function_exists("date_default_timezone_set") && function_exists("date_default_timezone_get") { + if function_exists("date_default_timezone_set") + && function_exists("date_default_timezone_get") + { let tz = Silencer::call(|| Ok(date_default_timezone_get())).unwrap_or_default(); date_default_timezone_set(&tz); } @@ -153,7 +155,11 @@ impl Application { } } - pub fn run(&mut self, input: Option<&dyn InputInterface>, output: Option<&dyn OutputInterface>) -> anyhow::Result<i64> { + pub fn run( + &mut self, + input: Option<&dyn InputInterface>, + output: Option<&dyn OutputInterface>, + ) -> anyhow::Result<i64> { let output_owned: Box<dyn OutputInterface>; let output_ref: &dyn OutputInterface = if let Some(o) = output { o @@ -165,13 +171,18 @@ impl Application { self.inner.run(input, Some(output_ref)) } - pub fn do_run(&mut self, input: &mut dyn InputInterface, output: &dyn OutputInterface) -> anyhow::Result<i64> { + pub fn do_run( + &mut self, + input: &mut dyn InputInterface, + output: &dyn OutputInterface, + ) -> anyhow::Result<i64> { self.disable_plugins_by_default = input.has_parameter_option("--no-plugins", false); self.disable_scripts_by_default = input.has_parameter_option("--no-scripts", false); // PHP: static $stdin = null; // We use an Option here to mimic the lazy initialization. - static STDIN: std::sync::OnceLock<Option<shirabe_php_shim::PhpResource>> = std::sync::OnceLock::new(); + static STDIN: std::sync::OnceLock<Option<shirabe_php_shim::PhpResource>> = + std::sync::OnceLock::new(); let stdin = STDIN.get_or_init(|| { if defined("STDIN") { Some(shirabe_php_shim::stdin_handle()) @@ -187,7 +198,9 @@ impl Application { input.set_interactive(false); } - let mut helpers: Vec<Box<dyn shirabe_external_packages::symfony::component::console::helper::helper::Helper>> = vec![]; + let mut helpers: Vec< + Box<dyn shirabe_external_packages::symfony::component::console::helper::helper::Helper>, + > = vec![]; helpers.push(Box::new(QuestionHelper::new())); let console_io = ConsoleIO::new(input, output, HelperSet::new(helpers)); self.io = Box::new(console_io); @@ -200,7 +213,11 @@ impl Application { io.write_error("Disabling cache usage", true, IOInterface::DEBUG); Platform::put_env( "COMPOSER_CACHE_DIR", - if Platform::is_windows() { "nul" } else { "/dev/null" }, + if Platform::is_windows() { + "nul" + } else { + "/dev/null" + }, ); } @@ -213,7 +230,14 @@ impl Application { self.initial_working_directory = getcwd(); let cwd = Platform::get_cwd_real(true); io.write_error( - &format!("Changed CWD to {}", if !cwd.is_empty() { cwd.clone() } else { nwd.clone() }), + &format!( + "Changed CWD to {}", + if !cwd.is_empty() { + cwd.clone() + } else { + nwd.clone() + } + ), true, IOInterface::DEBUG, ); @@ -237,13 +261,24 @@ impl Application { // prompt user for dir change if no composer.json is present in current dir let no_composer_json_commands = vec![ - "".to_string(), "list".to_string(), "init".to_string(), "about".to_string(), - "help".to_string(), "diagnose".to_string(), "self-update".to_string(), - "global".to_string(), "create-project".to_string(), "outdated".to_string(), + "".to_string(), + "list".to_string(), + "init".to_string(), + "about".to_string(), + "help".to_string(), + "diagnose".to_string(), + "self-update".to_string(), + "global".to_string(), + "create-project".to_string(), + "outdated".to_string(), ]; let use_parent_dir_if_no_json_available = self.get_use_parent_dir_config_value(); if new_work_dir.is_none() - && !in_array(command_name.as_deref().unwrap_or(""), &no_composer_json_commands, true) + && !in_array( + command_name.as_deref().unwrap_or(""), + &no_composer_json_commands, + true, + ) && !file_exists(&Factory::get_composer_file()) && use_parent_dir_if_no_json_available.as_bool() != Some(false) && (command_name.as_deref() != Some("config") @@ -253,13 +288,17 @@ impl Application { && input.has_parameter_option("-h", true) == false { let mut dir = dirname(&Platform::get_cwd_real(true)); - let home_value = Platform::get_env("HOME").or_else(|| Platform::get_env("USERPROFILE")).unwrap_or_else(|| "/".to_string()); + let home_value = Platform::get_env("HOME") + .or_else(|| Platform::get_env("USERPROFILE")) + .unwrap_or_else(|| "/".to_string()); let home = realpath(&home_value).unwrap_or_default(); // abort when we reach the home dir or top of the filesystem while dirname(&dir) != dir && dir != home { if file_exists(&format!("{}/{}", dir, Factory::get_composer_file())) { - if use_parent_dir_if_no_json_available.as_bool() != Some(true) && !io.is_interactive() { + if use_parent_dir_if_no_json_available.as_bool() != Some(true) + && !io.is_interactive() + { io.write_error(&format!("<info>No composer.json in current directory, to use the one at {} run interactively or set config.use-parent-dir to true</info>", dir)); break; } @@ -292,12 +331,17 @@ impl Application { is_non_allowed_root = self.is_running_as_root(); if is_non_allowed_root { - let uid: i64 = Platform::get_env("SUDO_UID").map(|v| v.parse().unwrap_or(0)).unwrap_or(0); + let uid: i64 = Platform::get_env("SUDO_UID") + .map(|v| v.parse().unwrap_or(0)) + .unwrap_or(0); if uid != 0 { // Silently clobber any sudo credentials on the invoking user to avoid privilege escalations later on // ref. https://github.com/composer/composer/issues/5119 let _ = Silencer::call(|| { - shirabe_php_shim::exec(&format!("sudo -u \\#{} sudo -K > /dev/null 2>&1", uid)); + shirabe_php_shim::exec(&format!( + "sudo -u \\#{} sudo -K > /dev/null 2>&1", + uid + )); Ok(()) }); } @@ -312,24 +356,34 @@ impl Application { // avoid loading plugins/initializing the Composer instance earlier than necessary if no plugin command is needed // if showing the version, we never need plugin commands - let may_need_plugin_command = !input.has_parameter_option_array(&vec!["--version".to_string(), "-V".to_string()], false) - && ( - command_name.is_none() - || in_array(command_name.as_deref().unwrap_or(""), &vec!["".to_string(), "list".to_string(), "help".to_string()], true) - || (command_name.as_deref() == Some("_complete") && !is_non_allowed_root) - ); + let may_need_plugin_command = !input + .has_parameter_option_array(&vec!["--version".to_string(), "-V".to_string()], false) + && (command_name.is_none() + || in_array( + command_name.as_deref().unwrap_or(""), + &vec!["".to_string(), "list".to_string(), "help".to_string()], + true, + ) + || (command_name.as_deref() == Some("_complete") && !is_non_allowed_root)); let may_need_script_command = may_need_plugin_command || command_name.as_deref() == Some("run-script") || raw_command_name != command_name; - if may_need_plugin_command && !self.disable_plugins_by_default && !self.has_plugin_commands { + if may_need_plugin_command && !self.disable_plugins_by_default && !self.has_plugin_commands + { // at this point plugins are needed, so if we are running as root and it is not allowed we need to prompt // if interactive, and abort otherwise if is_non_allowed_root { io.write_error("<warning>Do not run Composer as root/super user! See https://getcomposer.org/root for details</warning>"); - if io.is_interactive() && io.ask_confirmation("<info>Continue as root/super user</info> [<comment>yes</comment>]? ".to_string(), true) { + if io.is_interactive() + && io.ask_confirmation( + "<info>Continue as root/super user</info> [<comment>yes</comment>]? " + .to_string(), + true, + ) + { // avoid a second prompt later is_non_allowed_root = false; } else { @@ -431,7 +485,9 @@ impl Application { io.write_error(&format!("<warning>Composer supports PHP 7.2.5 and above, you will most likely encounter problems with your PHP {}. Upgrading is strongly recommended but you can use Composer 2.2.x LTS as a fallback.</warning>", PHP_VERSION)); } - if XdebugHandler::is_xdebug_active() && Platform::get_env("COMPOSER_DISABLE_XDEBUG_WARN").is_none() { + if XdebugHandler::is_xdebug_active() + && Platform::get_env("COMPOSER_DISABLE_XDEBUG_WARN").is_none() + { io.write_error("<warning>Composer is operating slower than normal because you have Xdebug enabled. See https://getcomposer.org/xdebug</warning>"); } @@ -454,7 +510,11 @@ impl Application { io.write_error("<warning>Do not run Composer as root/super user! See https://getcomposer.org/root for details</warning>"); if io.is_interactive() { - if !io.ask_confirmation("<info>Continue as root/super user</info> [<comment>yes</comment>]? ".to_string(), true) { + if !io.ask_confirmation( + "<info>Continue as root/super user</info> [<comment>yes</comment>]? " + .to_string(), + true, + ) { return Ok(1); } } @@ -468,7 +528,12 @@ impl Application { } else { String::new() }; - let tempfile = format!("{}/temp-{}{}", sys_get_temp_dir(), pid, bin2hex(&random_bytes(5))); + let tempfile = format!( + "{}/temp-{}{}", + sys_get_temp_dir(), + pid, + bin2hex(&random_bytes(5)) + ); if !(file_put_contents(&tempfile, file!()) > 0 && file_get_contents(&tempfile).as_deref() == Some(file!()) && unlink(&tempfile) @@ -482,31 +547,70 @@ impl Application { // add non-standard scripts as own commands let file = Factory::get_composer_file(); if may_need_script_command && is_file(&file) && Filesystem::is_readable(&file) { - let composer_json = json_decode(&file_get_contents(&file).unwrap_or_default(), true); + let composer_json = + json_decode(&file_get_contents(&file).unwrap_or_default(), true); if let Some(arr) = composer_json.as_array() { if let Some(scripts) = arr.get("scripts").and_then(|v| v.as_array()) { for (script, dummy) in scripts { - let script_event_const = format!("Composer\\Script\\ScriptEvents::{}", str_replace("-", "_", &strtoupper(script))); + let script_event_const = format!( + "Composer\\Script\\ScriptEvents::{}", + str_replace("-", "_", &strtoupper(script)) + ); if !defined(&script_event_const) { if self.inner.has(script) { io.write_error(&format!("<warning>A script named {} would override a Composer command and has been skipped</warning>", script)); } else { - let mut description = format!("Runs the {} script as defined in composer.json", script); + let mut description = format!( + "Runs the {} script as defined in composer.json", + script + ); - if let Some(desc) = arr.get("scripts-descriptions").and_then(|v| v.as_array()).and_then(|a| a.get(script)).and_then(|v| v.as_string()) { + if let Some(desc) = arr + .get("scripts-descriptions") + .and_then(|v| v.as_array()) + .and_then(|a| a.get(script)) + .and_then(|v| v.as_string()) + { description = desc.to_string(); } - let aliases: Vec<String> = arr.get("scripts-aliases").and_then(|v| v.as_array()).and_then(|a| a.get(script)).and_then(|v| v.as_list()).map(|l| l.iter().filter_map(|v| v.as_string().map(|s| s.to_string())).collect()).unwrap_or_default(); + let aliases: Vec<String> = arr + .get("scripts-aliases") + .and_then(|v| v.as_array()) + .and_then(|a| a.get(script)) + .and_then(|v| v.as_list()) + .map(|l| { + l.iter() + .filter_map(|v| { + v.as_string().map(|s| s.to_string()) + }) + .collect() + }) + .unwrap_or_default(); if let Some(composer) = self.get_composer(false, None, None)? { let root_package = composer.get_package(); let generator = composer.get_autoload_generator(); - let package_map = generator.build_package_map(composer.get_installation_manager(), &*root_package, vec![])?; - let map = generator.parse_autoloads(&package_map, &*root_package, PhpMixed::Bool(false)); + let package_map = generator.build_package_map( + composer.get_installation_manager(), + &*root_package, + vec![], + )?; + let map = generator.parse_autoloads( + &package_map, + &*root_package, + PhpMixed::Bool(false), + ); - let loader = generator.create_loader(&map, composer.get_config().get("vendor-dir").as_string().map(|s| s.to_string())); + let loader = generator.create_loader( + &map, + composer + .get_config() + .get("vendor-dir") + .as_string() + .map(|s| s.to_string()), + ); loader.register(false); } @@ -514,12 +618,22 @@ impl Application { let dummy_str = dummy.as_string().unwrap_or(""); let cmd: Box<dyn SymfonyCommand> = if is_string(dummy) && shirabe_php_shim::class_exists(dummy_str) - && is_subclass_of(dummy_str, "Symfony\\Component\\Console\\Command\\Command") - { - if is_subclass_of(dummy_str, "Symfony\\Component\\Console\\SingleCommandApplication") { + && is_subclass_of( + dummy_str, + "Symfony\\Component\\Console\\Command\\Command", + ) { + if is_subclass_of( + dummy_str, + "Symfony\\Component\\Console\\SingleCommandApplication", + ) { io.write_error(&format!("<warning>The script named {} extends SingleCommandApplication which is not compatible with Composer 2.9+, make sure you extend Symfony\\Component\\Console\\Command instead.</warning>", script)); } - let mut cmd = shirabe_php_shim::instantiate_class::<Box<dyn SymfonyCommand>>(dummy_str, vec![PhpMixed::String(script.clone())]); + let mut cmd = shirabe_php_shim::instantiate_class::< + Box<dyn SymfonyCommand>, + >( + dummy_str, + vec![PhpMixed::String(script.clone())], + ); let _ = SingleCommandApplication::class_name(); // makes sure the command is find()'able by the name defined in composer.json, and the name isn't overridden in its configure() @@ -529,13 +643,19 @@ impl Application { cmd.set_name(script); } - if cmd.get_description().is_empty() && is_string(&PhpMixed::String(description.clone())) { + if cmd.get_description().is_empty() + && is_string(&PhpMixed::String(description.clone())) + { cmd.set_description(&description); } cmd } else { // fallback to usual aliasing behavior - Box::new(ScriptAliasCommand::new(script.clone(), description.clone(), aliases)) + Box::new(ScriptAliasCommand::new( + script.clone(), + description.clone(), + aliases, + )) }; // Compatibility layer for symfony/console <7.4 @@ -561,12 +681,16 @@ impl Application { let result = self.inner.do_run(input, output)?; - if input.has_parameter_option_array(&vec!["--version".to_string(), "-V".to_string()], true) { + if input + .has_parameter_option_array(&vec!["--version".to_string(), "-V".to_string()], true) + { io.write_error(&sprintf( "<info>PHP</info> version <comment>%s</comment> (%s)", &[PHP_VERSION.into(), PHP_BINARY.into()], )); - io.write_error("Run the \"diagnose\" command to get more detailed diagnostics output."); + io.write_error( + "Run the \"diagnose\" command to get more detailed diagnostics output.", + ); } Ok(result) @@ -596,9 +720,16 @@ impl Application { Ok(r) => Ok(r), Err(e) => { if let Some(see) = e.downcast_ref::<ScriptExecutionException>() { - if self.get_disable_plugins_by_default() && self.is_running_as_root() && !self.io.is_interactive() { + if self.get_disable_plugins_by_default() + && self.is_running_as_root() + && !self.io.is_interactive() + { io.write_error("<error>Plugins have been disabled automatically as you are running as root, this may be the cause of the script failure.</error>", true, IOInterface::QUIET); - io.write_error("<error>See also https://getcomposer.org/root</error>", true, IOInterface::QUIET); + io.write_error( + "<error>See also https://getcomposer.org/root</error>", + true, + IOInterface::QUIET, + ); } Ok(see.get_code()) @@ -609,13 +740,18 @@ impl Application { self.hint_common_errors(&e, output); if !method_exists(&self.inner, "setCatchErrors") { - if let Some(coi) = output.as_any().downcast_ref::<dyn ConsoleOutputInterface>() { + if let Some(coi) = + output.as_any().downcast_ref::<dyn ConsoleOutputInterface>() + { self.inner.render_throwable(&e, coi.get_error_output()); } else { self.inner.render_throwable(&e, output); } - let code = e.downcast_ref::<RuntimeException>().map(|r| r.code).unwrap_or(0); + let code = e + .downcast_ref::<RuntimeException>() + .map(|r| r.code) + .unwrap_or(0); return Ok(max_i64(1, code)); } @@ -641,13 +777,20 @@ impl Application { fn get_new_working_dir(&self, input: &dyn InputInterface) -> anyhow::Result<Option<String>> { let working_dir = input - .get_parameter_option(&vec!["--working-dir".to_string(), "-d".to_string()], None, true) + .get_parameter_option( + &vec!["--working-dir".to_string(), "-d".to_string()], + None, + true, + ) .as_string() .map(|s| s.to_string()); if let Some(ref wd) = working_dir { if !is_dir(wd) { return Err(RuntimeException { - message: format!("Invalid working directory specified, {} does not exist.", wd), + message: format!( + "Invalid working directory specified, {} does not exist.", + wd + ), code: 0, } .into()); @@ -677,7 +820,11 @@ impl Application { let df = disk_free_space(&dir); let mut hit = df.map(|d| d < min_space_free).unwrap_or(false); if !hit { - dir = config.get("vendor-dir").as_string().unwrap_or("").to_string(); + dir = config + .get("vendor-dir") + .as_string() + .unwrap_or("") + .to_string(); let df = disk_free_space(&dir); hit = df.map(|d| d < min_space_free).unwrap_or(false); } @@ -695,23 +842,48 @@ impl Application { Silencer::restore(); let message = exception.to_string(); - if exception.downcast_ref::<TransportException>().is_some() && str_contains(&message, "Unable to use a proxy") { - io.write_error("<error>The following exception indicates your proxy is misconfigured</error>", true, IOInterface::QUIET); + if exception.downcast_ref::<TransportException>().is_some() + && str_contains(&message, "Unable to use a proxy") + { + io.write_error( + "<error>The following exception indicates your proxy is misconfigured</error>", + true, + IOInterface::QUIET, + ); io.write_error("<error>Check https://getcomposer.org/doc/faqs/how-to-use-composer-behind-a-proxy.md for details</error>", true, IOInterface::QUIET); } - if Platform::is_windows() && exception.downcast_ref::<TransportException>().is_some() && str_contains(&message, "unable to get local issuer certificate") { + if Platform::is_windows() + && exception.downcast_ref::<TransportException>().is_some() + && str_contains(&message, "unable to get local issuer certificate") + { let avast_detect = glob("C:\\Program Files\\Avast*"); - if is_array(&PhpMixed::List(avast_detect.iter().map(|s| Box::new(PhpMixed::String(s.clone()))).collect())) && count(&avast_detect) != 0 { + if is_array(&PhpMixed::List( + avast_detect + .iter() + .map(|s| Box::new(PhpMixed::String(s.clone()))) + .collect(), + )) && count(&avast_detect) != 0 + { io.write_error("<error>The following exception indicates a possible issue with the Avast Firewall</error>", true, IOInterface::QUIET); - io.write_error("<error>Check https://getcomposer.org/local-issuer for details</error>", true, IOInterface::QUIET); + io.write_error( + "<error>Check https://getcomposer.org/local-issuer for details</error>", + true, + IOInterface::QUIET, + ); } else { io.write_error("<error>The following exception indicates a possible issue with a Firewall/Antivirus</error>", true, IOInterface::QUIET); - io.write_error("<error>Check https://getcomposer.org/local-issuer for details</error>", true, IOInterface::QUIET); + io.write_error( + "<error>Check https://getcomposer.org/local-issuer for details</error>", + true, + IOInterface::QUIET, + ); } } - if Platform::is_windows() && strpos(&message, "The system cannot find the path specified").is_some() { + if Platform::is_windows() + && strpos(&message, "The system cannot find the path specified").is_some() + { io.write_error("<error>The following exception may be caused by a stale entry in your cmd.exe AutoRun</error>", true, IOInterface::QUIET); io.write_error("<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#-the-system-cannot-find-the-path-specified-windows- for details</error>", true, IOInterface::QUIET); } @@ -721,14 +893,28 @@ impl Application { io.write_error("<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details</error>", true, IOInterface::QUIET); } - if exception.downcast_ref::<ProcessTimedOutException>().is_some() { - io.write_error("<error>The following exception is caused by a process timeout</error>", true, IOInterface::QUIET); + if exception + .downcast_ref::<ProcessTimedOutException>() + .is_some() + { + io.write_error( + "<error>The following exception is caused by a process timeout</error>", + true, + IOInterface::QUIET, + ); io.write_error("<error>Check https://getcomposer.org/doc/06-config.md#process-timeout for details</error>", true, IOInterface::QUIET); } - if self.get_disable_plugins_by_default() && self.is_running_as_root() && !self.io.is_interactive() { + if self.get_disable_plugins_by_default() + && self.is_running_as_root() + && !self.io.is_interactive() + { io.write_error("<error>Plugins have been disabled automatically as you are running as root, this may be the cause of the following exception. See also https://getcomposer.org/root</error>", true, IOInterface::QUIET); - } else if exception.downcast_ref::<CommandNotFoundException>().is_some() && self.get_disable_plugins_by_default() { + } else if exception + .downcast_ref::<CommandNotFoundException>() + .is_some() + && self.get_disable_plugins_by_default() + { io.write_error("<error>Plugins have been disabled, which may be why some commands are missing, unless you made a typo</error>", true, IOInterface::QUIET); } @@ -851,7 +1037,10 @@ impl Application { if !Composer::BRANCH_ALIAS_VERSION.is_empty() && Composer::BRANCH_ALIAS_VERSION != "@package_branch_alias_version@" { - branch_alias_string = sprintf(" (%s)", &[Composer::BRANCH_ALIAS_VERSION.to_string().into()]); + branch_alias_string = sprintf( + " (%s)", + &[Composer::BRANCH_ALIAS_VERSION.to_string().into()], + ); } sprintf( @@ -867,11 +1056,46 @@ impl Application { pub(crate) fn get_default_input_definition(&self) -> InputDefinition { let mut definition = self.inner.get_default_input_definition(); - definition.add_option(InputOption::new("--profile", None, Some(InputOption::VALUE_NONE), "Display timing and memory usage information", None, vec![])); - definition.add_option(InputOption::new("--no-plugins", None, Some(InputOption::VALUE_NONE), "Whether to disable plugins.", None, vec![])); - definition.add_option(InputOption::new("--no-scripts", None, Some(InputOption::VALUE_NONE), "Skips the execution of all scripts defined in composer.json file.", None, vec![])); - definition.add_option(InputOption::new("--working-dir", Some("-d"), Some(InputOption::VALUE_REQUIRED), "If specified, use the given directory as working directory.", None, vec![])); - definition.add_option(InputOption::new("--no-cache", None, Some(InputOption::VALUE_NONE), "Prevent use of the cache", None, vec![])); + definition.add_option(InputOption::new( + "--profile", + None, + Some(InputOption::VALUE_NONE), + "Display timing and memory usage information", + None, + vec![], + )); + definition.add_option(InputOption::new( + "--no-plugins", + None, + Some(InputOption::VALUE_NONE), + "Whether to disable plugins.", + None, + vec![], + )); + definition.add_option(InputOption::new( + "--no-scripts", + None, + Some(InputOption::VALUE_NONE), + "Skips the execution of all scripts defined in composer.json file.", + None, + vec![], + )); + definition.add_option(InputOption::new( + "--working-dir", + Some("-d"), + Some(InputOption::VALUE_REQUIRED), + "If specified, use the given directory as working directory.", + None, + vec![], + )); + definition.add_option(InputOption::new( + "--no-cache", + None, + Some(InputOption::VALUE_NONE), + "Prevent use of the cache", + None, + vec![], + )); definition } @@ -883,15 +1107,27 @@ impl Application { let composer = self.get_composer(false, Some(false), None)?.cloned(); let composer = match composer { Some(c) => Some(c), - None => Factory::create_global(&*self.io, self.disable_plugins_by_default, self.disable_scripts_by_default), + None => Factory::create_global( + &*self.io, + self.disable_plugins_by_default, + self.disable_scripts_by_default, + ), }; if let Some(composer) = composer { let pm = composer.get_plugin_manager(); let mut ctor_args: IndexMap<String, PhpMixed> = IndexMap::new(); - ctor_args.insert("composer".to_string(), PhpMixed::Object(shirabe_php_shim::ArrayObject::new())); - ctor_args.insert("io".to_string(), PhpMixed::Object(shirabe_php_shim::ArrayObject::new())); - for capability in pm.get_plugin_capabilities("Composer\\Plugin\\Capability\\CommandProvider", ctor_args) { + ctor_args.insert( + "composer".to_string(), + PhpMixed::Object(shirabe_php_shim::ArrayObject::new()), + ); + ctor_args.insert( + "io".to_string(), + PhpMixed::Object(shirabe_php_shim::ArrayObject::new()), + ); + for capability in pm + .get_plugin_capabilities("Composer\\Plugin\\Capability\\CommandProvider", ctor_args) + { let new_commands = capability.get_commands(); for command in &new_commands { if command.as_any().downcast_ref::<BaseCommand>().is_none() { diff --git a/crates/shirabe/src/console/github_action_error.rs b/crates/shirabe/src/console/github_action_error.rs index cb6a181..c74f95f 100644 --- a/crates/shirabe/src/console/github_action_error.rs +++ b/crates/shirabe/src/console/github_action_error.rs @@ -24,10 +24,16 @@ impl GithubActionError { if file_truthy && line_truthy { let file = self.escape_property(file.unwrap()); - self.io.write(&format!("::error file={},line={}::{}", file, line.unwrap(), message)); + self.io.write(&format!( + "::error file={},line={}::{}", + file, + line.unwrap(), + message + )); } else if file_truthy { let file = self.escape_property(file.unwrap()); - self.io.write(&format!("::error file={}::{}", file, message)); + self.io + .write(&format!("::error file={}::{}", file, message)); } else { self.io.write(&format!("::error ::{}", message)); } diff --git a/crates/shirabe/src/console/html_output_formatter.rs b/crates/shirabe/src/console/html_output_formatter.rs index f34ca1b..a69b4c6 100644 --- a/crates/shirabe/src/console/html_output_formatter.rs +++ b/crates/shirabe/src/console/html_output_formatter.rs @@ -1,9 +1,9 @@ //! ref: composer/src/Composer/Console/HtmlOutputFormatter.php +use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter; use shirabe_external_packages::symfony::console::formatter::output_formatter_style::OutputFormatterStyle; -use indexmap::IndexMap; #[derive(Debug)] pub struct HtmlOutputFormatter { @@ -66,11 +66,19 @@ impl HtmlOutputFormatter { for code_str in codes_str.split(';') { let code: i64 = code_str.parse().unwrap_or(0); - if let Some(&(_, color)) = Self::AVAILABLE_FOREGROUND_COLORS.iter().find(|&&(k, _)| k == code) { + if let Some(&(_, color)) = Self::AVAILABLE_FOREGROUND_COLORS + .iter() + .find(|&&(k, _)| k == code) + { out.push_str(&format!("color:{};", color)); - } else if let Some(&(_, color)) = Self::AVAILABLE_BACKGROUND_COLORS.iter().find(|&&(k, _)| k == code) { + } else if let Some(&(_, color)) = Self::AVAILABLE_BACKGROUND_COLORS + .iter() + .find(|&&(k, _)| k == code) + { out.push_str(&format!("background-color:{};", color)); - } else if let Some(&(_, option)) = Self::AVAILABLE_OPTIONS.iter().find(|&&(k, _)| k == code) { + } else if let Some(&(_, option)) = + Self::AVAILABLE_OPTIONS.iter().find(|&&(k, _)| k == code) + { match option { "bold" => out.push_str("font-weight:bold;"), "underscore" => out.push_str("text-decoration:underline;"), diff --git a/crates/shirabe/src/console/input/input_option.rs b/crates/shirabe/src/console/input/input_option.rs index 069c3f4..6e93a62 100644 --- a/crates/shirabe/src/console/input/input_option.rs +++ b/crates/shirabe/src/console/input/input_option.rs @@ -51,16 +51,20 @@ impl InputOption { if let SuggestedValues::List(ref list) = this.suggested_values { if !list.is_empty() && !this.inner.accept_value() { return Err(LogicException { - message: "Cannot set suggested values if the option does not accept a value.".to_string(), + message: "Cannot set suggested values if the option does not accept a value." + .to_string(), code: 0, - }.into()); + } + .into()); } } else if let SuggestedValues::Closure(_) = this.suggested_values { if !this.inner.accept_value() { return Err(LogicException { - message: "Cannot set suggested values if the option does not accept a value.".to_string(), + message: "Cannot set suggested values if the option does not accept a value." + .to_string(), code: 0, - }.into()); + } + .into()); } } diff --git a/crates/shirabe/src/console/input/mod.rs b/crates/shirabe/src/console/input/mod.rs new file mode 100644 index 0000000..f4451af --- /dev/null +++ b/crates/shirabe/src/console/input/mod.rs @@ -0,0 +1,2 @@ +pub mod input_argument; +pub mod input_option; diff --git a/crates/shirabe/src/console/mod.rs b/crates/shirabe/src/console/mod.rs new file mode 100644 index 0000000..ea42d75 --- /dev/null +++ b/crates/shirabe/src/console/mod.rs @@ -0,0 +1,4 @@ +pub mod application; +pub mod github_action_error; +pub mod html_output_formatter; +pub mod input; diff --git a/crates/shirabe/src/dependency_resolver/decisions.rs b/crates/shirabe/src/dependency_resolver/decisions.rs index db1068d..22b6fa4 100644 --- a/crates/shirabe/src/dependency_resolver/decisions.rs +++ b/crates/shirabe/src/dependency_resolver/decisions.rs @@ -1,11 +1,11 @@ //! ref: composer/src/Composer/DependencyResolver/Decisions.php -use std::fmt; -use indexmap::IndexMap; -use shirabe_php_shim::LogicException; use crate::dependency_resolver::pool::Pool; use crate::dependency_resolver::rule::Rule; use crate::dependency_resolver::solver_bug_exception::SolverBugException; +use indexmap::IndexMap; +use shirabe_php_shim::LogicException; +use std::fmt; #[derive(Debug)] pub struct Decisions { @@ -43,16 +43,28 @@ impl Decisions { pub fn conflict(&self, literal: i64) -> bool { let package_id = literal.abs(); - (self.decision_map.contains_key(&package_id) && self.decision_map[&package_id] > 0 && literal < 0) - || (self.decision_map.contains_key(&package_id) && self.decision_map[&package_id] < 0 && literal > 0) + (self.decision_map.contains_key(&package_id) + && self.decision_map[&package_id] > 0 + && literal < 0) + || (self.decision_map.contains_key(&package_id) + && self.decision_map[&package_id] < 0 + && literal > 0) } pub fn decided(&self, literal_or_package_id: i64) -> bool { - self.decision_map.get(&literal_or_package_id.abs()).copied().unwrap_or(0) != 0 + self.decision_map + .get(&literal_or_package_id.abs()) + .copied() + .unwrap_or(0) + != 0 } pub fn undecided(&self, literal_or_package_id: i64) -> bool { - self.decision_map.get(&literal_or_package_id.abs()).copied().unwrap_or(0) == 0 + self.decision_map + .get(&literal_or_package_id.abs()) + .copied() + .unwrap_or(0) + == 0 } pub fn decided_install(&self, literal_or_package_id: i64) -> bool { @@ -79,10 +91,17 @@ impl Decisions { } } - panic!("{}", LogicException { - message: format!("Did not find a decision rule using {}", literal_or_package_id), - code: 0, - }.message); + panic!( + "{}", + LogicException { + message: format!( + "Did not find a decision rule using {}", + literal_or_package_id + ), + code: 0, + } + .message + ); } pub fn at_offset(&self, queue_offset: usize) -> &(i64, Rule) { @@ -133,7 +152,8 @@ impl Decisions { } pub fn current(&self) -> Option<&(i64, Rule)> { - self.iterator_cursor.and_then(|cursor| self.decision_queue.get(cursor)) + self.iterator_cursor + .and_then(|cursor| self.decision_queue.get(cursor)) } pub fn key(&self) -> Option<usize> { @@ -141,13 +161,9 @@ impl Decisions { } pub fn next(&mut self) { - self.iterator_cursor = self.iterator_cursor.and_then(|cursor| { - if cursor > 0 { - Some(cursor - 1) - } else { - None - } - }); + self.iterator_cursor = self + .iterator_cursor + .and_then(|cursor| if cursor > 0 { Some(cursor - 1) } else { None }); } pub fn valid(&self) -> bool { @@ -163,11 +179,19 @@ impl Decisions { let previous_decision = self.decision_map.get(&package_id).copied().unwrap_or(0); if previous_decision != 0 { - let literal_string = self.pool.literal_to_pretty_string(literal, &IndexMap::new()); + let literal_string = self + .pool + .literal_to_pretty_string(literal, &IndexMap::new()); let package = self.pool.literal_to_package(literal); - panic!("{}", SolverBugException::new( - format!("Trying to decide {} on level {}, even though {} was previously decided as {}.", literal_string, level, package, previous_decision) - ).0.message); + panic!( + "{}", + SolverBugException::new(format!( + "Trying to decide {} on level {}, even though {} was previously decided as {}.", + literal_string, level, package, previous_decision + )) + .0 + .message + ); } if literal > 0 { diff --git a/crates/shirabe/src/dependency_resolver/default_policy.rs b/crates/shirabe/src/dependency_resolver/default_policy.rs index d84863b..f647327 100644 --- a/crates/shirabe/src/dependency_resolver/default_policy.rs +++ b/crates/shirabe/src/dependency_resolver/default_policy.rs @@ -51,8 +51,12 @@ impl DefaultPolicy { ignore_replace: bool, ) -> i64 { if a.get_name() == b.get_name() { - let a_aliased = (a.as_any() as &dyn Any).downcast_ref::<AliasPackage>().is_some(); - let b_aliased = (b.as_any() as &dyn Any).downcast_ref::<AliasPackage>().is_some(); + let a_aliased = (a.as_any() as &dyn Any) + .downcast_ref::<AliasPackage>() + .is_some(); + let b_aliased = (b.as_any() as &dyn Any) + .downcast_ref::<AliasPackage>() + .is_some(); if a_aliased && !b_aliased { return -1; } @@ -108,7 +112,9 @@ impl DefaultPolicy { let best_literals: Vec<i64> = literals .iter() .copied() - .filter(|&literal| pool.literal_to_package(literal).get_version() == preferred_version) + .filter(|&literal| { + pool.literal_to_package(literal).get_version() == preferred_version + }) .collect(); if !best_literals.is_empty() { return best_literals; @@ -174,15 +180,28 @@ impl DefaultPolicy { } impl PolicyInterface for DefaultPolicy { - fn version_compare(&self, a: &dyn PackageInterface, b: &dyn PackageInterface, operator: &str) -> bool { + fn version_compare( + &self, + a: &dyn PackageInterface, + b: &dyn PackageInterface, + operator: &str, + ) -> bool { if self.prefer_stable { let stab_a = a.get_stability().to_string(); let stab_b = b.get_stability().to_string(); if stab_a != stab_b { let (mut stab_a, mut stab_b) = (stab_a, stab_b); - if self.prefer_lowest && self.prefer_dev_over_prerelease && "stable" != stab_a && "stable" != stab_b { - if stab_a == "dev" { stab_a = "stable".to_string(); } - if stab_b == "dev" { stab_b = "stable".to_string(); } + if self.prefer_lowest + && self.prefer_dev_over_prerelease + && "stable" != stab_a + && "stable" != stab_b + { + if stab_a == "dev" { + stab_a = "stable".to_string(); + } + if stab_b == "dev" { + stab_b = "stable".to_string(); + } } return STABILITIES.get(stab_a.as_str()).copied().unwrap_or(0) < STABILITIES.get(stab_b.as_str()).copied().unwrap_or(0); @@ -213,7 +232,11 @@ impl PolicyInterface for DefaultPolicy { literals.sort(); let result_cache_key = format!( "{}{}", - literals.iter().map(|l| l.to_string()).collect::<Vec<_>>().join(","), + literals + .iter() + .map(|l| l.to_string()) + .collect::<Vec<_>>() + .join(","), required_package.as_deref().unwrap_or("") ); let pool_id = pool as *const Pool as i64; @@ -231,12 +254,8 @@ impl PolicyInterface for DefaultPolicy { for name_literals in packages.values_mut() { name_literals.sort_by(|&a, &b| { - let cache_key = format!( - "i{}.{}{}", - a, - b, - required_package.as_deref().unwrap_or("") - ); + let cache_key = + format!("i{}.{}{}", a, b, required_package.as_deref().unwrap_or("")); { let cache = self.sorting_cache_per_pool.borrow(); if let Some(pool_cache) = cache.get(&pool_id) { @@ -269,12 +288,7 @@ impl PolicyInterface for DefaultPolicy { let mut selected: Vec<i64> = packages.into_values().flatten().collect(); selected.sort_by(|&a, &b| { - let cache_key = format!( - "{}.{}{}", - a, - b, - required_package.as_deref().unwrap_or("") - ); + let cache_key = format!("{}.{}{}", a, b, required_package.as_deref().unwrap_or("")); { let cache = self.sorting_cache_per_pool.borrow(); if let Some(pool_cache) = cache.get(&pool_id) { diff --git a/crates/shirabe/src/dependency_resolver/generic_rule.rs b/crates/shirabe/src/dependency_resolver/generic_rule.rs index 520d483..f30d612 100644 --- a/crates/shirabe/src/dependency_resolver/generic_rule.rs +++ b/crates/shirabe/src/dependency_resolver/generic_rule.rs @@ -1,8 +1,8 @@ //! ref: composer/src/Composer/DependencyResolver/GenericRule.php -use anyhow::Result; -use shirabe_php_shim::{hash_raw, implode, unpack, RuntimeException, PHP_VERSION_ID}; use crate::dependency_resolver::rule::Rule; +use anyhow::Result; +use shirabe_php_shim::{PHP_VERSION_ID, RuntimeException, hash_raw, implode, unpack}; pub struct GenericRule { inner: Rule, @@ -10,7 +10,11 @@ pub struct GenericRule { } impl GenericRule { - pub fn new(mut literals: Vec<i64>, reason: shirabe_php_shim::PhpMixed, reason_data: shirabe_php_shim::PhpMixed) -> Self { + pub fn new( + mut literals: Vec<i64>, + reason: shirabe_php_shim::PhpMixed, + reason_data: shirabe_php_shim::PhpMixed, + ) -> Self { let inner = Rule::new(reason, reason_data); literals.sort(); Self { inner, literals } @@ -21,8 +25,17 @@ impl GenericRule { } pub fn get_hash(&self) -> Result<i64> { - let joined = self.literals.iter().map(|l| l.to_string()).collect::<Vec<_>>().join(","); - let algo = if PHP_VERSION_ID > 80100 { "xxh3" } else { "sha1" }; + let joined = self + .literals + .iter() + .map(|l| l.to_string()) + .collect::<Vec<_>>() + .join(","); + let algo = if PHP_VERSION_ID > 80100 { + "xxh3" + } else { + "sha1" + }; let binary = hash_raw(algo, &joined); let data = unpack("ihash", &binary); match data { @@ -33,13 +46,15 @@ impl GenericRule { Err(RuntimeException { message: format!("Failed unpacking: {}", joined), code: 0, - }.into()) + } + .into()) } } None => Err(RuntimeException { message: format!("Failed unpacking: {}", joined), code: 0, - }.into()), + } + .into()), } } @@ -52,7 +67,11 @@ impl GenericRule { } pub fn to_string(&self) -> String { - let prefix = if self.inner.is_disabled() { "disabled(" } else { "(" }; + let prefix = if self.inner.is_disabled() { + "disabled(" + } else { + "(" + }; let mut result = prefix.to_string(); for (i, literal) in self.literals.iter().enumerate() { if i != 0 { @@ -67,7 +86,9 @@ impl GenericRule { pub trait RuleLiterals { fn get_literals(&self) -> &Vec<i64>; - fn is_multi_conflict_rule(&self) -> bool { false } + fn is_multi_conflict_rule(&self) -> bool { + false + } } impl RuleLiterals for GenericRule { diff --git a/crates/shirabe/src/dependency_resolver/local_repo_transaction.rs b/crates/shirabe/src/dependency_resolver/local_repo_transaction.rs index 5797a3b..dca5c1a 100644 --- a/crates/shirabe/src/dependency_resolver/local_repo_transaction.rs +++ b/crates/shirabe/src/dependency_resolver/local_repo_transaction.rs @@ -1,8 +1,8 @@ //! ref: composer/src/Composer/DependencyResolver/LocalRepoTransaction.php +use super::transaction::Transaction; use crate::repository::installed_repository_interface::InstalledRepositoryInterface; use crate::repository::repository_interface::RepositoryInterface; -use super::transaction::Transaction; #[derive(Debug)] pub struct LocalRepoTransaction { @@ -10,7 +10,10 @@ pub struct LocalRepoTransaction { } impl LocalRepoTransaction { - pub fn new(locked_repository: &dyn RepositoryInterface, local_repository: &dyn InstalledRepositoryInterface) -> Self { + pub fn new( + locked_repository: &dyn RepositoryInterface, + local_repository: &dyn InstalledRepositoryInterface, + ) -> Self { Self { inner: Transaction::new( local_repository.get_packages(), diff --git a/crates/shirabe/src/dependency_resolver/lock_transaction.rs b/crates/shirabe/src/dependency_resolver/lock_transaction.rs index 4b4cc32..44becb0 100644 --- a/crates/shirabe/src/dependency_resolver/lock_transaction.rs +++ b/crates/shirabe/src/dependency_resolver/lock_transaction.rs @@ -39,9 +39,8 @@ impl LockTransaction { }; this.set_result_packages(pool, decisions); let all = this.result_packages.get("all").cloned().unwrap_or_default(); - let present: Vec<Box<dyn PackageInterface>> = this.present_map.values() - .map(|p| p.clone_box()) - .collect(); + let present: Vec<Box<dyn PackageInterface>> = + this.present_map.values().map(|p| p.clone_box()).collect(); this.inner = Transaction::new(present, all); this } @@ -58,9 +57,15 @@ impl LockTransaction { if literal > 0 { let package = pool.literal_to_package(literal); - result_packages.get_mut("all").unwrap().push(package.clone_box()); + result_packages + .get_mut("all") + .unwrap() + .push(package.clone_box()); if !self.unlockable_map.contains_key(&package.get_id()) { - result_packages.get_mut("non-dev").unwrap().push(package.clone_box()); + result_packages + .get_mut("non-dev") + .unwrap() + .push(package.clone_box()); } } } @@ -81,22 +86,37 @@ impl LockTransaction { while i < remaining_dev.len() { if package.get_name() == remaining_dev[i].get_name() { let result_package = remaining_dev.remove(i); - self.result_packages.get_mut("non-dev").unwrap().push(result_package); + self.result_packages + .get_mut("non-dev") + .unwrap() + .push(result_package); } else { i += 1; } } } - self.result_packages.insert("dev".to_string(), remaining_dev); + self.result_packages + .insert("dev".to_string(), remaining_dev); } - pub fn get_new_lock_packages(&self, dev_mode: bool, update_mirrors: bool) -> Vec<Box<dyn PackageInterface>> { + pub fn get_new_lock_packages( + &self, + dev_mode: bool, + update_mirrors: bool, + ) -> Vec<Box<dyn PackageInterface>> { let key = if dev_mode { "dev" } else { "non-dev" }; let mut packages = vec![]; - let source = self.result_packages.get(key).map(|v| v.as_slice()).unwrap_or_default(); + let source = self + .result_packages + .get(key) + .map(|v| v.as_slice()) + .unwrap_or_default(); for package in source { - 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; } @@ -129,7 +149,9 @@ impl LockTransaction { continue; } - if let Some(concrete_pkg) = (present_package.as_any() as &dyn Any).downcast_ref::<Package>() { + if let Some(concrete_pkg) = + (present_package.as_any() as &dyn Any).downcast_ref::<Package>() + { concrete_pkg.set_source_url(package.get_source_url()); concrete_pkg.set_source_mirrors(package.get_source_mirrors()); } @@ -167,10 +189,15 @@ impl LockTransaction { if let Some(all_packages) = self.result_packages.get("all") { for package in all_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() + { let mut i = 0; while i < remaining_aliases.len() { - if remaining_aliases[i].get("package").map(|s| s.as_str()) == Some(package.get_name()) { + if remaining_aliases[i].get("package").map(|s| s.as_str()) + == Some(package.get_name()) + { used_aliases.push(remaining_aliases.remove(i)); } else { i += 1; @@ -189,7 +216,10 @@ impl LockTransaction { used_aliases } - pub fn get_operations(&self) -> &Vec<Box<dyn crate::dependency_resolver::operation::operation_interface::OperationInterface>> { + pub fn get_operations( + &self, + ) -> &Vec<Box<dyn crate::dependency_resolver::operation::operation_interface::OperationInterface>> + { self.inner.get_operations() } } diff --git a/crates/shirabe/src/dependency_resolver/mod.rs b/crates/shirabe/src/dependency_resolver/mod.rs new file mode 100644 index 0000000..9154084 --- /dev/null +++ b/crates/shirabe/src/dependency_resolver/mod.rs @@ -0,0 +1,26 @@ +pub mod decisions; +pub mod default_policy; +pub mod generic_rule; +pub mod local_repo_transaction; +pub mod lock_transaction; +pub mod multi_conflict_rule; +pub mod operation; +pub mod policy_interface; +pub mod pool; +pub mod pool_builder; +pub mod pool_optimizer; +pub mod problem; +pub mod request; +pub mod rule; +pub mod rule2_literals; +pub mod rule_set; +pub mod rule_set_generator; +pub mod rule_set_iterator; +pub mod rule_watch_chain; +pub mod rule_watch_graph; +pub mod rule_watch_node; +pub mod security_advisory_pool_filter; +pub mod solver; +pub mod solver_bug_exception; +pub mod solver_problems_exception; +pub mod transaction; diff --git a/crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs b/crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs index 172b6f4..b15434e 100644 --- a/crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs +++ b/crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs @@ -1,9 +1,9 @@ //! ref: composer/src/Composer/DependencyResolver/MultiConflictRule.php -use anyhow::Result; -use shirabe_php_shim::{hash_raw, PHP_VERSION_ID, RuntimeException}; use crate::dependency_resolver::generic_rule::RuleLiterals; use crate::dependency_resolver::rule::Rule; +use anyhow::Result; +use shirabe_php_shim::{PHP_VERSION_ID, RuntimeException, hash_raw}; #[derive(Debug)] pub struct MultiConflictRule { @@ -12,12 +12,17 @@ pub struct MultiConflictRule { } impl MultiConflictRule { - pub fn new(mut literals: Vec<i64>, reason: shirabe_php_shim::PhpMixed, reason_data: shirabe_php_shim::PhpMixed) -> Result<Self> { + pub fn new( + mut literals: Vec<i64>, + reason: shirabe_php_shim::PhpMixed, + reason_data: shirabe_php_shim::PhpMixed, + ) -> Result<Self> { if literals.len() < 3 { return Err(RuntimeException { message: "multi conflict rule requires at least 3 literals".to_string(), code: 0, - }.into()); + } + .into()); } // sort all packages ascending by id @@ -34,8 +39,17 @@ impl MultiConflictRule { } pub fn get_hash(&self) -> Result<i64> { - let joined = self.literals.iter().map(|l| l.to_string()).collect::<Vec<_>>().join(","); - let algo = if PHP_VERSION_ID > 80100 { "xxh3" } else { "sha1" }; + let joined = self + .literals + .iter() + .map(|l| l.to_string()) + .collect::<Vec<_>>() + .join(","); + let algo = if PHP_VERSION_ID > 80100 { + "xxh3" + } else { + "sha1" + }; let binary = hash_raw(algo, &format!("c:{}", joined)); let data = shirabe_php_shim::unpack("ihash", &binary); match data { @@ -46,13 +60,15 @@ impl MultiConflictRule { Err(RuntimeException { message: format!("Failed unpacking: {}", joined), code: 0, - }.into()) + } + .into()) } } None => Err(RuntimeException { message: format!("Failed unpacking: {}", joined), code: 0, - }.into()), + } + .into()), } } diff --git a/crates/shirabe/src/dependency_resolver/operation/mod.rs b/crates/shirabe/src/dependency_resolver/operation/mod.rs new file mode 100644 index 0000000..ffb0ac7 --- /dev/null +++ b/crates/shirabe/src/dependency_resolver/operation/mod.rs @@ -0,0 +1,7 @@ +pub mod install_operation; +pub mod mark_alias_installed_operation; +pub mod mark_alias_uninstalled_operation; +pub mod operation_interface; +pub mod solver_operation; +pub mod uninstall_operation; +pub mod update_operation; diff --git a/crates/shirabe/src/dependency_resolver/operation/update_operation.rs b/crates/shirabe/src/dependency_resolver/operation/update_operation.rs index eb6e9d6..618ecee 100644 --- a/crates/shirabe/src/dependency_resolver/operation/update_operation.rs +++ b/crates/shirabe/src/dependency_resolver/operation/update_operation.rs @@ -13,7 +13,10 @@ pub struct UpdateOperation { impl UpdateOperation { pub fn new(initial: Box<dyn PackageInterface>, target: Box<dyn PackageInterface>) -> Self { - Self { initial_package: initial, target_package: target } + Self { + initial_package: initial, + target_package: target, + } } pub fn get_initial_package(&self) -> &dyn PackageInterface { @@ -24,19 +27,36 @@ impl UpdateOperation { self.target_package.as_ref() } - pub fn format(initial_package: &dyn PackageInterface, target_package: &dyn PackageInterface, lock: bool) -> String { - let mut from_version = initial_package.get_full_pretty_version(false, PackageInterface::DISPLAY_SOURCE_REF); - let mut to_version = target_package.get_full_pretty_version(false, PackageInterface::DISPLAY_SOURCE_REF); + pub fn format( + initial_package: &dyn PackageInterface, + target_package: &dyn PackageInterface, + lock: bool, + ) -> String { + let mut from_version = + initial_package.get_full_pretty_version(false, PackageInterface::DISPLAY_SOURCE_REF); + let mut to_version = + target_package.get_full_pretty_version(false, PackageInterface::DISPLAY_SOURCE_REF); - if from_version == to_version && initial_package.get_source_reference() != target_package.get_source_reference() { - from_version = initial_package.get_full_pretty_version(true, PackageInterface::DISPLAY_SOURCE_REF); - to_version = target_package.get_full_pretty_version(true, PackageInterface::DISPLAY_SOURCE_REF); - } else if from_version == to_version && initial_package.get_dist_reference() != target_package.get_dist_reference() { - from_version = initial_package.get_full_pretty_version(true, PackageInterface::DISPLAY_DIST_REF); - to_version = target_package.get_full_pretty_version(true, PackageInterface::DISPLAY_DIST_REF); + if from_version == to_version + && initial_package.get_source_reference() != target_package.get_source_reference() + { + from_version = + initial_package.get_full_pretty_version(true, PackageInterface::DISPLAY_SOURCE_REF); + to_version = + target_package.get_full_pretty_version(true, PackageInterface::DISPLAY_SOURCE_REF); + } else if from_version == to_version + && initial_package.get_dist_reference() != target_package.get_dist_reference() + { + from_version = + initial_package.get_full_pretty_version(true, PackageInterface::DISPLAY_DIST_REF); + to_version = + target_package.get_full_pretty_version(true, PackageInterface::DISPLAY_DIST_REF); } - let action_name = if VersionParser::is_upgrade(&initial_package.get_version(), &target_package.get_version()) { + let action_name = if VersionParser::is_upgrade( + &initial_package.get_version(), + &target_package.get_version(), + ) { "Upgrading" } else { "Downgrading" @@ -62,7 +82,11 @@ impl OperationInterface for UpdateOperation { } fn show(&self, lock: bool) -> String { - Self::format(self.initial_package.as_ref(), self.target_package.as_ref(), lock) + Self::format( + self.initial_package.as_ref(), + self.target_package.as_ref(), + lock, + ) } fn to_string(&self) -> String { diff --git a/crates/shirabe/src/dependency_resolver/policy_interface.rs b/crates/shirabe/src/dependency_resolver/policy_interface.rs index 279cf38..a48cd4b 100644 --- a/crates/shirabe/src/dependency_resolver/policy_interface.rs +++ b/crates/shirabe/src/dependency_resolver/policy_interface.rs @@ -4,7 +4,17 @@ use crate::dependency_resolver::pool::Pool; use crate::package::package_interface::PackageInterface; pub trait PolicyInterface { - fn version_compare(&self, a: &dyn PackageInterface, b: &dyn PackageInterface, operator: &str) -> bool; + fn version_compare( + &self, + a: &dyn PackageInterface, + b: &dyn PackageInterface, + operator: &str, + ) -> bool; - fn select_preferred_packages(&self, pool: &Pool, literals: Vec<i64>, required_package: Option<String>) -> Vec<i64>; + fn select_preferred_packages( + &self, + pool: &Pool, + literals: Vec<i64>, + required_package: Option<String>, + ) -> Vec<i64>; } diff --git a/crates/shirabe/src/dependency_resolver/pool.rs b/crates/shirabe/src/dependency_resolver/pool.rs index c783f45..e86e4c2 100644 --- a/crates/shirabe/src/dependency_resolver/pool.rs +++ b/crates/shirabe/src/dependency_resolver/pool.rs @@ -3,7 +3,7 @@ use std::fmt; use indexmap::IndexMap; -use shirabe_php_shim::{abs, spl_object_hash, str_pad, Countable, STR_PAD_LEFT}; +use shirabe_php_shim::{Countable, STR_PAD_LEFT, abs, spl_object_hash, str_pad}; use shirabe_semver::compiling_matcher::CompilingMatcher; use shirabe_semver::constraint::constraint::Constraint; use shirabe_semver::constraint::constraint_interface::ConstraintInterface; @@ -48,10 +48,7 @@ impl Pool { unacceptable_fixed_or_locked_packages: Vec<Box<BasePackage>>, removed_versions: IndexMap<String, IndexMap<String, String>>, removed_versions_by_package: IndexMap<String, IndexMap<String, String>>, - security_removed_versions: IndexMap< - String, - IndexMap<String, Vec<PartialSecurityAdvisory>>, - >, + security_removed_versions: IndexMap<String, IndexMap<String, Vec<PartialSecurityAdvisory>>>, abandoned_removed_versions: IndexMap<String, IndexMap<String, String>>, ) -> Self { let mut this = Self { @@ -116,7 +113,10 @@ impl Pool { constraint: Option<&dyn ConstraintInterface>, ) -> bool { let empty = IndexMap::new(); - let versions = self.security_removed_versions.get(package_name).unwrap_or(&empty); + let versions = self + .security_removed_versions + .get(package_name) + .unwrap_or(&empty); for (version, _package_with_security_advisories) in versions { if let Some(c) = constraint { if c.matches(&Constraint::new("==", version)) { @@ -135,7 +135,10 @@ impl Pool { constraint: Option<&dyn ConstraintInterface>, ) -> Vec<String> { let empty = IndexMap::new(); - let versions = self.security_removed_versions.get(package_name).unwrap_or(&empty); + let versions = self + .security_removed_versions + .get(package_name) + .unwrap_or(&empty); for (version, package_with_security_advisories) in versions { if let Some(c) = constraint { if c.matches(&Constraint::new("==", version)) { @@ -156,7 +159,10 @@ impl Pool { constraint: Option<&dyn ConstraintInterface>, ) -> bool { let empty = IndexMap::new(); - let versions = self.abandoned_removed_versions.get(package_name).unwrap_or(&empty); + let versions = self + .abandoned_removed_versions + .get(package_name) + .unwrap_or(&empty); for (version, _pretty_version) in versions { if let Some(c) = constraint { if c.matches(&Constraint::new("==", version)) { @@ -280,11 +286,7 @@ impl Pool { let package = self.literal_to_package(literal); let prefix = if installed_map.contains_key(&package.id) { - if literal > 0 { - "keep" - } else { - "remove" - } + if literal > 0 { "keep" } else { "remove" } } else { if literal > 0 { "install" @@ -328,8 +330,7 @@ impl Pool { if replaces.contains_key("0") || provides.contains_key("0") { for link in provides.values() { if link.get_target() == name - && (constraint.is_none() - || constraint.unwrap().matches(link.get_constraint())) + && (constraint.is_none() || constraint.unwrap().matches(link.get_constraint())) { return true; } @@ -337,8 +338,7 @@ impl Pool { for link in replaces.values() { if link.get_target() == name - && (constraint.is_none() - || constraint.unwrap().matches(link.get_constraint())) + && (constraint.is_none() || constraint.unwrap().matches(link.get_constraint())) { return true; } diff --git a/crates/shirabe/src/dependency_resolver/pool_builder.rs b/crates/shirabe/src/dependency_resolver/pool_builder.rs index 0d99587..f518545 100644 --- a/crates/shirabe/src/dependency_resolver/pool_builder.rs +++ b/crates/shirabe/src/dependency_resolver/pool_builder.rs @@ -6,8 +6,8 @@ use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::composer::semver::compiling_matcher::CompilingMatcher; use shirabe_external_packages::composer::semver::intervals::Intervals; use shirabe_php_shim::{ - array_chunk, array_flip, array_map, array_merge, array_search, count, in_array, microtime, - number_format, round, spl_object_hash, sprintf, strpos, LogicException, PhpMixed, + LogicException, PhpMixed, array_chunk, array_flip, array_map, array_merge, array_search, count, + in_array, microtime, number_format, round, spl_object_hash, sprintf, strpos, }; use shirabe_semver::constraint::constraint::Constraint; use shirabe_semver::constraint::constraint_interface::ConstraintInterface; @@ -126,7 +126,11 @@ impl PoolBuilder { self.allowed_types = types; } - pub fn build_pool(&mut self, repositories: Vec<Box<dyn RepositoryInterface>>, request: &mut Request) -> anyhow::Result<Pool> { + pub fn build_pool( + &mut self, + repositories: Vec<Box<dyn RepositoryInterface>>, + request: &mut Request, + ) -> anyhow::Result<Pool> { self.restricted_packages_list = if request.get_restricted_packages().is_some() { Some(array_flip(&request.get_restricted_packages().unwrap())) } else { @@ -139,7 +143,8 @@ impl PoolBuilder { if request.get_locked_repository().is_none() { return Err(LogicException { - message: "No lock repo present and yet a partial update was requested.".to_string(), + message: "No lock repo present and yet a partial update was requested." + .to_string(), code: 0, } .into()); @@ -165,9 +170,13 @@ impl PoolBuilder { // if they do get unlocked, but by default they are unlocked without update propagation. if locked_package.get_dist_type().as_deref() == Some("path") { let transport_options = locked_package.get_transport_options(); - let symlink_disabled = transport_options.get("symlink").map(|v| v.as_bool() == Some(false)).unwrap_or(false); + let symlink_disabled = transport_options + .get("symlink") + .map(|v| v.as_bool() == Some(false)) + .unwrap_or(false); if !transport_options.contains_key("symlink") || !symlink_disabled { - self.path_repo_unlocked.insert(locked_package.get_name().to_string(), true); + self.path_repo_unlocked + .insert(locked_package.get_name().to_string(), true); continue; } } @@ -180,11 +189,17 @@ impl PoolBuilder { for package in request.get_fixed_or_locked_packages() { // using MatchAllConstraint here because fixed packages do not need to retrigger // loading any packages - self.loaded_packages.insert(package.get_name().to_string(), Box::new(MatchAllConstraint::new())); + self.loaded_packages.insert( + package.get_name().to_string(), + Box::new(MatchAllConstraint::new()), + ); // replace means conflict, so if a fixed package replaces a name, no need to load that one, packages would conflict anyways for (_k, link) in &package.get_replaces() { - self.loaded_packages.insert(link.get_target().to_string(), Box::new(MatchAllConstraint::new())); + self.loaded_packages.insert( + link.get_target().to_string(), + Box::new(MatchAllConstraint::new()), + ); } // TODO in how far can we do the above for conflicts? It's more tricky cause conflicts can be limited to @@ -193,7 +208,8 @@ impl PoolBuilder { let in_root_or_platform = package .get_repository() .map(|r| { - r.as_any().is::<RootPackageRepository>() || r.as_any().is::<PlatformRepository>() + r.as_any().is::<RootPackageRepository>() + || r.as_any().is::<PlatformRepository>() }) .unwrap_or(false); if in_root_or_platform @@ -206,7 +222,8 @@ impl PoolBuilder { { self.load_package(request, &repositories, &*package, false)?; } else { - self.unacceptable_fixed_or_locked_packages.push(package.clone_box()); + self.unacceptable_fixed_or_locked_packages + .push(package.clone_box()); } } @@ -216,7 +233,8 @@ impl PoolBuilder { continue; } - self.packages_to_load.insert(package_name.clone(), constraint.clone_box()); + self.packages_to_load + .insert(package_name.clone(), constraint.clone_box()); self.max_extended_reqs.insert(package_name.clone(), true); } @@ -253,7 +271,8 @@ impl PoolBuilder { None => continue, }; - let mut package_and_aliases: IndexMap<i64, Box<dyn BasePackage>> = IndexMap::new(); + let mut package_and_aliases: IndexMap<i64, Box<dyn BasePackage>> = + IndexMap::new(); package_and_aliases.insert(i, package.clone_box()); if let Some(aliases) = self.alias_map.get(&spl_object_hash(&*package)) { for (idx, alias) in aliases { @@ -263,7 +282,11 @@ impl PoolBuilder { let mut found = false; for (_idx, package_or_alias) in &package_and_aliases { - if CompilingMatcher::matches(&*constraint, Constraint::OP_EQ, package_or_alias.get_version()) { + if CompilingMatcher::matches( + &*constraint, + Constraint::OP_EQ, + package_or_alias.get_version(), + ) { found = true; } } @@ -287,7 +310,10 @@ impl PoolBuilder { self.root_aliases.clone(), self.root_references.clone(), self.packages.values().map(|p| p.clone_box()).collect(), - self.unacceptable_fixed_or_locked_packages.iter().map(|p| p.clone_box()).collect(), + self.unacceptable_fixed_or_locked_packages + .iter() + .map(|p| p.clone_box()) + .collect(), ); self.event_dispatcher .as_mut() @@ -300,12 +326,16 @@ impl PoolBuilder { .enumerate() .map(|(i, p)| (i as i64, p)) .collect(); - self.unacceptable_fixed_or_locked_packages = pre_pool_create_event.get_unacceptable_fixed_packages(); + self.unacceptable_fixed_or_locked_packages = + pre_pool_create_event.get_unacceptable_fixed_packages(); } let mut pool = Pool::new( self.packages.values().map(|p| p.clone_box()).collect(), - self.unacceptable_fixed_or_locked_packages.iter().map(|p| p.clone_box()).collect(), + self.unacceptable_fixed_or_locked_packages + .iter() + .map(|p| p.clone_box()) + .collect(), ); self.alias_map = IndexMap::new(); @@ -330,7 +360,12 @@ impl PoolBuilder { Ok(pool) } - fn mark_package_name_for_loading(&mut self, request: &Request, name: &str, constraint: Box<dyn ConstraintInterface>) { + fn mark_package_name_for_loading( + &mut self, + request: &Request, + name: &str, + constraint: Box<dyn ConstraintInterface>, + ) { // Skip platform requires at this stage if PlatformRepository::is_platform_package(name) { return; @@ -399,7 +434,11 @@ impl PoolBuilder { self.loaded_packages.shift_remove(name); } - fn load_packages_marked_for_loading(&mut self, request: &mut Request, repositories: &Vec<Box<dyn RepositoryInterface>>) -> anyhow::Result<()> { + fn load_packages_marked_for_loading( + &mut self, + request: &mut Request, + repositories: &Vec<Box<dyn RepositoryInterface>>, + ) -> anyhow::Result<()> { let to_remove: Vec<String> = self .packages_to_load .keys() @@ -420,7 +459,8 @@ impl PoolBuilder { .map(|(k, v)| (k.clone(), v.clone_box())) .collect(); for (name, constraint) in &snapshot { - self.loaded_packages.insert(name.clone(), constraint.clone_box()); + self.loaded_packages + .insert(name.clone(), constraint.clone_box()); } // Load packages in chunks of 50 to prevent memory usage build-up due to caches of all sorts @@ -432,7 +472,12 @@ impl PoolBuilder { // never need to load anything else from them let is_locked_repo = request .get_locked_repository() - .map(|lr| std::ptr::eq(lr as *const _ as *const u8, repository.as_ref() as *const _ as *const u8)) + .map(|lr| { + std::ptr::eq( + lr as *const _ as *const u8, + repository.as_ref() as *const _ as *const u8, + ) + }) .unwrap_or(false); if repository.as_any().is::<PlatformRepository>() || is_locked_repo { continue; @@ -447,17 +492,28 @@ impl PoolBuilder { package_batch, &self.acceptable_stabilities, &self.stability_flags, - self.loaded_per_repo.get(&(repo_index as i64)).cloned().unwrap_or_default(), + self.loaded_per_repo + .get(&(repo_index as i64)) + .cloned() + .unwrap_or_default(), ); - let names_found = result.get("namesFound").and_then(|v| v.as_list()).cloned().unwrap_or_default(); + let names_found = result + .get("namesFound") + .and_then(|v| v.as_list()) + .cloned() + .unwrap_or_default(); for name in &names_found { // avoid loading the same package again from other repositories once it has been found if let Some(b) = package_batches.get_mut(batch_index) { b.shift_remove(name.as_string().unwrap_or("")); } } - let packages_in_result = result.get("packages").and_then(|v| v.as_list()).cloned().unwrap_or_default(); + let packages_in_result = result + .get("packages") + .and_then(|v| v.as_list()) + .cloned() + .unwrap_or_default(); for package in &packages_in_result { let pkg = match package.as_package_interface() { Some(p) => p, @@ -472,7 +528,11 @@ impl PoolBuilder { if in_array(pkg.get_type(), &self.ignored_types, true) || (self.allowed_types.is_some() - && !in_array(pkg.get_type(), self.allowed_types.as_ref().unwrap(), true)) + && !in_array( + pkg.get_type(), + self.allowed_types.as_ref().unwrap(), + true, + )) { continue; } @@ -495,7 +555,13 @@ impl PoolBuilder { Ok(()) } - fn load_package(&mut self, request: &mut Request, repositories: &Vec<Box<dyn RepositoryInterface>>, package: &dyn BasePackage, propagate_update: bool) -> anyhow::Result<()> { + fn load_package( + &mut self, + request: &mut Request, + repositories: &Vec<Box<dyn RepositoryInterface>>, + package: &dyn BasePackage, + propagate_update: bool, + ) -> anyhow::Result<()> { let index = self.index_counter; self.index_counter += 1; self.packages.insert(index, package.clone_box()); @@ -537,19 +603,20 @@ impl PoolBuilder { } else { package.clone_box() }; - let alias_package: Box<dyn BasePackage> = if base_package.as_any().is::<CompletePackage>() { - Box::new(CompleteAliasPackage::new( - base_package.clone_box(), - alias.get("alias_normalized").cloned().unwrap_or_default(), - alias.get("alias").cloned().unwrap_or_default(), - )) - } else { - Box::new(AliasPackage::new( - base_package.clone_box(), - alias.get("alias_normalized").cloned().unwrap_or_default(), - alias.get("alias").cloned().unwrap_or_default(), - )) - }; + let alias_package: Box<dyn BasePackage> = + if base_package.as_any().is::<CompletePackage>() { + Box::new(CompleteAliasPackage::new( + base_package.clone_box(), + alias.get("alias_normalized").cloned().unwrap_or_default(), + alias.get("alias").cloned().unwrap_or_default(), + )) + } else { + Box::new(AliasPackage::new( + base_package.clone_box(), + alias.get("alias_normalized").cloned().unwrap_or_default(), + alias.get("alias").cloned().unwrap_or_default(), + )) + }; // PHP: $aliasPackage->setRootPackageAlias(true); // BasePackage doesn't expose this directly; the AliasPackage trait method handles it. @@ -577,7 +644,9 @@ impl PoolBuilder { if propagate_update && request.get_update_allow_transitive_dependencies() { let skipped_root_requires = self.get_skipped_root_requires(request, &require); - if request.get_update_allow_transitive_root_dependencies() || 0 == count(&skipped_root_requires) { + if request.get_update_allow_transitive_root_dependencies() + || 0 == count(&skipped_root_requires) + { self.unlock_package(request, repositories, &require)?; self.mark_package_name_for_loading(request, &require, link_constraint); } else { @@ -588,7 +657,9 @@ impl PoolBuilder { } } } - } else if self.path_repo_unlocked.contains_key(&require) && !self.loaded_packages.contains_key(&require) { + } else if self.path_repo_unlocked.contains_key(&require) + && !self.loaded_packages.contains_key(&require) + { // if doing a partial update and a package depends on a path-repo-unlocked package which is not referenced by the root, we need to ensure it gets loaded as it was not loaded by the request's root requirements // and would not be loaded above if update propagation is not allowed (which happens if the requirer is itself a path-repo-unlocked package) or if transitive deps are not allowed to be unlocked self.mark_package_name_for_loading(request, &require, link_constraint); @@ -603,10 +674,14 @@ impl PoolBuilder { if propagate_update && request.get_update_allow_transitive_dependencies() { for (_k, link) in &package.get_replaces() { let replace = link.get_target().to_string(); - if self.loaded_packages.contains_key(&replace) && self.skipped_load.contains_key(&replace) { + if self.loaded_packages.contains_key(&replace) + && self.skipped_load.contains_key(&replace) + { let skipped_root_requires = self.get_skipped_root_requires(request, &replace); - if request.get_update_allow_transitive_root_dependencies() || 0 == count(&skipped_root_requires) { + if request.get_update_allow_transitive_root_dependencies() + || 0 == count(&skipped_root_requires) + { self.unlock_package(request, repositories, &replace)?; // the replaced package only needs to be loaded if something else requires it self.mark_package_name_for_loading_if_required(request, &replace); @@ -660,7 +735,11 @@ impl PoolBuilder { for (_k, link) in &package_or_replacer.get_replaces() { if root_requires.contains_key(link.get_target()) { if name != package_or_replacer.get_name() { - matches.push(format!("{} (via replace of {})", package_or_replacer.get_name(), name)); + matches.push(format!( + "{} (via replace of {})", + package_or_replacer.get_name(), + name + )); } else { matches.push(package_or_replacer.get_name().to_string()); } @@ -735,7 +814,12 @@ impl PoolBuilder { /// Reverts the decision to use a locked package if a partial update with transitive dependencies /// found that this package actually needs to be updated - fn unlock_package(&mut self, request: &mut Request, repositories: &Vec<Box<dyn RepositoryInterface>>, name: &str) -> anyhow::Result<()> { + fn unlock_package( + &mut self, + request: &mut Request, + repositories: &Vec<Box<dyn RepositoryInterface>>, + name: &str, + ) -> anyhow::Result<()> { let skipped: Vec<Box<dyn PackageInterface>> = self .skipped_load .get(name) @@ -744,21 +828,35 @@ impl PoolBuilder { for package_or_replacer in &skipped { // if we unfixed a replaced package name, we also need to unfix the replacer itself // as long as it was not unfixed yet - if package_or_replacer.get_name() != name && self.skipped_load.contains_key(package_or_replacer.get_name()) { + if package_or_replacer.get_name() != name + && self + .skipped_load + .contains_key(package_or_replacer.get_name()) + { let replacer_name = package_or_replacer.get_name().to_string(); if request.get_update_allow_transitive_root_dependencies() - || (!self.is_root_require(request, name) && !self.is_root_require(request, &replacer_name)) + || (!self.is_root_require(request, name) + && !self.is_root_require(request, &replacer_name)) { self.unlock_package(request, repositories, &replacer_name)?; if self.is_root_require(request, &replacer_name) { - self.mark_package_name_for_loading(request, &replacer_name, Box::new(MatchAllConstraint::new())); + self.mark_package_name_for_loading( + request, + &replacer_name, + Box::new(MatchAllConstraint::new()), + ); } else { - let pkgs: Vec<Box<dyn BasePackage>> = self.packages.values().map(|p| p.clone_box()).collect(); + let pkgs: Vec<Box<dyn BasePackage>> = + self.packages.values().map(|p| p.clone_box()).collect(); for loaded_package in &pkgs { let requires = loaded_package.get_requires(); if let Some(req_link) = requires.get(&replacer_name) { - self.mark_package_name_for_loading(request, &replacer_name, req_link.get_constraint()); + self.mark_package_name_for_loading( + request, + &replacer_name, + req_link.get_constraint(), + ); } } } @@ -784,10 +882,15 @@ impl PoolBuilder { self.path_repo_unlocked.shift_remove(name); // remove locked package by this name which was already initialized - let locked_packages: Vec<Box<dyn BasePackage>> = request.get_locked_packages().iter().map(|p| p.clone_box()).collect(); + let locked_packages: Vec<Box<dyn BasePackage>> = request + .get_locked_packages() + .iter() + .map(|p| p.clone_box()) + .collect(); for locked_package in &locked_packages { if locked_package.as_alias_package().is_none() && locked_package.get_name() == name { - let pkgs: Vec<Box<dyn BasePackage>> = self.packages.values().map(|p| p.clone_box()).collect(); + let pkgs: Vec<Box<dyn BasePackage>> = + self.packages.values().map(|p| p.clone_box()).collect(); let index_opt = array_search(&**locked_package, &pkgs, true); if let Some(index) = index_opt { request.unlock_package(&**locked_package); @@ -798,23 +901,47 @@ impl PoolBuilder { // satisfied their requirements // and if this package is replacing another that is required by a locked or fixed package, ensure // that we load that replaced package in case an update to this package removes the replacement - let fixed_or_locked: Vec<Box<dyn BasePackage>> = request.get_fixed_or_locked_packages().iter().map(|p| p.clone_box()).collect(); + let fixed_or_locked: Vec<Box<dyn BasePackage>> = request + .get_fixed_or_locked_packages() + .iter() + .map(|p| p.clone_box()) + .collect(); for fixed_or_locked_package in &fixed_or_locked { - if std::ptr::eq(fixed_or_locked_package.as_ref() as *const _, locked_package.as_ref() as *const _) { + if std::ptr::eq( + fixed_or_locked_package.as_ref() as *const _, + locked_package.as_ref() as *const _, + ) { continue; } - if self.skipped_load.contains_key(fixed_or_locked_package.get_name()) { + if self + .skipped_load + .contains_key(fixed_or_locked_package.get_name()) + { let requires = fixed_or_locked_package.get_requires(); if let Some(req_link) = requires.get(locked_package.get_name()) { - self.mark_package_name_for_loading(request, locked_package.get_name(), req_link.get_constraint()); + self.mark_package_name_for_loading( + request, + locked_package.get_name(), + req_link.get_constraint(), + ); } for (_k, replace) in &locked_package.get_replaces() { - if requires.contains_key(replace.get_target()) && self.skipped_load.contains_key(replace.get_target()) { - self.unlock_package(request, repositories, replace.get_target())?; + if requires.contains_key(replace.get_target()) + && self.skipped_load.contains_key(replace.get_target()) + { + self.unlock_package( + request, + repositories, + replace.get_target(), + )?; // this package is in $requires so no need to call markPackageNameForLoadingIfRequired - self.mark_package_name_for_loading(request, replace.get_target(), replace.get_constraint()); + self.mark_package_name_for_loading( + request, + replace.get_target(), + replace.get_constraint(), + ); } } } @@ -827,21 +954,37 @@ impl PoolBuilder { fn mark_package_name_for_loading_if_required(&mut self, request: &Request, name: &str) { if self.is_root_require(request, name) { - self.mark_package_name_for_loading(request, name, request.get_requires()[name].clone_box()); + self.mark_package_name_for_loading( + request, + name, + request.get_requires()[name].clone_box(), + ); } - let pkgs: Vec<Box<dyn BasePackage>> = self.packages.values().map(|p| p.clone_box()).collect(); + let pkgs: Vec<Box<dyn BasePackage>> = + self.packages.values().map(|p| p.clone_box()).collect(); for package in &pkgs { for (_k, link) in &package.get_requires() { if name == link.get_target() { - self.mark_package_name_for_loading(request, link.get_target(), link.get_constraint()); + self.mark_package_name_for_loading( + request, + link.get_target(), + link.get_constraint(), + ); } } } } - fn remove_loaded_package(&mut self, _request: &Request, repositories: &Vec<Box<dyn RepositoryInterface>>, package: &dyn BasePackage, index: i64) { - let repos_box: Vec<Box<dyn RepositoryInterface>> = repositories.iter().map(|r| r.clone_box()).collect(); + fn remove_loaded_package( + &mut self, + _request: &Request, + repositories: &Vec<Box<dyn RepositoryInterface>>, + package: &dyn BasePackage, + index: i64, + ) { + let repos_box: Vec<Box<dyn RepositoryInterface>> = + repositories.iter().map(|r| r.clone_box()).collect(); let repo_index = match package.get_repository() { Some(repo) => array_search(&*repo, &repos_box, true).unwrap_or(-1), None => -1, @@ -881,7 +1024,11 @@ impl PoolBuilder { let before = microtime(true); let total = count(&pool.get_packages()) as f64; - let pool = self.pool_optimizer.as_mut().unwrap().optimize(request, pool); + let pool = self + .pool_optimizer + .as_mut() + .unwrap() + .optimize(request, pool); let filtered = total - (count(&pool.get_packages()) as f64); @@ -890,7 +1037,10 @@ impl PoolBuilder { } self.io.write_with_verbosity( - &sprintf("Pool optimizer completed in %.3f seconds", &[(microtime(true) - before).into()]), + &sprintf( + "Pool optimizer completed in %.3f seconds", + &[(microtime(true) - before).into()], + ), true, IOInterface::VERY_VERBOSE, ); @@ -910,7 +1060,12 @@ impl PoolBuilder { pool } - fn run_security_advisory_filter(&mut self, pool: Pool, repositories: &Vec<Box<dyn RepositoryInterface>>, request: &Request) -> Pool { + fn run_security_advisory_filter( + &mut self, + pool: Pool, + repositories: &Vec<Box<dyn RepositoryInterface>>, + request: &Request, + ) -> Pool { if self.security_advisory_pool_filter.is_none() { return pool; } @@ -920,7 +1075,11 @@ impl PoolBuilder { let before = microtime(true); let total = count(&pool.get_packages()) as f64; - let pool = self.security_advisory_pool_filter.as_mut().unwrap().filter(pool, repositories, request); + let pool = self.security_advisory_pool_filter.as_mut().unwrap().filter( + pool, + repositories, + request, + ); let filtered = total - (count(&pool.get_packages()) as f64); @@ -929,7 +1088,10 @@ impl PoolBuilder { } self.io.write_with_verbosity( - &sprintf("Security advisory pool filter completed in %.3f seconds", &[(microtime(true) - before).into()]), + &sprintf( + "Security advisory pool filter completed in %.3f seconds", + &[(microtime(true) - before).into()], + ), true, IOInterface::VERY_VERBOSE, ); diff --git a/crates/shirabe/src/dependency_resolver/pool_optimizer.rs b/crates/shirabe/src/dependency_resolver/pool_optimizer.rs index 94b6771..b71af14 100644 --- a/crates/shirabe/src/dependency_resolver/pool_optimizer.rs +++ b/crates/shirabe/src/dependency_resolver/pool_optimizer.rs @@ -4,7 +4,7 @@ use std::any::Any; use anyhow::Result; use indexmap::IndexMap; -use shirabe_php_shim::{implode, ksort, spl_object_hash, LogicException, PhpMixed}; +use shirabe_php_shim::{LogicException, PhpMixed, implode, ksort, spl_object_hash}; use shirabe_semver::compiling_matcher::CompilingMatcher; use shirabe_semver::constraint::constraint::Constraint; use shirabe_semver::constraint::constraint_interface::ConstraintInterface; @@ -28,7 +28,8 @@ pub struct PoolOptimizer { irremovable_packages: IndexMap<i64, bool>, /// @var array<string, array<string, ConstraintInterface>> - require_constraints_per_package: IndexMap<String, IndexMap<String, Box<dyn ConstraintInterface>>>, + require_constraints_per_package: + IndexMap<String, IndexMap<String, Box<dyn ConstraintInterface>>>, /// @var array<string, array<string, ConstraintInterface>> conflict_constraints_per_package: @@ -104,10 +105,7 @@ impl PoolOptimizer { // Extract requested package requirements for (require, constraint) in request.get_requires() { // TODO(phase-b): clone Box<dyn ConstraintInterface> - self.extract_require_constraints_per_package( - require, - todo!("constraint.clone_box()"), - ); + self.extract_require_constraints_per_package(require, todo!("constraint.clone_box()")); } // First pass over all packages to extract information and mark package constraints irremovable @@ -131,9 +129,7 @@ impl PoolOptimizer { // Keep track of alias packages for every package so if either the alias or aliased is kept // we keep the others as they are a unit of packages really - if let Some(alias_pkg) = - (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() - { + if let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() { self.aliases_per_package .entry(alias_pkg.get_alias_of().id) .or_insert_with(Vec::new) @@ -162,7 +158,9 @@ impl PoolOptimizer { continue; } - let constraint = irremovable_package_constraints.get(package.get_name()).unwrap(); + let constraint = irremovable_package_constraints + .get(package.get_name()) + .unwrap(); if CompilingMatcher::r#match( constraint.as_ref(), Constraint::OP_EQ, @@ -175,9 +173,7 @@ impl PoolOptimizer { fn mark_package_irremovable(&mut self, package: &BasePackage) { self.irremovable_packages.insert(package.id, true); - if let Some(alias_pkg) = - (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() - { + if let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() { // recursing here so aliasesPerPackage for the aliasOf can be checked // and all its aliases marked as irremovable as well self.mark_package_irremovable(alias_pkg.get_alias_of()); @@ -279,8 +275,7 @@ impl PoolOptimizer { package.get_version(), ) { // Use the same hash part as the regular require hash because that's what the replacement does - group_hash_parts - .push(format!("require:{}", link.get_constraint())); + group_hash_parts.push(format!("require:{}", link.get_constraint())); } } } @@ -294,8 +289,7 @@ impl PoolOptimizer { Constraint::OP_EQ, package.get_version(), ) { - group_hash_parts - .push(format!("conflict:{}", conflict_constraint)); + group_hash_parts.push(format!("conflict:{}", conflict_constraint)); } } } @@ -350,7 +344,10 @@ impl PoolOptimizer { literals.push(package.id); } - for preferred_literal in self.policy.select_preferred_packages(pool, literals.clone()) { + for preferred_literal in self + .policy + .select_preferred_packages(pool, literals.clone()) + { self.keep_package( &pool.literal_to_package(preferred_literal), &identical_definitions_per_package, @@ -392,7 +389,10 @@ impl PoolOptimizer { // However, the majority of projects are going to specify their constraints already pretty // much in the best variant possible. In other words, we'd be wasting time here and it would actually hurt // performance more than the additional few packages that could be filtered out would benefit the process. - subhash.insert(link.get_target().to_string(), link.get_constraint().to_string()); + subhash.insert( + link.get_target().to_string(), + link.get_constraint().to_string(), + ); } // Sort for best result @@ -442,9 +442,7 @@ impl PoolOptimizer { self.packages_to_remove.shift_remove(&package.id); - if let Some(alias_pkg) = - (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() - { + if let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() { // recursing here so aliasesPerPackage for the aliasOf can be checked // and all its aliases marked to be kept as well self.keep_package( @@ -467,7 +465,9 @@ impl PoolOptimizer { let pkg = if let Some(alias_pkg) = (pkg.as_any() as &dyn Any).downcast_ref::<AliasPackage>() { - if alias_pkg.get_pretty_version() == VersionParser::DEFAULT_BRANCH_ALIAS { + if alias_pkg.get_pretty_version() + == VersionParser::DEFAULT_BRANCH_ALIAS + { alias_pkg.get_alias_of().clone_box() } else { pkg.clone_box() @@ -498,9 +498,7 @@ impl PoolOptimizer { // record all the versions of the package group so we can list them later in Problem output for name in alias_package.get_names(false) { - if let Some(per_name) = - package_identical_definition_lookup.get(&alias_package.id) - { + if let Some(per_name) = package_identical_definition_lookup.get(&alias_package.id) { if let Some(package_group_pointers) = per_name.get(&name) { let package_group = identical_definitions_per_package .get(&name) diff --git a/crates/shirabe/src/dependency_resolver/problem.rs b/crates/shirabe/src/dependency_resolver/problem.rs index 0108487..50b651d 100644 --- a/crates/shirabe/src/dependency_resolver/problem.rs +++ b/crates/shirabe/src/dependency_resolver/problem.rs @@ -5,9 +5,9 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter; use shirabe_php_shim::{ - defined, extension_loaded, implode, in_array, php_to_string, phpversion, sprintf, - spl_object_hash, str_replace, str_starts_with, stripos, strpos, strtolower, substr, - substr_count, version_compare, LogicException, PhpMixed, + LogicException, PhpMixed, defined, extension_loaded, implode, in_array, php_to_string, + phpversion, spl_object_hash, sprintf, str_replace, str_starts_with, stripos, strpos, + strtolower, substr, substr_count, version_compare, }; use shirabe_semver::constraint::constraint::Constraint; use shirabe_semver::constraint::constraint_interface::ConstraintInterface; @@ -134,10 +134,7 @@ impl Problem { fn get_sortable_string(&self, pool: &Pool, rule: &Rule) -> String { match rule.get_reason() { - Rule::RULE_ROOT_REQUIRE => rule - .get_reason_data() - .as_array() - .unwrap()["packageName"] + Rule::RULE_ROOT_REQUIRE => rule.get_reason_data().as_array().unwrap()["packageName"] .as_string() .unwrap() .to_string(), @@ -302,12 +299,9 @@ impl Problem { }; if versions_list.len() > 1 { // remove the s from requires/conflicts to correct grammar - let message_var = Preg::replace( - r"{^(%s%s (?:require|conflict))s}", - "$1", - message, - ) - .unwrap_or(message.clone()); + let message_var = + Preg::replace(r"{^(%s%s (?:require|conflict))s}", "$1", message) + .unwrap_or(message.clone()); result.push(sprintf( &message_var, &[ @@ -331,7 +325,11 @@ impl Problem { } } - format!("\n{}- {}", indent, implode(&format!("\n{}- ", indent), &result)) + format!( + "\n{}- {}", + indent, + implode(&format!("\n{}- ", indent), &result) + ) } pub fn is_caused_by_lock( @@ -585,7 +583,13 @@ impl Problem { ), format!( "found {}. The # character in branch names is replaced by a + character. Make sure to require it as \"{}\".", - Self::get_package_list(&packages, is_verbose, Some(pool), constraint, false), + Self::get_package_list( + &packages, + is_verbose, + Some(pool), + constraint, + false + ), str_replace("#", "+", &c.get_pretty_string()) ), ); @@ -602,8 +606,7 @@ impl Problem { let filtered: Vec<&Box<dyn PackageInterface>> = packages .iter() .filter(|p| { - root_reqs[package_name] - .matches(&Constraint::new("==", &p.get_version())) + root_reqs[package_name].matches(&Constraint::new("==", &p.get_version())) }) .collect(); if filtered.len() == 0 { @@ -615,7 +618,13 @@ impl Problem { ), format!( "found {} but {} with your root composer.json require ({}).", - Self::get_package_list(&packages, is_verbose, Some(pool), constraint, false), + Self::get_package_list( + &packages, + is_verbose, + Some(pool), + constraint, + false + ), if Self::has_multiple_names(&packages) { "these conflict" } else { @@ -646,7 +655,13 @@ impl Problem { ), format!( "found {} but {} with your temporary update constraint ({}:{}).", - Self::get_package_list(&packages, is_verbose, Some(pool), constraint, false), + Self::get_package_list( + &packages, + is_verbose, + Some(pool), + constraint, + false + ), if Self::has_multiple_names(&packages) { "these conflict" } else { @@ -664,9 +679,7 @@ impl Problem { let fixed_constraint = Constraint::new("==", &lp.get_version()); let filtered: Vec<&Box<dyn PackageInterface>> = packages .iter() - .filter(|p| { - fixed_constraint.matches(&Constraint::new("==", &p.get_version())) - }) + .filter(|p| fixed_constraint.matches(&Constraint::new("==", &p.get_version()))) .collect(); if filtered.len() == 0 { return ( @@ -677,7 +690,13 @@ impl Problem { ), format!( "found {} but the package is fixed to {} (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.", - Self::get_package_list(&packages, is_verbose, Some(pool), constraint, false), + Self::get_package_list( + &packages, + is_verbose, + Some(pool), + constraint, + false + ), lp.get_pretty_version() ), ); @@ -698,7 +717,13 @@ impl Problem { ), format!( "found {} in the lock file but not in remote repositories, make sure you avoid updating this package to keep the one from the lock file.", - Self::get_package_list(&packages, is_verbose, Some(pool), constraint, false) + Self::get_package_list( + &packages, + is_verbose, + Some(pool), + constraint, + false + ) ), ); } @@ -712,7 +737,13 @@ impl Problem { ), format!( "found {} but these were not loaded, because they are abandoned and you configured \"block-abandoned\" to true in your \"audit\" config.", - Self::get_package_list(&packages, is_verbose, Some(pool), constraint, false) + Self::get_package_list( + &packages, + is_verbose, + Some(pool), + constraint, + false + ) ), ); } @@ -781,7 +812,13 @@ impl Problem { ), format!( "found {} but these were not loaded, because they are affected by security advisories (\"{}\"). Go to https://packagist.org/security-advisories/ to find advisory details. To ignore the advisories, add them to the audit \"ignore\" config. To turn the feature off entirely, you can set \"block-insecure\" to false in your \"audit\" config.", - Self::get_package_list(&packages, is_verbose, Some(pool), constraint, false), + Self::get_package_list( + &packages, + is_verbose, + Some(pool), + constraint, + false + ), implode("\", \"", &advisories_list) ), ); @@ -900,9 +937,8 @@ impl Problem { let top_package = all_repos_packages.first(); if let Some(tp) = top_package { if tp.is_root_package_interface() { - suffix = - " See https://getcomposer.org/dep-on-root for details and assistance." - .to_string(); + suffix = " See https://getcomposer.org/dep-on-root for details and assistance." + .to_string(); } } @@ -1019,7 +1055,9 @@ impl Problem { let mut prepared_strings: Vec<String> = Vec::new(); for (name, mut package) in prepared { // remove the implicit default branch alias to avoid cruft in the display - if package.versions.contains_key(VersionParser::DEFAULT_BRANCH_ALIAS) + if package + .versions + .contains_key(VersionParser::DEFAULT_BRANCH_ALIAS) && has_default_branch.contains_key(&name) { package @@ -1222,7 +1260,13 @@ impl Problem { ), format!( "satisfiable by {} from {} but {} {} is the root package and cannot be modified. See https://getcomposer.org/dep-on-root for details and assistance.", - Self::get_package_list(&next_repo_packages, is_verbose, Some(pool), constraint, false), + Self::get_package_list( + &next_repo_packages, + is_verbose, + Some(pool), + constraint, + false + ), next_repo.get_repo_name(), top_package.get_pretty_name(), top_package.get_pretty_version() @@ -1237,7 +1281,13 @@ impl Problem { let mut suggestion = format!( "Make sure you either fix the {} or avoid updating this package to keep the one present in the lock file ({}).", reason, - Self::get_package_list(&next_repo_packages, is_verbose, Some(pool), constraint, false) + Self::get_package_list( + &next_repo_packages, + is_verbose, + Some(pool), + constraint, + false + ) ); // symlinked path repos cannot be locked so do not suggest keeping it locked if next_repo_packages[0].get_dist_type() == "path" { @@ -1260,7 +1310,13 @@ impl Problem { ), format!( "found {} but {} not match your {} and {} therefore not installable. {}", - Self::get_package_list(higher_repo_packages, is_verbose, Some(pool), constraint, false), + Self::get_package_list( + higher_repo_packages, + is_verbose, + Some(pool), + constraint, + false + ), if singular { "it does" } else { "these do" }, reason, if singular { "is" } else { "are" }, @@ -1277,10 +1333,26 @@ impl Problem { ), format!( "satisfiable by {} from {} but {} from {} has higher repository priority. The packages from the higher priority repository do not match your {} and are therefore not installable. That repository is canonical so the lower priority repo's packages are not installable. See https://getcomposer.org/repoprio for details and assistance.", - Self::get_package_list(&next_repo_packages, is_verbose, Some(pool), constraint, false), + Self::get_package_list( + &next_repo_packages, + is_verbose, + Some(pool), + constraint, + false + ), next_repo.get_repo_name(), - Self::get_package_list(higher_repo_packages, is_verbose, Some(pool), constraint, false), - higher_repo_packages.first().unwrap().get_repository().get_repo_name(), + Self::get_package_list( + higher_repo_packages, + is_verbose, + Some(pool), + constraint, + false + ), + higher_repo_packages + .first() + .unwrap() + .get_repository() + .get_repo_name(), reason ), ) @@ -1313,7 +1385,10 @@ impl Problem { "{} or {}", implode( ", ", - &versions[..versions.len() - 1].iter().cloned().collect::<Vec<_>>() + &versions[..versions.len() - 1] + .iter() + .cloned() + .collect::<Vec<_>>() ), last ) @@ -1343,7 +1418,11 @@ impl Problem { if providers.len() > 0 { let provider_count = providers.len() as i64; let slice = if provider_count > max_providers + 1 { - providers.iter().take(max_providers as usize).cloned().collect::<Vec<_>>() + providers + .iter() + .take(max_providers as usize) + .cloned() + .collect::<Vec<_>>() } else { providers.clone() }; diff --git a/crates/shirabe/src/dependency_resolver/request.rs b/crates/shirabe/src/dependency_resolver/request.rs index 9aef975..e1f8c1e 100644 --- a/crates/shirabe/src/dependency_resolver/request.rs +++ b/crates/shirabe/src/dependency_resolver/request.rs @@ -1,7 +1,7 @@ //! ref: composer/src/Composer/DependencyResolver/Request.php use indexmap::IndexMap; -use shirabe_php_shim::{spl_object_hash, strtolower, LogicException}; +use shirabe_php_shim::{LogicException, spl_object_hash, strtolower}; use shirabe_semver::constraint::constraint_interface::ConstraintInterface; use shirabe_semver::constraint::match_all_constraint::MatchAllConstraint; diff --git a/crates/shirabe/src/dependency_resolver/rule.rs b/crates/shirabe/src/dependency_resolver/rule.rs index 8c015b4..1ba1dd7 100644 --- a/crates/shirabe/src/dependency_resolver/rule.rs +++ b/crates/shirabe/src/dependency_resolver/rule.rs @@ -5,8 +5,8 @@ use std::any::Any; use anyhow::Result; use indexmap::IndexMap; use shirabe_php_shim::{ - abs, array_filter, array_keys, array_shift, array_values, implode, is_object, LogicException, - PhpMixed, + LogicException, PhpMixed, abs, array_filter, array_keys, array_shift, array_values, implode, + is_object, }; use shirabe_semver::constraint::constraint::Constraint; use shirabe_semver::constraint::constraint_interface::ConstraintInterface; @@ -156,7 +156,9 @@ impl Rule { if pool.is_unacceptable_fixed_or_locked_package(p) { return true; } - if !link.get_constraint().matches(&Constraint::new("=", p.get_version())) + if !link + .get_constraint() + .matches(&Constraint::new("=", p.get_version())) { return true; } @@ -173,7 +175,11 @@ impl Rule { } if self.get_reason() == Self::RULE_ROOT_REQUIRE { - if let ReasonData::RootRequire { package_name, constraint } = self.get_reason_data() { + if let ReasonData::RootRequire { + package_name, + constraint, + } = self.get_reason_data() + { if PlatformRepository::is_platform_package(package_name) { return false; } @@ -254,9 +260,10 @@ impl Rule { let reason_data = self.get_reason_data(); let (package_name, constraint): (&str, &dyn ConstraintInterface) = match reason_data { - ReasonData::RootRequire { package_name, constraint } => { - (package_name.as_str(), constraint.as_ref()) - } + ReasonData::RootRequire { + package_name, + constraint, + } => (package_name.as_str(), constraint.as_ref()), _ => return String::new(), }; @@ -273,7 +280,9 @@ impl Rule { let packages_non_alias: Vec<Box<BasePackage>> = packages .iter() .filter(|p| { - (p.as_any() as &dyn Any).downcast_ref::<AliasPackage>().is_none() + (p.as_any() as &dyn Any) + .downcast_ref::<AliasPackage>() + .is_none() }) .map(|p| p.clone_box()) .collect(); @@ -293,7 +302,13 @@ impl Rule { "Root composer.json requires {} {} -> satisfiable by {}.", package_name, constraint.get_pretty_string(), - self.format_packages_unique(pool, packages, is_verbose, Some(constraint), false), + self.format_packages_unique( + pool, + packages, + is_verbose, + Some(constraint), + false + ), ) } @@ -335,8 +350,11 @@ impl Rule { // swap literals if they are not in the right order with package2 being the conflicter if link.get_source() == package1.get_name() { std::mem::swap(&mut package1, &mut package2); - conflict_target = - format!("{} {}", package1.get_pretty_name(), link.get_pretty_constraint().unwrap_or("")); + conflict_target = format!( + "{} {}", + package1.get_pretty_name(), + link.get_pretty_constraint().unwrap_or("") + ); } // if the conflict is not directly against the package but something it provides/replaces, @@ -347,14 +365,16 @@ impl Rule { for provide in package1.get_provides().values() { if provide.get_target() == link.get_target() { provide_type = Some("provides"); - provided = Some(provide.get_pretty_constraint().unwrap_or("").to_string()); + provided = + Some(provide.get_pretty_constraint().unwrap_or("").to_string()); break; } } for replace in package1.get_replaces().values() { if replace.get_target() == link.get_target() { provide_type = Some("replaces"); - provided = Some(replace.get_pretty_constraint().unwrap_or("").to_string()); + provided = + Some(replace.get_pretty_constraint().unwrap_or("").to_string()); break; } } @@ -371,7 +391,11 @@ impl Rule { } } - format!("{} conflicts with {}.", package2.get_pretty_string(), conflict_target) + format!( + "{} conflicts with {}.", + package2.get_pretty_string(), + conflict_target + ) } r if r == Self::RULE_PACKAGE_REQUIRES => { @@ -488,14 +512,18 @@ impl Rule { return format!( "Only one of these can be installed: {}. {}", - self.format_packages_unique_from_literals(pool, &literals, is_verbose, None, true), + self.format_packages_unique_from_literals( + pool, &literals, is_verbose, None, true + ), reason, ); } format!( "You can only install one version of a package, so only one of these can be installed: {}.", - self.format_packages_unique_from_literals(pool, &literals, is_verbose, None, true), + self.format_packages_unique_from_literals( + pool, &literals, is_verbose, None, true + ), ) } r if r == Self::RULE_LEARNED => { @@ -512,7 +540,11 @@ impl Rule { let group = if installed_map.contains_key(&package.id) { if *literal > 0 { "keep" } else { "remove" } } else { - if *literal > 0 { "install" } else { "don't install" } + if *literal > 0 { + "install" + } else { + "don't install" + } }; groups @@ -604,7 +636,13 @@ impl Rule { packages.push(package); } - Problem::get_package_list(packages, is_verbose, pool, constraint, use_removed_version_group) + Problem::get_package_list( + packages, + is_verbose, + pool, + constraint, + use_removed_version_group, + ) } /// Helper for cases where literals come as int IDs (PHP supports both via union). @@ -620,13 +658,17 @@ impl Rule { for literal in literals { packages.push(pool.literal_to_package(*literal).clone_box()); } - Problem::get_package_list(packages, is_verbose, pool, constraint, use_removed_version_group) + Problem::get_package_list( + packages, + is_verbose, + pool, + constraint, + use_removed_version_group, + ) } fn deduplicate_default_branch_alias(&self, package: Box<BasePackage>) -> Box<BasePackage> { - if let Some(alias_pkg) = - (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() - { + if let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() { if alias_pkg.get_pretty_version() == VersionParser::DEFAULT_BRANCH_ALIAS { return alias_pkg.get_alias_of().clone_box(); } diff --git a/crates/shirabe/src/dependency_resolver/rule2_literals.rs b/crates/shirabe/src/dependency_resolver/rule2_literals.rs index 9d3aefc..e86c93d 100644 --- a/crates/shirabe/src/dependency_resolver/rule2_literals.rs +++ b/crates/shirabe/src/dependency_resolver/rule2_literals.rs @@ -12,7 +12,12 @@ pub struct Rule2Literals { } impl Rule2Literals { - pub fn new(literal1: i64, literal2: i64, reason: shirabe_php_shim::PhpMixed, reason_data: shirabe_php_shim::PhpMixed) -> Self { + pub fn new( + literal1: i64, + literal2: i64, + reason: shirabe_php_shim::PhpMixed, + reason_data: shirabe_php_shim::PhpMixed, + ) -> Self { let (literal1, literal2) = if literal1 < literal2 { (literal1, literal2) } else { @@ -52,7 +57,11 @@ impl Rule2Literals { } pub fn to_string(&self) -> String { - let prefix = if self.inner.is_disabled() { "disabled(" } else { "(" }; + let prefix = if self.inner.is_disabled() { + "disabled(" + } else { + "(" + }; format!("{}{}|{})", prefix, self.literal1, self.literal2) } } diff --git a/crates/shirabe/src/dependency_resolver/rule_set.rs b/crates/shirabe/src/dependency_resolver/rule_set.rs index 36009d9..4fc7928 100644 --- a/crates/shirabe/src/dependency_resolver/rule_set.rs +++ b/crates/shirabe/src/dependency_resolver/rule_set.rs @@ -53,7 +53,8 @@ impl RuleSet { return Err(OutOfBoundsException { message: format!("Unknown rule type: {}", r#type), code: 0, - }.into()); + } + .into()); } let hash = rule.get_hash(); @@ -66,13 +67,19 @@ impl RuleSet { } } - self.rules.entry(r#type).or_insert_with(Vec::new).push(rule.clone()); + self.rules + .entry(r#type) + .or_insert_with(Vec::new) + .push(rule.clone()); rule.set_type(r#type); self.rule_by_id.insert(self.next_rule_id, rule.clone()); self.next_rule_id += 1; - self.rules_by_hash.entry(hash).or_insert_with(Vec::new).push(rule); + self.rules_by_hash + .entry(hash) + .or_insert_with(Vec::new) + .push(rule); Ok(()) } diff --git a/crates/shirabe/src/dependency_resolver/rule_set_generator.rs b/crates/shirabe/src/dependency_resolver/rule_set_generator.rs index a2066ef..9c803d6 100644 --- a/crates/shirabe/src/dependency_resolver/rule_set_generator.rs +++ b/crates/shirabe/src/dependency_resolver/rule_set_generator.rs @@ -12,8 +12,8 @@ use crate::dependency_resolver::policy_interface::PolicyInterface; use crate::dependency_resolver::pool::Pool; use crate::dependency_resolver::request::Request; use crate::dependency_resolver::rule::Rule; -use crate::dependency_resolver::rule2_literals::Rule2Literals; use crate::dependency_resolver::rule_set::RuleSet; +use crate::dependency_resolver::rule2_literals::Rule2Literals; use crate::filter::platform_requirement_filter::ignore_list_platform_requirement_filter::IgnoreListPlatformRequirementFilter; use crate::filter::platform_requirement_filter::platform_requirement_filter_factory::PlatformRequirementFilterFactory; use crate::filter::platform_requirement_filter::platform_requirement_filter_interface::PlatformRequirementFilterInterface; @@ -65,7 +65,11 @@ impl RuleSetGenerator { literals.push(provider.get_id()); } - Some(GenericRule::new(literals, PhpMixed::Int(reason), reason_data)) + Some(GenericRule::new( + literals, + PhpMixed::Int(reason), + reason_data, + )) } /// Creates a rule to install at least one of a set of packages. @@ -126,7 +130,9 @@ impl RuleSetGenerator { reason_data, )) } else { - Rule::from(MultiConflictRule::new(literals, PhpMixed::Int(reason), reason_data).unwrap()) + Rule::from( + MultiConflictRule::new(literals, PhpMixed::Int(reason), reason_data).unwrap(), + ) } } @@ -202,8 +208,8 @@ impl RuleSetGenerator { if platform_requirement_filter.is_ignored(link.get_target()) { continue; } else if let Some(ignore_list_filter) = (platform_requirement_filter as &dyn Any) - .downcast_ref::<IgnoreListPlatformRequirementFilter>() - { + .downcast_ref::<IgnoreListPlatformRequirementFilter>( + ) { constraint = ignore_list_filter .filter_constraint(link.get_target(), constraint, true) .unwrap_or(constraint); @@ -230,11 +236,8 @@ impl RuleSetGenerator { &mut self, platform_requirement_filter: &dyn PlatformRequirementFilterInterface, ) { - let packages: Vec<Box<dyn PackageInterface>> = self - .added_map - .values() - .map(|p| p.clone_box()) - .collect(); + let packages: Vec<Box<dyn PackageInterface>> = + self.added_map.values().map(|p| p.clone_box()).collect(); for package in &packages { for link in package.get_conflicts().values() { @@ -247,8 +250,8 @@ impl RuleSetGenerator { if platform_requirement_filter.is_ignored(link.get_target()) { continue; } else if let Some(ignore_list_filter) = (platform_requirement_filter as &dyn Any) - .downcast_ref::<IgnoreListPlatformRequirementFilter>() - { + .downcast_ref::<IgnoreListPlatformRequirementFilter>( + ) { constraint = ignore_list_filter .filter_constraint(link.get_target(), constraint, false) .unwrap_or(constraint); @@ -286,11 +289,8 @@ impl RuleSetGenerator { for (name, packages) in names_packages { if packages.len() > 1 { let reason = Rule::RULE_PACKAGE_SAME_NAME; - let rule = self.create_multi_conflict_rule( - &packages, - reason, - PhpMixed::String(name), - ); + let rule = + self.create_multi_conflict_rule(&packages, reason, PhpMixed::String(name)); self.add_rule(RuleSet::TYPE_PACKAGE, Some(rule)); } } @@ -309,15 +309,13 @@ impl RuleSetGenerator { } // otherwise, looks like a bug - return Err(anyhow::anyhow!( - shirabe_php_shim::LogicException { - message: format!( - "Fixed package {} was not added to solver pool.", - package.get_pretty_string() - ), - code: 0, - } - )); + return Err(anyhow::anyhow!(shirabe_php_shim::LogicException { + message: format!( + "Fixed package {} was not added to solver pool.", + package.get_pretty_string() + ), + code: 0, + })); } self.add_rules_for_package(package.clone_box(), platform_requirement_filter); @@ -340,8 +338,8 @@ impl RuleSetGenerator { if platform_requirement_filter.is_ignored(package_name) { continue; } else if let Some(ignore_list_filter) = (platform_requirement_filter as &dyn Any) - .downcast_ref::<IgnoreListPlatformRequirementFilter>() - { + .downcast_ref::<IgnoreListPlatformRequirementFilter>( + ) { constraint = ignore_list_filter .filter_constraint(package_name, constraint, true) .unwrap_or(constraint); @@ -406,8 +404,8 @@ impl RuleSetGenerator { request: &Request, platform_requirement_filter: Option<Box<dyn PlatformRequirementFilterInterface>>, ) -> anyhow::Result<RuleSet> { - let platform_requirement_filter = - platform_requirement_filter.unwrap_or_else(PlatformRequirementFilterFactory::ignore_nothing); + let platform_requirement_filter = platform_requirement_filter + .unwrap_or_else(PlatformRequirementFilterFactory::ignore_nothing); self.add_rules_for_request(request, &*platform_requirement_filter)?; diff --git a/crates/shirabe/src/dependency_resolver/rule_set_iterator.rs b/crates/shirabe/src/dependency_resolver/rule_set_iterator.rs index e2b67f4..f66a277 100644 --- a/crates/shirabe/src/dependency_resolver/rule_set_iterator.rs +++ b/crates/shirabe/src/dependency_resolver/rule_set_iterator.rs @@ -1,7 +1,7 @@ //! ref: composer/src/Composer/DependencyResolver/RuleSetIterator.php -use indexmap::IndexMap; use crate::dependency_resolver::rule::Rule; +use indexmap::IndexMap; /// Implements PHP \Iterator over a grouped rule set. #[derive(Debug)] diff --git a/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs b/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs index 5428ca1..80c9bee 100644 --- a/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs +++ b/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs @@ -27,24 +27,38 @@ impl RuleWatchGraph { return; } - if (node.get_rule().as_any() as &dyn Any).downcast_ref::<MultiConflictRule>().is_none() { + if (node.get_rule().as_any() as &dyn Any) + .downcast_ref::<MultiConflictRule>() + .is_none() + { for literal in [node.watch1, node.watch2] { if !self.watch_chains.contains_key(&literal) { self.watch_chains.insert(literal, RuleWatchChain::new()); } - self.watch_chains.get_mut(&literal).unwrap().unshift(node.clone()); + self.watch_chains + .get_mut(&literal) + .unwrap() + .unshift(node.clone()); } } else { for literal in node.get_rule().get_literals() { if !self.watch_chains.contains_key(&literal) { self.watch_chains.insert(literal, RuleWatchChain::new()); } - self.watch_chains.get_mut(&literal).unwrap().unshift(node.clone()); + self.watch_chains + .get_mut(&literal) + .unwrap() + .unshift(node.clone()); } } } - pub fn propagate_literal(&mut self, decided_literal: i64, level: i64, decisions: &mut Decisions) -> Option<Box<dyn Rule>> { + pub fn propagate_literal( + &mut self, + decided_literal: i64, + level: i64, + decisions: &mut Decisions, + ) -> Option<Box<dyn Rule>> { let literal = -decided_literal; if !self.watch_chains.contains_key(&literal) { @@ -56,13 +70,17 @@ impl RuleWatchGraph { chain.rewind(); while chain.valid() { let node = chain.current(); - if (node.get_rule().as_any() as &dyn Any).downcast_ref::<MultiConflictRule>().is_none() { + if (node.get_rule().as_any() as &dyn Any) + .downcast_ref::<MultiConflictRule>() + .is_none() + { let other_watch = node.get_other_watch(literal); if !node.get_rule().is_disabled() && !decisions.satisfy(other_watch) { let rule_literals = node.get_rule().get_literals(); - let alternative_literals: Vec<i64> = rule_literals.into_iter() + let alternative_literals: Vec<i64> = rule_literals + .into_iter() .filter(|&rule_literal| { literal != rule_literal && other_watch != rule_literal @@ -107,6 +125,9 @@ impl RuleWatchGraph { node.move_watch(from_literal, to_literal); self.watch_chains.get_mut(&from_literal).unwrap().remove(); - self.watch_chains.get_mut(&to_literal).unwrap().unshift(node); + self.watch_chains + .get_mut(&to_literal) + .unwrap() + .unshift(node); } } diff --git a/crates/shirabe/src/dependency_resolver/rule_watch_node.rs b/crates/shirabe/src/dependency_resolver/rule_watch_node.rs index 4161268..12dd83f 100644 --- a/crates/shirabe/src/dependency_resolver/rule_watch_node.rs +++ b/crates/shirabe/src/dependency_resolver/rule_watch_node.rs @@ -25,7 +25,11 @@ impl RuleWatchNode { let watch1 = if literal_count > 0 { literals[0] } else { 0 }; let watch2 = if literal_count > 1 { literals[1] } else { 0 }; - Self { watch1, watch2, rule } + Self { + watch1, + watch2, + rule, + } } pub fn watch2_on_highest(&mut self, decisions: &Decisions) { diff --git a/crates/shirabe/src/dependency_resolver/security_advisory_pool_filter.rs b/crates/shirabe/src/dependency_resolver/security_advisory_pool_filter.rs index 8c30f60..8caf6bd 100644 --- a/crates/shirabe/src/dependency_resolver/security_advisory_pool_filter.rs +++ b/crates/shirabe/src/dependency_resolver/security_advisory_pool_filter.rs @@ -1,8 +1,5 @@ //! ref: composer/src/Composer/DependencyResolver/SecurityAdvisoryPoolFilter.php -use indexmap::IndexMap; -use shirabe_php_shim::PhpMixed; -use shirabe_semver::constraint::constraint::Constraint; use crate::advisory::audit_config::AuditConfig; use crate::advisory::auditor::Auditor; use crate::dependency_resolver::pool::Pool; @@ -11,6 +8,9 @@ use crate::package::package_interface::PackageInterface; use crate::repository::platform_repository::PlatformRepository; use crate::repository::repository_interface::RepositoryInterface; use crate::repository::repository_set::RepositorySet; +use indexmap::IndexMap; +use shirabe_php_shim::PhpMixed; +use shirabe_semver::constraint::constraint::Constraint; #[derive(Debug)] pub struct SecurityAdvisoryPoolFilter { @@ -20,10 +20,18 @@ pub struct SecurityAdvisoryPoolFilter { impl SecurityAdvisoryPoolFilter { pub fn new(auditor: Auditor, audit_config: AuditConfig) -> Self { - Self { auditor, audit_config } + Self { + auditor, + audit_config, + } } - pub fn filter(&self, pool: Pool, repositories: Vec<Box<dyn RepositoryInterface>>, request: &Request) -> Pool { + pub fn filter( + &self, + pool: Pool, + repositories: Vec<Box<dyn RepositoryInterface>>, + request: &Request, + ) -> Pool { if !self.audit_config.block_insecure { return pool; } @@ -35,15 +43,23 @@ impl SecurityAdvisoryPoolFilter { let mut packages_for_advisories: Vec<Box<dyn PackageInterface>> = vec![]; for package in pool.get_packages() { - if !package.is_root() && !PlatformRepository::is_platform_package(package.get_name()) && !request.is_locked_package(package.as_ref()) { + if !package.is_root() + && !PlatformRepository::is_platform_package(package.get_name()) + && !request.is_locked_package(package.as_ref()) + { packages_for_advisories.push(package); } } // all_advisories: ['advisories' => array<string, array<PartialSecurityAdvisory|SecurityAdvisory>>, ...] - let mut all_advisories: IndexMap<String, PhpMixed> = repo_set.get_matching_security_advisories(&packages_for_advisories, true, true); - if self.auditor.needs_complete_advisory_load(&all_advisories["advisories"], &self.audit_config.ignore_list_for_blocking) { - all_advisories = repo_set.get_matching_security_advisories(&packages_for_advisories, false, true); + let mut all_advisories: IndexMap<String, PhpMixed> = + repo_set.get_matching_security_advisories(&packages_for_advisories, true, true); + if self.auditor.needs_complete_advisory_load( + &all_advisories["advisories"], + &self.audit_config.ignore_list_for_blocking, + ) { + all_advisories = + repo_set.get_matching_security_advisories(&packages_for_advisories, false, true); } // advisory_map: array<string, array<PartialSecurityAdvisory|SecurityAdvisory>> @@ -51,20 +67,35 @@ impl SecurityAdvisoryPoolFilter { &all_advisories["advisories"], &self.audit_config.ignore_list_for_blocking, &self.audit_config.ignore_severity_for_blocking, - )["advisories"].clone().into(); + )["advisories"] + .clone() + .into(); let mut packages: Vec<Box<dyn PackageInterface>> = vec![]; // security_removed_versions: array<string, array<string, array<PartialSecurityAdvisory|SecurityAdvisory>>> - let mut security_removed_versions: IndexMap<String, IndexMap<String, Vec<PhpMixed>>> = IndexMap::new(); + let mut security_removed_versions: IndexMap<String, IndexMap<String, Vec<PhpMixed>>> = + IndexMap::new(); // abandoned_removed_versions: array<string, array<string, string>> - let mut abandoned_removed_versions: IndexMap<String, IndexMap<String, String>> = IndexMap::new(); + let mut abandoned_removed_versions: IndexMap<String, IndexMap<String, String>> = + IndexMap::new(); for package in pool.get_packages() { - if self.audit_config.block_abandoned && !self.auditor.filter_abandoned_packages(vec![package.as_ref()], &self.audit_config.ignore_abandoned_for_blocking).is_empty() { + if self.audit_config.block_abandoned + && !self + .auditor + .filter_abandoned_packages( + vec![package.as_ref()], + &self.audit_config.ignore_abandoned_for_blocking, + ) + .is_empty() + { for package_name in package.get_names(false) { abandoned_removed_versions .entry(package_name) .or_default() - .insert(package.get_version().to_string(), package.get_pretty_version().to_string()); + .insert( + package.get_version().to_string(), + package.get_pretty_version().to_string(), + ); } continue; } @@ -75,7 +106,10 @@ impl SecurityAdvisoryPoolFilter { security_removed_versions .entry(package_name) .or_default() - .insert(package.get_version().to_string(), matching_advisories.clone()); + .insert( + package.get_version().to_string(), + matching_advisories.clone(), + ); } continue; } @@ -83,10 +117,21 @@ impl SecurityAdvisoryPoolFilter { packages.push(package); } - Pool::new(packages, pool.get_unacceptable_fixed_or_locked_packages(), pool.get_all_removed_versions(), pool.get_all_removed_versions_by_package(), security_removed_versions, abandoned_removed_versions) + Pool::new( + packages, + pool.get_unacceptable_fixed_or_locked_packages(), + pool.get_all_removed_versions(), + pool.get_all_removed_versions_by_package(), + security_removed_versions, + abandoned_removed_versions, + ) } - fn get_matching_advisories(&self, package: &dyn PackageInterface, advisory_map: &IndexMap<String, Vec<PhpMixed>>) -> Vec<PhpMixed> { + fn get_matching_advisories( + &self, + package: &dyn PackageInterface, + advisory_map: &IndexMap<String, Vec<PhpMixed>>, + ) -> Vec<PhpMixed> { if package.is_dev() { return vec![]; } diff --git a/crates/shirabe/src/dependency_resolver/solver.rs b/crates/shirabe/src/dependency_resolver/solver.rs index 76de9de..388cccd 100644 --- a/crates/shirabe/src/dependency_resolver/solver.rs +++ b/crates/shirabe/src/dependency_resolver/solver.rs @@ -2,7 +2,9 @@ use indexmap::IndexMap; -use shirabe_php_shim::{array_pop, array_shift, array_unshift, microtime, spl_object_hash, sprintf, PhpMixed}; +use shirabe_php_shim::{ + PhpMixed, array_pop, array_shift, array_unshift, microtime, spl_object_hash, sprintf, +}; use shirabe_semver::constraint::constraint_interface::ConstraintInterface; use crate::dependency_resolver::decisions::Decisions; @@ -183,8 +185,8 @@ impl Solver { continue; } else if let Some(ignore_filter) = platform_requirement_filter .as_any() - .downcast_ref::<IgnoreListPlatformRequirementFilter>() - { + .downcast_ref::<IgnoreListPlatformRequirementFilter>( + ) { constraint = ignore_filter.filter_constraint(package_name, constraint); } @@ -201,8 +203,7 @@ impl Solver { ); // TODO(phase-b): store the constraint inside reason_data; PhpMixed needs to // accept a `dyn ConstraintInterface` wrapper. - reason_data - .insert("constraint".to_string(), PhpMixed::Null); + reason_data.insert("constraint".to_string(), PhpMixed::Null); problem.add_rule(Rule::generic(GenericRule::new( Vec::new(), PhpMixed::Int(Rule::RULE_ROOT_REQUIRE), @@ -233,7 +234,8 @@ impl Solver { true, <dyn IOInterface>::DEBUG, ); - let mut rule_set_generator = RuleSetGenerator::new(self.policy.clone_box(), self.pool.clone()); + let mut rule_set_generator = + RuleSetGenerator::new(self.policy.clone_box(), self.pool.clone()); self.rules = rule_set_generator.get_rules_for(request, platform_requirement_filter.as_ref())?; drop(rule_set_generator); @@ -242,8 +244,7 @@ impl Solver { self.watch_graph = RuleWatchGraph::new(); for rule in self.rules.iter() { - self.watch_graph - .insert(RuleWatchNode::new(rule.clone()))?; + self.watch_graph.insert(RuleWatchNode::new(rule.clone()))?; } // make decisions based on root require/fix assertions @@ -298,11 +299,9 @@ impl Solver { .at_offset(self.propagate_index as usize) .clone(); - let conflict = self.watch_graph.propagate_literal( - decision.0, - level, - &mut self.decisions, - ); + let conflict = + self.watch_graph + .propagate_literal(decision.0, level, &mut self.decisions); self.propagate_index += 1; @@ -333,9 +332,7 @@ impl Solver { self.propagate_index = self.decisions.count() as i64; } - while !self.branches.is_empty() - && self.branches[self.branches.len() - 1].1 >= level - { + while !self.branches.is_empty() && self.branches[self.branches.len() - 1].1 >= level { // PHP: array_pop($this->branches) self.branches.pop(); } @@ -388,8 +385,7 @@ impl Solver { self.rules .add(new_rule.clone().into(), RuleSet::TYPE_LEARNED)?; - self.learned_why - .insert(spl_object_hash(&new_rule), why); + self.learned_why.insert(spl_object_hash(&new_rule), why); let mut rule_node = RuleWatchNode::new(new_rule.clone().into()); rule_node.watch2_on_highest(&self.decisions); @@ -408,9 +404,11 @@ impl Solver { rule: Rule, ) -> anyhow::Result<i64> { // choose best package to install from decisionQueue - let mut literals = - self.policy - .select_preferred_packages(&self.pool, decision_queue, rule.get_required_package()); + let mut literals = self.policy.select_preferred_packages( + &self.pool, + decision_queue, + rule.get_required_package(), + ); let selected_literal = array_shift::<i64>(&mut literals); @@ -422,11 +420,7 @@ impl Solver { self.set_propagate_learn(level, selected_literal, rule) } - fn analyze( - &mut self, - level: i64, - rule: Rule, - ) -> anyhow::Result<(i64, i64, GenericRule, i64)> { + fn analyze(&mut self, level: i64, rule: Rule) -> anyhow::Result<(i64, i64, GenericRule, i64)> { let analyzed_rule = rule.clone(); let mut rule = rule; let mut rule_level = 1_i64; diff --git a/crates/shirabe/src/dependency_resolver/solver_problems_exception.rs b/crates/shirabe/src/dependency_resolver/solver_problems_exception.rs index 37cbbfe..ddc8746 100644 --- a/crates/shirabe/src/dependency_resolver/solver_problems_exception.rs +++ b/crates/shirabe/src/dependency_resolver/solver_problems_exception.rs @@ -50,10 +50,18 @@ impl SolverProblemsException { for problem in &self.problems { problems.push(format!( "{}\n", - problem.get_pretty_string(repository_set, request, pool, is_verbose, &installed_map, &self.learned_pool) + problem.get_pretty_string( + repository_set, + request, + pool, + is_verbose, + &installed_map, + &self.learned_pool + ) )); missing_extensions.extend(self.get_extension_problems(problem.get_reasons())); - is_caused_by_lock = is_caused_by_lock || problem.is_caused_by_lock(repository_set, request, pool); + is_caused_by_lock = + is_caused_by_lock || problem.is_caused_by_lock(repository_set, request, pool); } let mut i = 1; @@ -66,7 +74,9 @@ impl SolverProblemsException { } let mut hints: Vec<String> = Vec::new(); - if !is_dev_extraction && (text.contains("could not be found") || text.contains("no matching package found")) { + if !is_dev_extraction + && (text.contains("could not be found") || text.contains("no matching package found")) + { hints.push("Potential causes:\n - A typo in the package name\n - The package is not available in a stable-enough version according to your minimum-stability setting\n see <https://getcomposer.org/doc/04-schema.md#minimum-stability> for more details.\n - It's a private package and you forgot to add a custom repository to find it\n\nRead <https://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.".to_string()); } @@ -74,11 +84,16 @@ impl SolverProblemsException { hints.push(self.create_extension_hint(&missing_extensions)); } - if is_caused_by_lock && !is_dev_extraction && !request.get_update_allow_transitive_root_dependencies() { + if is_caused_by_lock + && !is_dev_extraction + && !request.get_update_allow_transitive_root_dependencies() + { hints.push("Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions.".to_string()); } - if text.contains("found composer-plugin-api[2.0.0] but it does not match") && text.contains("- ocramius/package-versions") { + if text.contains("found composer-plugin-api[2.0.0] but it does not match") + && text.contains("- ocramius/package-versions") + { hints.push("<warning>ocramius/package-versions only provides support for Composer 2 in 1.8+, which requires PHP 7.4.</warning>\nIf you can not upgrade PHP you can require <info>composer/package-versions-deprecated</info> to resolve this with PHP 7.0+.".to_string()); } @@ -118,7 +133,9 @@ impl SolverProblemsException { .collect::<Vec<_>>() .join(" "); - let mut text = "To enable extensions, verify that they are enabled in your .ini files:\n - ".to_string(); + let mut text = + "To enable extensions, verify that they are enabled in your .ini files:\n - " + .to_string(); text.push_str(&paths.join("\n - ")); text.push_str("\nYou can also run `php --ini` in a terminal to see which files are used by PHP in CLI mode."); text.push_str(&format!("\nAlternatively, you can run Composer with `{}` to temporarily ignore these required extensions.", ignore_extensions_arguments)); diff --git a/crates/shirabe/src/dependency_resolver/transaction.rs b/crates/shirabe/src/dependency_resolver/transaction.rs index 1b7ed02..7c93114 100644 --- a/crates/shirabe/src/dependency_resolver/transaction.rs +++ b/crates/shirabe/src/dependency_resolver/transaction.rs @@ -4,8 +4,8 @@ use std::any::Any; use indexmap::IndexMap; use shirabe_php_shim::{ - array_filter, array_intersect, array_keys, array_pop, array_unshift, spl_object_hash, strcmp, - uasort, PhpMixed, + PhpMixed, array_filter, array_intersect, array_keys, array_pop, array_unshift, spl_object_hash, + strcmp, uasort, }; use crate::dependency_resolver::operation::install_operation::InstallOperation; @@ -150,9 +150,7 @@ impl Transaction { visited.insert(spl_object_hash(package.as_ref()), true); stack.push(package.clone_box()); - if let Some(alias) = - (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() - { + if let Some(alias) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() { stack.push(alias.get_alias_of().clone_box()); } else { for link in package.get_requires().values() { @@ -170,15 +168,14 @@ impl Transaction { .downcast_ref::<AliasPackage>() .is_some() { - let alias_key = - format!("{}::{}", package.get_name(), package.get_version()); + let alias_key = format!("{}::{}", package.get_name(), package.get_version()); if present_alias_map.contains_key(&alias_key) { remove_alias_map.shift_remove(&alias_key); } else { // TODO(phase-b): MarkAliasInstalledOperation::new expects AliasPackage by value - operations.push(Box::new(MarkAliasInstalledOperation::new( - todo!("package as AliasPackage by value"), - ))); + operations.push(Box::new(MarkAliasInstalledOperation::new(todo!( + "package as AliasPackage by value" + )))); } } else if let Some(source) = present_package_map.get(package.get_name()) { // do we need to update? @@ -187,9 +184,8 @@ impl Transaction { // TODO(phase-b): downcast to CompletePackageInterface trait object let package_is_complete = false; let present_is_complete = false; - let abandoned_or_replacement_changed = package_is_complete - && present_is_complete - && { + let abandoned_or_replacement_changed = + package_is_complete && present_is_complete && { // PHP: $package->isAbandoned() !== $presentPackageMap[$package->getName()]->isAbandoned() // || $package->getReplacementPackage() !== $presentPackageMap[$package->getName()]->getReplacementPackage() todo!("compare abandoned/replacement across CompletePackageInterface") @@ -221,9 +217,9 @@ impl Transaction { } for (_name_version, _package) in remove_alias_map { // TODO(phase-b): MarkAliasUninstalledOperation::new expects AliasPackage by value - operations.push(Box::new(MarkAliasUninstalledOperation::new( - todo!("package as AliasPackage by value"), - ))); + operations.push(Box::new(MarkAliasUninstalledOperation::new(todo!( + "package as AliasPackage by value" + )))); } let operations = self.move_plugins_to_front(operations); @@ -368,9 +364,7 @@ impl Transaction { || package.get_type() == "composer-installer"; // is this a plugin or a dependency of a plugin? - if is_plugin - || array_intersect(&package.get_names(true), &plugin_requires).len() > 0 - { + if is_plugin || array_intersect(&package.get_names(true), &plugin_requires).len() > 0 { // get the package's requires, but filter out any platform requirements let requires: Vec<String> = array_filter( &array_keys(&package.get_requires()), diff --git a/crates/shirabe/src/downloader/archive_downloader.rs b/crates/shirabe/src/downloader/archive_downloader.rs index 9220410..59704e3 100644 --- a/crates/shirabe/src/downloader/archive_downloader.rs +++ b/crates/shirabe/src/downloader/archive_downloader.rs @@ -1,14 +1,16 @@ //! ref: composer/src/Composer/Downloader/ArchiveDownloader.php -use anyhow::Result; -use indexmap::IndexMap; -use shirabe_php_shim::{bin2hex, file_exists, is_dir, random_bytes, realpath, RuntimeException, DIRECTORY_SEPARATOR}; -use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; -use shirabe_external_packages::symfony::component::finder::finder::Finder; use crate::dependency_resolver::operation::install_operation::InstallOperation; use crate::downloader::file_downloader::FileDownloader; use crate::package::package_interface::PackageInterface; use crate::util::platform::Platform; +use anyhow::Result; +use indexmap::IndexMap; +use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; +use shirabe_external_packages::symfony::component::finder::finder::Finder; +use shirabe_php_shim::{ + DIRECTORY_SEPARATOR, RuntimeException, bin2hex, file_exists, is_dir, random_bytes, realpath, +}; #[derive(Debug)] pub struct ArchiveDownloader { @@ -17,21 +19,43 @@ pub struct ArchiveDownloader { } impl ArchiveDownloader { - pub fn prepare(&mut self, r#type: &str, package: &dyn PackageInterface, path: &str, prev_package: Option<&dyn PackageInterface>) -> Result<Box<dyn PromiseInterface>> { + pub fn prepare( + &mut self, + r#type: &str, + package: &dyn PackageInterface, + path: &str, + prev_package: Option<&dyn PackageInterface>, + ) -> Result<Box<dyn PromiseInterface>> { self.cleanup_executed.remove(package.get_name()); self.inner.prepare(r#type, package, path, prev_package) } - pub fn cleanup(&mut self, r#type: &str, package: &dyn PackageInterface, path: &str, prev_package: Option<&dyn PackageInterface>) -> Result<Box<dyn PromiseInterface>> { - self.cleanup_executed.insert(package.get_name().to_string(), true); + pub fn cleanup( + &mut self, + r#type: &str, + package: &dyn PackageInterface, + path: &str, + prev_package: Option<&dyn PackageInterface>, + ) -> Result<Box<dyn PromiseInterface>> { + self.cleanup_executed + .insert(package.get_name().to_string(), true); self.inner.cleanup(r#type, package, path, prev_package) } - pub fn install(&mut self, package: &dyn PackageInterface, path: &str, output: bool) -> Result<Box<dyn PromiseInterface>> { + pub fn install( + &mut self, + package: &dyn PackageInterface, + path: &str, + output: bool, + ) -> Result<Box<dyn PromiseInterface>> { if output { - self.inner.io.write_error(&format!(" - {}{}", InstallOperation::format(package, false), self.get_install_operation_appendix(package, path))); + self.inner.io.write_error(&format!( + " - {}{}", + InstallOperation::format(package, false), + self.get_install_operation_appendix(package, path) + )); } let vendor_dir = self.inner.config.get("vendor-dir"); @@ -39,7 +63,12 @@ impl ArchiveDownloader { // clean up the target directory, unless it contains the vendor dir, as the vendor dir contains // the archive to be extracted. This is the case when installing with create-project in the current directory // but in that case we ensure the directory is empty already in ProjectInstaller so no need to empty it here. - if !self.inner.filesystem.normalize_path(&vendor_dir).contains(&self.inner.filesystem.normalize_path(&format!("{}{}", path, DIRECTORY_SEPARATOR))) { + if !self.inner.filesystem.normalize_path(&vendor_dir).contains( + &self + .inner + .filesystem + .normalize_path(&format!("{}{}", path, DIRECTORY_SEPARATOR)), + ) { self.inner.filesystem.empty_directory(path); } @@ -58,7 +87,9 @@ impl ArchiveDownloader { self.inner.add_cleanup_path(package, path); } - self.inner.filesystem.ensure_directory_exists(&temporary_dir); + self.inner + .filesystem + .ensure_directory_exists(&temporary_dir); let file_name = self.inner.get_file_name(package, path); let filesystem = &self.inner.filesystem; @@ -187,11 +218,20 @@ impl ArchiveDownloader { )) } - pub fn get_install_operation_appendix(&self, _package: &dyn PackageInterface, _path: &str) -> &str { + pub fn get_install_operation_appendix( + &self, + _package: &dyn PackageInterface, + _path: &str, + ) -> &str { ": Extracting archive" } - pub(crate) fn extract(&self, _package: &dyn PackageInterface, _file: &str, _path: &str) -> Result<Box<dyn PromiseInterface>> { + pub(crate) fn extract( + &self, + _package: &dyn PackageInterface, + _file: &str, + _path: &str, + ) -> Result<Box<dyn PromiseInterface>> { todo!() } } diff --git a/crates/shirabe/src/downloader/download_manager.rs b/crates/shirabe/src/downloader/download_manager.rs index e787689..ff453f3 100644 --- a/crates/shirabe/src/downloader/download_manager.rs +++ b/crates/shirabe/src/downloader/download_manager.rs @@ -5,9 +5,9 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use shirabe_php_shim::{ - array_keys, array_reverse, array_shift, dirname, get_class, implode, in_array, preg_quote, - rtrim, sprintf, str_replace, strtolower, usort, InvalidArgumentException, LogicException, - PhpMixed, RuntimeException, + InvalidArgumentException, LogicException, PhpMixed, RuntimeException, array_keys, + array_reverse, array_shift, dirname, get_class, implode, in_array, preg_quote, rtrim, sprintf, + str_replace, strtolower, usort, }; use crate::downloader::downloader_interface::DownloaderInterface; @@ -200,7 +200,8 @@ impl DownloadManager { prev_package: Option<&dyn PackageInterface>, ) -> Result<Box<dyn PromiseInterface>> { let target_dir = self.normalize_target_dir(target_dir); - self.filesystem.ensure_directory_exists(&dirname(&target_dir)); + self.filesystem + .ensure_directory_exists(&dirname(&target_dir)); let mut sources = self.get_available_sources(package, prev_package)?; @@ -241,7 +242,8 @@ impl DownloadManager { // PHP closure handleError: rethrow if not RuntimeException or if IrrecoverableDownloadException // TODO(phase-b): downcast for instanceof checks let is_runtime: bool = todo!("e instanceof RuntimeException"); - let is_irrecoverable: bool = todo!("e instanceof IrrecoverableDownloadException"); + let is_irrecoverable: bool = + todo!("e instanceof IrrecoverableDownloadException"); if is_runtime && !is_irrecoverable { if sources.is_empty() { return Err(e); @@ -268,7 +270,9 @@ impl DownloadManager { // PHP: $result->then(static fn ($res) => $res, $handleError); // TODO(phase-b): chain $handleError as the rejection handler on the promise - let res = result.then(Box::new(move |res: PhpMixed| -> Result<PhpMixed> { Ok(res) })); + let res = result.then(Box::new(move |res: PhpMixed| -> Result<PhpMixed> { + Ok(res) + })); return Ok(res); } @@ -358,10 +362,7 @@ impl DownloadManager { return Err(e); } self.io.write_error( - PhpMixed::String(format!( - "<error> Update failed ({})</error>", - e, - )), + PhpMixed::String(format!("<error> Update failed ({})</error>", e,)), true, IOInterface::NORMAL, ); @@ -503,11 +504,7 @@ impl DownloadManager { { let prev_source_owned = prev_source.unwrap_or("").to_string(); usort(&mut sources, move |a: &String, b: &String| -> i64 { - if *a == prev_source_owned { - -1 - } else { - 1 - } + if *a == prev_source_owned { -1 } else { 1 } }); return Ok(sources); diff --git a/crates/shirabe/src/downloader/downloader_interface.rs b/crates/shirabe/src/downloader/downloader_interface.rs index c78a094..423df69 100644 --- a/crates/shirabe/src/downloader/downloader_interface.rs +++ b/crates/shirabe/src/downloader/downloader_interface.rs @@ -7,15 +7,45 @@ use crate::package::package_interface::PackageInterface; pub trait DownloaderInterface { fn get_installation_source(&self) -> String; - fn download(&self, package: &dyn PackageInterface, path: &str, prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Box<dyn PromiseInterface>>; + fn download( + &self, + package: &dyn PackageInterface, + path: &str, + prev_package: Option<&dyn PackageInterface>, + ) -> anyhow::Result<Box<dyn PromiseInterface>>; - fn prepare(&self, r#type: &str, package: &dyn PackageInterface, path: &str, prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Box<dyn PromiseInterface>>; + fn prepare( + &self, + r#type: &str, + package: &dyn PackageInterface, + path: &str, + prev_package: Option<&dyn PackageInterface>, + ) -> anyhow::Result<Box<dyn PromiseInterface>>; - fn install(&self, package: &dyn PackageInterface, path: &str) -> anyhow::Result<Box<dyn PromiseInterface>>; + fn install( + &self, + package: &dyn PackageInterface, + path: &str, + ) -> anyhow::Result<Box<dyn PromiseInterface>>; - fn update(&self, initial: &dyn PackageInterface, target: &dyn PackageInterface, path: &str) -> anyhow::Result<Box<dyn PromiseInterface>>; + fn update( + &self, + initial: &dyn PackageInterface, + target: &dyn PackageInterface, + path: &str, + ) -> anyhow::Result<Box<dyn PromiseInterface>>; - fn remove(&self, package: &dyn PackageInterface, path: &str) -> anyhow::Result<Box<dyn PromiseInterface>>; + fn remove( + &self, + package: &dyn PackageInterface, + path: &str, + ) -> anyhow::Result<Box<dyn PromiseInterface>>; - fn cleanup(&self, r#type: &str, package: &dyn PackageInterface, path: &str, prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Box<dyn PromiseInterface>>; + fn cleanup( + &self, + r#type: &str, + package: &dyn PackageInterface, + path: &str, + prev_package: Option<&dyn PackageInterface>, + ) -> anyhow::Result<Box<dyn PromiseInterface>>; } diff --git a/crates/shirabe/src/downloader/file_downloader.rs b/crates/shirabe/src/downloader/file_downloader.rs index 7ddc60a..5b002ff 100644 --- a/crates/shirabe/src/downloader/file_downloader.rs +++ b/crates/shirabe/src/downloader/file_downloader.rs @@ -7,11 +7,11 @@ use std::sync::{LazyLock, Mutex}; use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use shirabe_external_packages::react::promise::resolve as react_promise_resolve; use shirabe_php_shim::{ - array_search, array_shift, file_exists, filesize, get_class, hash, hash_file, in_array, - is_dir, is_executable, parse_url, pathinfo, realpath, rtrim, spl_object_hash, strlen, strpos, - strtr, trim, umask, usleep, InvalidArgumentException, PhpMixed, RuntimeException, Silencer, - UnexpectedValueException, DIRECTORY_SEPARATOR, PATHINFO_BASENAME, PATHINFO_EXTENSION, - PHP_URL_PATH, + DIRECTORY_SEPARATOR, InvalidArgumentException, PATHINFO_BASENAME, PATHINFO_EXTENSION, + PHP_URL_PATH, PhpMixed, RuntimeException, Silencer, UnexpectedValueException, array_search, + array_shift, file_exists, filesize, get_class, hash, hash_file, in_array, is_dir, + is_executable, parse_url, pathinfo, realpath, rtrim, spl_object_hash, strlen, strpos, strtr, + trim, umask, usleep, }; use crate::cache::Cache; @@ -106,10 +106,7 @@ impl FileDownloader { this.io.write_error("Running cache garbage collection"); this.cache.as_mut().unwrap().gc( this.config.get("cache-files-ttl").as_int().unwrap_or(0), - this.config - .get("cache-files-maxsize") - .as_int() - .unwrap_or(0), + this.config.get("cache-files-maxsize").as_int().unwrap_or(0), ); } @@ -180,13 +177,15 @@ impl DownloaderInterface for FileDownloader { let _ = PluginEvents::PRE_FILE_DOWNLOAD; let _ = PluginEvents::POST_FILE_DOWNLOAD; - todo!("phase-b: orchestrate download/accept/reject closures and call download() returning a PromiseInterface") + todo!( + "phase-b: orchestrate download/accept/reject closures and call download() returning a PromiseInterface" + ) } /// @inheritDoc fn prepare( &mut self, - _r#type: &str, + _type: &str, _package: &dyn PackageInterface, _path: &str, _prev_package: Option<&dyn PackageInterface>, @@ -197,7 +196,7 @@ impl DownloaderInterface for FileDownloader { /// @inheritDoc fn cleanup( &mut self, - _r#type: &str, + _type: &str, package: &dyn PackageInterface, path: &str, _prev_package: Option<&dyn PackageInterface>, @@ -226,7 +225,11 @@ impl DownloaderInterface for FileDownloader { vendor_dir.clone(), ]; - if let Some(paths) = self.additional_cleanup_paths.get(package.get_name()).cloned() { + if let Some(paths) = self + .additional_cleanup_paths + .get(package.get_name()) + .cloned() + { for path_to_clean in &paths { self.filesystem.remove(path_to_clean)?; } @@ -337,9 +340,7 @@ impl DownloaderInterface for FileDownloader { // TODO(phase-b): chain `.then(|result| if !result { throw RuntimeException })` let _ = path; - todo!( - "phase-b: chain promise.then(|result| {{ if !result {{ throw RuntimeException }} }})" - ) + todo!("phase-b: chain promise.then(|result| {{ if !result {{ throw RuntimeException }} }})") } } @@ -365,7 +366,8 @@ impl ChangeReportInterface for FileDownloader { .remove_directory(&format!("{}_compare", target_dir), false)?; } - let promise = self.download(package, &format!("{}_compare", target_dir), None, false)?; + let promise = + self.download(package, &format!("{}_compare", target_dir), None, false)?; promise.then_with( None, Some(Box::new(|ex: PhpMixed| { @@ -419,7 +421,11 @@ impl ChangeReportInterface for FileDownloader { let output = trim(&output, None); - Ok(if strlen(&output) > 0 { Some(output) } else { None }) + Ok(if strlen(&output) > 0 { + Some(output) + } else { + None + }) } } diff --git a/crates/shirabe/src/downloader/fossil_downloader.rs b/crates/shirabe/src/downloader/fossil_downloader.rs index 47ad973..a686177 100644 --- a/crates/shirabe/src/downloader/fossil_downloader.rs +++ b/crates/shirabe/src/downloader/fossil_downloader.rs @@ -1,11 +1,11 @@ //! ref: composer/src/Composer/Downloader/FossilDownloader.php +use crate::downloader::vcs_downloader::VcsDownloader; +use crate::package::package_interface::PackageInterface; use anyhow::Result; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use shirabe_php_shim::RuntimeException; -use crate::downloader::vcs_downloader::VcsDownloader; -use crate::package::package_interface::PackageInterface; #[derive(Debug)] pub struct FossilDownloader { @@ -29,26 +29,48 @@ impl FossilDownloader { path: String, url: String, ) -> Result<Box<dyn PromiseInterface>> { - self.inner.config.prohibit_url_by_config(&url, &self.inner.io)?; + self.inner + .config + .prohibit_url_by_config(&url, &self.inner.io)?; let repo_file = format!("{}.fossil", path); let real_path = shirabe_php_shim::realpath(&path); - self.inner.io.write_error(&format!("Cloning {}", package.get_source_reference().unwrap_or_default())); + self.inner.io.write_error(&format!( + "Cloning {}", + package.get_source_reference().unwrap_or_default() + )); let mut output = String::new(); self.execute( - vec!["fossil".to_string(), "clone".to_string(), "--".to_string(), url, repo_file.clone()], + vec![ + "fossil".to_string(), + "clone".to_string(), + "--".to_string(), + url, + repo_file.clone(), + ], None, &mut output, )?; self.execute( - vec!["fossil".to_string(), "open".to_string(), "--nested".to_string(), "--".to_string(), repo_file], + vec![ + "fossil".to_string(), + "open".to_string(), + "--nested".to_string(), + "--".to_string(), + repo_file, + ], real_path.clone(), &mut output, )?; self.execute( - vec!["fossil".to_string(), "update".to_string(), "--".to_string(), package.get_source_reference().unwrap_or_default()], + vec![ + "fossil".to_string(), + "update".to_string(), + "--".to_string(), + package.get_source_reference().unwrap_or_default(), + ], real_path, &mut output, )?; @@ -63,9 +85,14 @@ impl FossilDownloader { path: String, url: String, ) -> Result<Box<dyn PromiseInterface>> { - self.inner.config.prohibit_url_by_config(&url, &self.inner.io)?; + self.inner + .config + .prohibit_url_by_config(&url, &self.inner.io)?; - self.inner.io.write_error(&format!(" Updating to {}", target.get_source_reference().unwrap_or_default())); + self.inner.io.write_error(&format!( + " Updating to {}", + target.get_source_reference().unwrap_or_default() + )); if !self.has_metadata_repository(&path) { return Err(RuntimeException { @@ -85,7 +112,12 @@ impl FossilDownloader { &mut output, )?; self.execute( - vec!["fossil".to_string(), "up".to_string(), "--".to_string(), target.get_source_reference().unwrap_or_default()], + vec![ + "fossil".to_string(), + "up".to_string(), + "--".to_string(), + target.get_source_reference().unwrap_or_default(), + ], real_path, &mut output, )?; @@ -93,7 +125,11 @@ impl FossilDownloader { Ok(shirabe_external_packages::react::promise::resolve(None)) } - pub fn get_local_changes(&self, _package: &dyn PackageInterface, path: String) -> Option<String> { + pub fn get_local_changes( + &self, + _package: &dyn PackageInterface, + path: String, + ) -> Option<String> { if !self.has_metadata_repository(&path) { return None; } @@ -119,11 +155,16 @@ impl FossilDownloader { let mut output = String::new(); self.execute( vec![ - "fossil".to_string(), "timeline".to_string(), - "-t".to_string(), "ci".to_string(), - "-W".to_string(), "0".to_string(), - "-n".to_string(), "0".to_string(), - "before".to_string(), to_reference.clone(), + "fossil".to_string(), + "timeline".to_string(), + "-t".to_string(), + "ci".to_string(), + "-W".to_string(), + "0".to_string(), + "-n".to_string(), + "0".to_string(), + "before".to_string(), + to_reference.clone(), ], shirabe_php_shim::realpath(&path), &mut output, @@ -149,7 +190,12 @@ impl FossilDownloader { Ok(log) } - fn execute(&self, command: Vec<String>, cwd: Option<String>, output: &mut String) -> Result<()> { + fn execute( + &self, + command: Vec<String>, + cwd: Option<String>, + output: &mut String, + ) -> Result<()> { if self.inner.process.execute(&command, output, cwd) != 0 { return Err(RuntimeException { message: format!( @@ -158,7 +204,8 @@ impl FossilDownloader { self.inner.process.get_error_output() ), code: 0, - }.into()); + } + .into()); } Ok(()) } diff --git a/crates/shirabe/src/downloader/git_downloader.rs b/crates/shirabe/src/downloader/git_downloader.rs index 6957ba1..97d0ee9 100644 --- a/crates/shirabe/src/downloader/git_downloader.rs +++ b/crates/shirabe/src/downloader/git_downloader.rs @@ -6,8 +6,8 @@ use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::react::promise; use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use shirabe_php_shim::{ - array_map, basename, dirname, implode, in_array, is_dir, preg_quote, realpath, rtrim, sprintf, - strlen, strpos, substr, trim, version_compare, PhpMixed, RuntimeException, + PhpMixed, RuntimeException, array_map, basename, dirname, implode, in_array, is_dir, + preg_quote, realpath, rtrim, sprintf, strlen, strpos, substr, trim, version_compare, }; use crate::cache::Cache; @@ -42,12 +42,7 @@ impl GitDownloader { fs: Option<Filesystem>, ) -> Self { let inner = VcsDownloader::new(io, config, process, fs); - let git_util = GitUtil::new( - &*inner.io, - &inner.config, - &inner.process, - &inner.filesystem, - ); + let git_util = GitUtil::new(&*inner.io, &inner.config, &inner.process, &inner.filesystem); Self { inner, has_stashed_changes: IndexMap::new(), @@ -73,7 +68,11 @@ impl GitDownloader { let cache_path = format!( "{}/{}/", - self.inner.config.get("cache-vcs-dir").as_string().unwrap_or(""), + self.inner + .config + .get("cache-vcs-dir") + .as_string() + .unwrap_or(""), Preg::replace(r"{[^a-z0-9.]}i", "-", Url::sanitize(url.to_string())), ); let git_version = GitUtil::get_version(&self.inner.process); @@ -134,7 +133,11 @@ impl GitDownloader { let path = self.normalize_path(path); let cache_path = format!( "{}/{}/", - self.inner.config.get("cache-vcs-dir").as_string().unwrap_or(""), + self.inner + .config + .get("cache-vcs-dir") + .as_string() + .unwrap_or(""), Preg::replace(r"{[^a-z0-9.]}i", "-", Url::sanitize(url.to_string())), ); let r#ref = package.get_source_reference().unwrap_or("").to_string(); @@ -157,10 +160,7 @@ impl GitDownloader { ]; let transport_options = package.get_transport_options(); if let Some(git_opts) = transport_options.get("git").and_then(|v| v.as_array()) { - if let Some(single) = git_opts - .get("single_use_clone") - .and_then(|v| v.as_bool()) - { + if let Some(single) = git_opts.get("single_use_clone").and_then(|v| v.as_bool()) { if single { clone_flags = vec![]; } @@ -298,7 +298,11 @@ impl GitDownloader { let cache_path = format!( "{}/{}/", - self.inner.config.get("cache-vcs-dir").as_string().unwrap_or(""), + self.inner + .config + .get("cache-vcs-dir") + .as_string() + .unwrap_or(""), Preg::replace(r"{[^a-z0-9.]}i", "-", Url::sanitize(url.to_string())), ); let r#ref = target.get_source_reference().unwrap_or("").to_string(); @@ -401,7 +405,8 @@ impl GitDownloader { ) == 0 { let origin_match = Preg::is_match_strict_groups(r"{^origin\s+(?P<url>\S+)}m", &output); - let composer_match = Preg::is_match_strict_groups(r"{^composer\s+(?P<url>\S+)}m", &output); + let composer_match = + Preg::is_match_strict_groups(r"{^composer\s+(?P<url>\S+)}m", &output); if let (Some(origin_match), Some(composer_match)) = (origin_match, composer_match) { let origin_url = origin_match.get("url").cloned().unwrap_or_default(); let composer_url = composer_match.get("url").cloned().unwrap_or_default(); @@ -560,11 +565,11 @@ impl GitDownloader { "--".to_string(), ]; let mut output = String::new(); - if self.inner.process.execute( - &command, - &mut output, - Some(path.clone()), - ) != 0 + if self + .inner + .process + .execute(&command, &mut output, Some(path.clone())) + != 0 { // TODO(phase-b): bubble error via Result later panic!( @@ -580,8 +585,7 @@ impl GitDownloader { let output = trim(&output, None); // keep the shortest diff from all remote branches we compare against if unpushed_changes.is_none() - || strlen(&output) - < strlen(unpushed_changes.as_deref().unwrap_or("")) + || strlen(&output) < strlen(unpushed_changes.as_deref().unwrap_or("")) { unpushed_changes = Some(output); } @@ -593,11 +597,7 @@ impl GitDownloader { if unpushed_changes.is_some() && i == 0 { let mut output = String::new(); self.inner.process.execute( - &vec![ - "git".to_string(), - "fetch".to_string(), - "--all".to_string(), - ], + &vec!["git".to_string(), "fetch".to_string(), "--all".to_string()], &mut output, Some(path.clone()), ); @@ -786,9 +786,7 @@ impl GitDownloader { " n - abort the {} and let you manually clean things up", if update { "update" } else { "uninstall" } ))), - Box::new(PhpMixed::String( - " v - view modified files".to_string(), - )), + Box::new(PhpMixed::String(" v - view modified files".to_string())), Box::new(PhpMixed::String( " d - view local modifications (diff)".to_string(), )), @@ -833,11 +831,7 @@ impl GitDownloader { ); let mut output = String::new(); if self.inner.process.execute( - &vec![ - "git".to_string(), - "stash".to_string(), - "pop".to_string(), - ], + &vec!["git".to_string(), "stash".to_string(), "pop".to_string()], &mut output, Some(path.clone()), ) != 0 @@ -886,8 +880,11 @@ impl GitDownloader { // If the non-existent branch is actually the name of a file, the file // is checked out. - let mut branch = - Preg::replace(r"{(?:^dev-|(?:\.x)?-dev$)}i", "", pretty_version.to_string()); + let mut branch = Preg::replace( + r"{(?:^dev-|(?:\.x)?-dev$)}i", + "", + pretty_version.to_string(), + ); // Closure equivalent: $execute = function(array $command) use (&$output, $path) { ... }; // Inlined below at each call site. @@ -910,10 +907,7 @@ impl GitDownloader { if !Preg::is_match(r"{^[a-f0-9]{40}$}", reference).unwrap_or(false) && branches.is_some() && Preg::is_match( - &format!( - "{{^\\s+composer/{}$}}m", - preg_quote(reference, None) - ), + &format!("{{^\\s+composer/{}$}}m", preg_quote(reference, None)), branches.as_deref().unwrap_or(""), ) .unwrap_or(false) @@ -935,18 +929,17 @@ impl GitDownloader { ]; let mut output = String::new(); - let ok1 = self.inner.process.execute( - &command1, - &mut output, - Some(path.to_string()), - ) == 0; + let ok1 = self + .inner + .process + .execute(&command1, &mut output, Some(path.to_string())) + == 0; let ok2 = if ok1 { let mut output = String::new(); - self.inner.process.execute( - &command2, - &mut output, - Some(path.to_string()), - ) == 0 + self.inner + .process + .execute(&command2, &mut output, Some(path.to_string())) + == 0 } else { false }; @@ -960,18 +953,12 @@ impl GitDownloader { // add 'v' in front of the branch if it was stripped when generating the pretty name if branches.is_some() && !Preg::is_match( - &format!( - "{{^\\s+composer/{}$}}m", - preg_quote(&branch, None) - ), + &format!("{{^\\s+composer/{}$}}m", preg_quote(&branch, None)), branches.as_deref().unwrap_or(""), ) .unwrap_or(false) && Preg::is_match( - &format!( - "{{^\\s+composer/v{}$}}m", - preg_quote(&branch, None) - ), + &format!("{{^\\s+composer/v{}$}}m", preg_quote(&branch, None)), branches.as_deref().unwrap_or(""), ) .unwrap_or(false) @@ -985,8 +972,7 @@ impl GitDownloader { branch.clone(), "--".to_string(), ]; - let mut fallback_command: Vec<String> = - vec!["git".to_string(), "checkout".to_string()]; + let mut fallback_command: Vec<String> = vec!["git".to_string(), "checkout".to_string()]; fallback_command.extend(force.clone()); fallback_command.extend(vec![ "-B".to_string(), @@ -1003,28 +989,26 @@ impl GitDownloader { ]; let mut output = String::new(); - let ok_command = self.inner.process.execute( - &command, - &mut output, - Some(path.to_string()), - ) == 0; + let ok_command = + self.inner + .process + .execute(&command, &mut output, Some(path.to_string())) + == 0; let ok_fallback = if !ok_command { let mut output = String::new(); - self.inner.process.execute( - &fallback_command, - &mut output, - Some(path.to_string()), - ) == 0 + self.inner + .process + .execute(&fallback_command, &mut output, Some(path.to_string())) + == 0 } else { false }; let ok_reset = if ok_command || ok_fallback { let mut output = String::new(); - self.inner.process.execute( - &reset_command, - &mut output, - Some(path.to_string()), - ) == 0 + self.inner + .process + .execute(&reset_command, &mut output, Some(path.to_string())) + == 0 } else { false }; @@ -1045,18 +1029,17 @@ impl GitDownloader { ]; { let mut output = String::new(); - let ok1 = self.inner.process.execute( - &command1, - &mut output, - Some(path.to_string()), - ) == 0; + let ok1 = self + .inner + .process + .execute(&command1, &mut output, Some(path.to_string())) + == 0; let ok2 = if ok1 { let mut output = String::new(); - self.inner.process.execute( - &command2, - &mut output, - Some(path.to_string()), - ) == 0 + self.inner + .process + .execute(&command2, &mut output, Some(path.to_string())) + == 0 } else { false }; @@ -1193,11 +1176,7 @@ impl GitDownloader { let path = self.normalize_path(path); let mut output = String::new(); if self.inner.process.execute( - &vec![ - "git".to_string(), - "clean".to_string(), - "-df".to_string(), - ], + &vec!["git".to_string(), "clean".to_string(), "-df".to_string()], &mut output, Some(path.clone()), ) != 0 @@ -1210,11 +1189,7 @@ impl GitDownloader { } let mut output = String::new(); if self.inner.process.execute( - &vec![ - "git".to_string(), - "reset".to_string(), - "--hard".to_string(), - ], + &vec!["git".to_string(), "reset".to_string(), "--hard".to_string()], &mut output, Some(path.clone()), ) != 0 @@ -1263,11 +1238,7 @@ impl GitDownloader { let path = self.normalize_path(path); let mut output = String::new(); if self.inner.process.execute( - &vec![ - "git".to_string(), - "diff".to_string(), - "HEAD".to_string(), - ], + &vec!["git".to_string(), "diff".to_string(), "HEAD".to_string()], &mut output, Some(path.clone()), ) != 0 @@ -1333,4 +1304,3 @@ impl DvcsDownloaderInterface for GitDownloader { GitDownloader::get_unpushed_changes(self, package, &path) } } - diff --git a/crates/shirabe/src/downloader/gzip_downloader.rs b/crates/shirabe/src/downloader/gzip_downloader.rs index 2158126..81fdbed 100644 --- a/crates/shirabe/src/downloader/gzip_downloader.rs +++ b/crates/shirabe/src/downloader/gzip_downloader.rs @@ -1,25 +1,31 @@ //! ref: composer/src/Composer/Downloader/GzipDownloader.php +use crate::downloader::archive_downloader::ArchiveDownloader; +use crate::package::package_interface::PackageInterface; +use crate::util::platform::Platform; use anyhow::Result; use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use shirabe_php_shim::{ - RuntimeException, - extension_loaded, fclose, fopen, fwrite, gzclose, gzopen, gzread, - implode, parse_url, pathinfo, strtr, - DIRECTORY_SEPARATOR, PATHINFO_FILENAME, PHP_URL_PATH, + DIRECTORY_SEPARATOR, PATHINFO_FILENAME, PHP_URL_PATH, RuntimeException, extension_loaded, + fclose, fopen, fwrite, gzclose, gzopen, gzread, implode, parse_url, pathinfo, strtr, }; -use crate::downloader::archive_downloader::ArchiveDownloader; -use crate::package::package_interface::PackageInterface; -use crate::util::platform::Platform; pub struct GzipDownloader { inner: ArchiveDownloader, } impl GzipDownloader { - pub(crate) fn extract(&self, package: &dyn PackageInterface, file: &str, path: &str) -> Result<Box<dyn PromiseInterface>> { + pub(crate) fn extract( + &self, + package: &dyn PackageInterface, + file: &str, + path: &str, + ) -> Result<Box<dyn PromiseInterface>> { let filename = pathinfo( - parse_url(&strtr(&package.get_dist_url().unwrap_or_default(), "\\", "/"), PHP_URL_PATH), + parse_url( + &strtr(&package.get_dist_url().unwrap_or_default(), "\\", "/"), + PHP_URL_PATH, + ), PATHINFO_FILENAME, ); let target_filepath = format!("{}{}{}", path, DIRECTORY_SEPARATOR, filename); @@ -47,7 +53,10 @@ impl GzipDownloader { implode(" ", &command), self.inner.process.get_error_output(), ); - return Err(anyhow::anyhow!(RuntimeException { message: process_error, code: 0 })); + return Err(anyhow::anyhow!(RuntimeException { + message: process_error, + code: 0 + })); } self.extract_using_ext(file, &target_filepath); diff --git a/crates/shirabe/src/downloader/hg_downloader.rs b/crates/shirabe/src/downloader/hg_downloader.rs index 7501fb6..ff06806 100644 --- a/crates/shirabe/src/downloader/hg_downloader.rs +++ b/crates/shirabe/src/downloader/hg_downloader.rs @@ -1,11 +1,11 @@ //! ref: composer/src/Composer/Downloader/HgDownloader.php -use anyhow::Result; -use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; -use shirabe_php_shim::RuntimeException; use crate::downloader::vcs_downloader::VcsDownloader; use crate::package::package_interface::PackageInterface; use crate::util::hg::Hg as HgUtils; +use anyhow::Result; +use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; +use shirabe_php_shim::RuntimeException; #[derive(Debug)] pub struct HgDownloader { @@ -24,7 +24,8 @@ impl HgDownloader { return Err(RuntimeException { message: "hg was not found in your PATH, skipping source download".to_string(), code: 0, - }.into()); + } + .into()); } Ok(shirabe_external_packages::react::promise::resolve(None)) @@ -40,7 +41,13 @@ impl HgDownloader { let path_clone = path.clone(); let clone_command = move |url: String| -> Vec<String> { - vec!["hg".to_string(), "clone".to_string(), "--".to_string(), url, path_clone.clone()] + vec![ + "hg".to_string(), + "clone".to_string(), + "--".to_string(), + url, + path_clone.clone(), + ] }; hg_utils.run_command(clone_command, url, Some(path.clone())); @@ -51,7 +58,12 @@ impl HgDownloader { package.get_source_reference().unwrap_or_default(), ]; let mut ignored_output = String::new(); - if self.inner.process.execute(&command, &mut ignored_output, shirabe_php_shim::realpath(&path)) != 0 { + if self.inner.process.execute( + &command, + &mut ignored_output, + shirabe_php_shim::realpath(&path), + ) != 0 + { return Err(RuntimeException { message: format!( "Failed to execute {}\n\n{}", @@ -59,7 +71,8 @@ impl HgDownloader { self.inner.process.get_error_output() ), code: 0, - }.into()); + } + .into()); } Ok(shirabe_external_packages::react::promise::resolve(None)) @@ -75,7 +88,10 @@ impl HgDownloader { let hg_utils = HgUtils::new(&self.inner.io, &self.inner.config, &self.inner.process); let ref_ = target.get_source_reference().unwrap_or_default(); - self.inner.io.write_error(&format!(" Updating to {}", target.get_source_reference().unwrap_or_default())); + self.inner.io.write_error(&format!( + " Updating to {}", + target.get_source_reference().unwrap_or_default() + )); if !self.has_metadata_repository(path.clone()) { return Err(RuntimeException { @@ -94,14 +110,23 @@ impl HgDownloader { let ref_clone = ref_.clone(); let up_command = move |_url: String| -> Vec<String> { - vec!["hg".to_string(), "up".to_string(), "--".to_string(), ref_clone.clone()] + vec![ + "hg".to_string(), + "up".to_string(), + "--".to_string(), + ref_clone.clone(), + ] }; hg_utils.run_command(up_command, url, Some(path)); Ok(shirabe_external_packages::react::promise::resolve(None)) } - pub fn get_local_changes(&self, package: &dyn PackageInterface, path: String) -> Option<String> { + pub fn get_local_changes( + &self, + package: &dyn PackageInterface, + path: String, + ) -> Option<String> { if !std::path::Path::new(&format!("{}/.hg", path)).is_dir() { return None; } @@ -115,7 +140,11 @@ impl HgDownloader { let output = output.trim().to_string(); - if !output.is_empty() { Some(output) } else { None } + if !output.is_empty() { + Some(output) + } else { + None + } } pub(crate) fn get_commit_logs( @@ -134,7 +163,12 @@ impl HgDownloader { ]; let mut output = String::new(); - if self.inner.process.execute(&command, &mut output, shirabe_php_shim::realpath(&path)) != 0 { + if self + .inner + .process + .execute(&command, &mut output, shirabe_php_shim::realpath(&path)) + != 0 + { return Err(RuntimeException { message: format!( "Failed to execute {}\n\n{}", @@ -142,7 +176,8 @@ impl HgDownloader { self.inner.process.get_error_output() ), code: 0, - }.into()); + } + .into()); } Ok(output) diff --git a/crates/shirabe/src/downloader/mod.rs b/crates/shirabe/src/downloader/mod.rs new file mode 100644 index 0000000..391ae23 --- /dev/null +++ b/crates/shirabe/src/downloader/mod.rs @@ -0,0 +1,23 @@ +pub mod archive_downloader; +pub mod change_report_interface; +pub mod download_manager; +pub mod downloader_interface; +pub mod dvcs_downloader_interface; +pub mod file_downloader; +pub mod filesystem_exception; +pub mod fossil_downloader; +pub mod git_downloader; +pub mod gzip_downloader; +pub mod hg_downloader; +pub mod max_file_size_exceeded_exception; +pub mod path_downloader; +pub mod perforce_downloader; +pub mod phar_downloader; +pub mod rar_downloader; +pub mod svn_downloader; +pub mod tar_downloader; +pub mod transport_exception; +pub mod vcs_capable_downloader_interface; +pub mod vcs_downloader; +pub mod xz_downloader; +pub mod zip_downloader; diff --git a/crates/shirabe/src/downloader/path_downloader.rs b/crates/shirabe/src/downloader/path_downloader.rs index 2ad3312..33e3b45 100644 --- a/crates/shirabe/src/downloader/path_downloader.rs +++ b/crates/shirabe/src/downloader/path_downloader.rs @@ -6,8 +6,8 @@ use shirabe_external_packages::react::promise::promise_interface::PromiseInterfa use shirabe_external_packages::symfony::component::filesystem::exception::io_exception::IOException; use shirabe_external_packages::symfony::component::filesystem::filesystem::Filesystem as SymfonyFilesystem; use shirabe_php_shim::{ - file_exists, function_exists, is_dir, realpath, PhpMixed, RuntimeException, - DIRECTORY_SEPARATOR, PHP_WINDOWS_VERSION_MAJOR, PHP_WINDOWS_VERSION_MINOR, + DIRECTORY_SEPARATOR, PHP_WINDOWS_VERSION_MAJOR, PHP_WINDOWS_VERSION_MINOR, PhpMixed, + RuntimeException, file_exists, function_exists, is_dir, realpath, }; use crate::dependency_resolver::operation::install_operation::InstallOperation; @@ -68,8 +68,12 @@ impl PathDownloader { return Ok(shirabe_external_packages::react::promise::resolve(None)); } - if format!("{}{}", realpath(&path).unwrap_or_default(), DIRECTORY_SEPARATOR) - .starts_with(&format!("{}{}", real_url, DIRECTORY_SEPARATOR)) + if format!( + "{}{}", + realpath(&path).unwrap_or_default(), + DIRECTORY_SEPARATOR + ) + .starts_with(&format!("{}{}", real_url, DIRECTORY_SEPARATOR)) { // IMPORTANT NOTICE: If you wish to change this, don't. You are wasting your time and ours. // @@ -140,7 +144,10 @@ impl PathDownloader { if output { self.inner.io.write_error( - PhpMixed::String(format!(" - {}: ", InstallOperation::format(package, false))), + PhpMixed::String(format!( + " - {}: ", + InstallOperation::format(package, false) + )), false, IOInterface::NORMAL, ); @@ -251,11 +258,9 @@ impl PathDownloader { } if output { - self.inner.io.write_error( - PhpMixed::String("".to_string()), - true, - IOInterface::NORMAL, - ); + self.inner + .io + .write_error(PhpMixed::String("".to_string()), true, IOInterface::NORMAL); } Ok(shirabe_external_packages::react::promise::resolve(None)) @@ -352,11 +357,7 @@ impl PathDownloader { self.inner.remove(package, &path, output) } - pub fn get_vcs_reference( - &self, - package: &dyn PackageInterface, - path: &str, - ) -> Option<String> { + pub fn get_vcs_reference(&self, package: &dyn PackageInterface, path: &str) -> Option<String> { let path = Filesystem::trim_trailing_slash(path); let parser = VersionParser::new(); let guesser = VersionGuesser::new( diff --git a/crates/shirabe/src/downloader/perforce_downloader.rs b/crates/shirabe/src/downloader/perforce_downloader.rs index 27316e7..8674b3d 100644 --- a/crates/shirabe/src/downloader/perforce_downloader.rs +++ b/crates/shirabe/src/downloader/perforce_downloader.rs @@ -1,14 +1,14 @@ //! ref: composer/src/Composer/Downloader/PerforceDownloader.php -use std::any::Any; -use anyhow::Result; -use indexmap::IndexMap; -use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; -use shirabe_php_shim::PhpMixed; use crate::downloader::vcs_downloader::VcsDownloader; use crate::package::package_interface::PackageInterface; use crate::repository::vcs_repository::VcsRepository; use crate::util::perforce::Perforce; +use anyhow::Result; +use indexmap::IndexMap; +use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; +use shirabe_php_shim::PhpMixed; +use std::any::Any; #[derive(Debug)] pub struct PerforceDownloader { @@ -36,13 +36,22 @@ impl PerforceDownloader { let source_ref = package.get_source_reference(); let label = self.get_label_from_source_reference(source_ref.clone().unwrap_or_default()); - self.inner.io.write_error(&format!("Cloning {}", source_ref.clone().unwrap_or_default())); + self.inner.io.write_error(&format!( + "Cloning {}", + source_ref.clone().unwrap_or_default() + )); self.init_perforce(package, path.clone(), url); - self.perforce.as_mut().unwrap().set_stream(source_ref.clone().unwrap_or_default()); + self.perforce + .as_mut() + .unwrap() + .set_stream(source_ref.clone().unwrap_or_default()); self.perforce.as_mut().unwrap().p4_login(); self.perforce.as_mut().unwrap().write_p4_client_spec(); self.perforce.as_mut().unwrap().connect_client(); - self.perforce.as_mut().unwrap().sync_code_base(label.as_deref()); + self.perforce + .as_mut() + .unwrap() + .sync_code_base(label.as_deref()); self.perforce.as_mut().unwrap().cleanup_client_spec(); Ok(shirabe_external_packages::react::promise::resolve(None)) @@ -73,7 +82,13 @@ impl PerforceDownloader { } else { None }; - self.perforce = Some(Perforce::create(repo_config, url, path, &self.inner.process, &self.inner.io)); + self.perforce = Some(Perforce::create( + repo_config, + url, + path, + &self.inner.process, + &self.inner.io, + )); } fn get_repo_config(&self, repository: &VcsRepository) -> IndexMap<String, PhpMixed> { @@ -90,8 +105,14 @@ impl PerforceDownloader { self.do_install(target, path, url) } - pub fn get_local_changes(&self, _package: &dyn PackageInterface, _path: String) -> Option<String> { - self.inner.io.write_error("Perforce driver does not check for local changes before overriding"); + pub fn get_local_changes( + &self, + _package: &dyn PackageInterface, + _path: String, + ) -> Option<String> { + self.inner + .io + .write_error("Perforce driver does not check for local changes before overriding"); None } @@ -102,7 +123,11 @@ impl PerforceDownloader { to_reference: String, _path: String, ) -> Result<String> { - Ok(self.perforce.as_ref().unwrap().get_commit_logs(from_reference, to_reference)) + Ok(self + .perforce + .as_ref() + .unwrap() + .get_commit_logs(from_reference, to_reference)) } pub fn set_perforce(&mut self, perforce: Perforce) { diff --git a/crates/shirabe/src/downloader/phar_downloader.rs b/crates/shirabe/src/downloader/phar_downloader.rs index 57610f1..8fea679 100644 --- a/crates/shirabe/src/downloader/phar_downloader.rs +++ b/crates/shirabe/src/downloader/phar_downloader.rs @@ -1,10 +1,10 @@ //! ref: composer/src/Composer/Downloader/PharDownloader.php -use anyhow::Result; -use shirabe_php_shim::Phar; -use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use crate::downloader::archive_downloader::ArchiveDownloader; use crate::package::package_interface::PackageInterface; +use anyhow::Result; +use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; +use shirabe_php_shim::Phar; #[derive(Debug)] pub struct PharDownloader { @@ -12,7 +12,12 @@ pub struct PharDownloader { } impl PharDownloader { - pub(crate) fn extract(&self, package: &dyn PackageInterface, file: &str, path: &str) -> Result<Box<dyn PromiseInterface>> { + pub(crate) fn extract( + &self, + package: &dyn PackageInterface, + file: &str, + path: &str, + ) -> Result<Box<dyn PromiseInterface>> { // Can throw an UnexpectedValueException let archive = Phar::new(file.to_string()); archive.extract_to(path, None, true); diff --git a/crates/shirabe/src/downloader/rar_downloader.rs b/crates/shirabe/src/downloader/rar_downloader.rs index 1aaa71c..1b4767f 100644 --- a/crates/shirabe/src/downloader/rar_downloader.rs +++ b/crates/shirabe/src/downloader/rar_downloader.rs @@ -1,19 +1,26 @@ //! ref: composer/src/Composer/Downloader/RarDownloader.php -use anyhow::Result; -use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; -use shirabe_php_shim::{class_exists, implode, RarArchive, RuntimeException, UnexpectedValueException}; use crate::downloader::archive_downloader::ArchiveDownloader; use crate::package::package_interface::PackageInterface; use crate::util::ini_helper::IniHelper; use crate::util::platform::Platform; +use anyhow::Result; +use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; +use shirabe_php_shim::{ + RarArchive, RuntimeException, UnexpectedValueException, class_exists, implode, +}; pub struct RarDownloader { inner: ArchiveDownloader, } impl RarDownloader { - pub(crate) fn extract(&self, _package: &dyn PackageInterface, file: &str, path: &str) -> Result<Box<dyn PromiseInterface>> { + pub(crate) fn extract( + &self, + _package: &dyn PackageInterface, + file: &str, + path: &str, + ) -> Result<Box<dyn PromiseInterface>> { let mut process_error: Option<String> = None; if !Platform::is_windows() { @@ -39,7 +46,10 @@ impl RarDownloader { if !class_exists("RarArchive") { let ini_message = IniHelper::get_message(); let error = if !Platform::is_windows() { - format!("Could not decompress the archive, enable the PHP rar extension.\n{}", ini_message) + format!( + "Could not decompress the archive, enable the PHP rar extension.\n{}", + ini_message + ) } else { format!( "Could not decompress the archive, enable the PHP rar extension or install unrar.\n{}\n{}", @@ -47,7 +57,11 @@ impl RarDownloader { process_error.as_deref().unwrap_or(""), ) }; - return Err(RuntimeException { message: error, code: 0 }.into()); + return Err(RuntimeException { + message: error, + code: 0, + } + .into()); } let rar_archive = RarArchive::open(file); @@ -55,7 +69,8 @@ impl RarDownloader { return Err(UnexpectedValueException { message: format!("Could not open RAR archive: {}", file), code: 0, - }.into()); + } + .into()); } let rar_archive = rar_archive.unwrap(); @@ -64,7 +79,8 @@ impl RarDownloader { return Err(RuntimeException { message: "Could not retrieve RAR archive entries".to_string(), code: 0, - }.into()); + } + .into()); } for entry in entries.unwrap() { @@ -72,7 +88,8 @@ impl RarDownloader { return Err(RuntimeException { message: "Could not extract entry".to_string(), code: 0, - }.into()); + } + .into()); } } diff --git a/crates/shirabe/src/downloader/svn_downloader.rs b/crates/shirabe/src/downloader/svn_downloader.rs index 75e5c02..30a64f2 100644 --- a/crates/shirabe/src/downloader/svn_downloader.rs +++ b/crates/shirabe/src/downloader/svn_downloader.rs @@ -3,7 +3,7 @@ use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::react::promise; use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; -use shirabe_php_shim::{is_dir, version_compare, PhpMixed, RuntimeException}; +use shirabe_php_shim::{PhpMixed, RuntimeException, is_dir, version_compare}; use crate::downloader::vcs_downloader::VcsDownloader; use crate::io::io_interface::IOInterface; @@ -26,7 +26,12 @@ impl SvnDownloader { prev_package: Option<&dyn PackageInterface>, ) -> anyhow::Result<Box<dyn PromiseInterface>> { SvnUtil::clean_env(); - let util = SvnUtil::new(url, &*self.inner.io, &self.inner.config, &self.inner.process); + let util = SvnUtil::new( + url, + &*self.inner.io, + &self.inner.config, + &self.inner.process, + ); if util.binary_version().is_none() { return Err(RuntimeException { message: "svn was not found in your PATH, skipping source download".to_string(), @@ -52,7 +57,10 @@ impl SvnDownloader { if let Some(vcs_repo) = repo.as_any().downcast_ref::<VcsRepository>() { let repo_config = vcs_repo.get_repo_config(); if repo_config.contains_key("svn-cache-credentials") { - if let Some(val) = repo_config.get("svn-cache-credentials").and_then(|v| v.as_bool()) { + if let Some(val) = repo_config + .get("svn-cache-credentials") + .and_then(|v| v.as_bool()) + { self.cache_credentials = val; } } @@ -97,7 +105,12 @@ impl SvnDownloader { .into()); } - let util = SvnUtil::new(url, &*self.inner.io, &self.inner.config, &self.inner.process); + let util = SvnUtil::new( + url, + &*self.inner.io, + &self.inner.config, + &self.inner.process, + ); let mut flags: Vec<String> = vec![]; if version_compare(&util.binary_version().unwrap_or_default(), "1.7.0", ">=") { flags.push("--ignore-ancestry".to_string()); @@ -271,9 +284,7 @@ impl SvnDownloader { " n - abort the {} and let you manually clean things up", if update { "update" } else { "uninstall" } ))), - Box::new(PhpMixed::String( - " v - view modified files".to_string(), - )), + Box::new(PhpMixed::String(" v - view modified files".to_string())), Box::new(PhpMixed::String(" ? - print help".to_string())), ]), true, @@ -355,11 +366,7 @@ impl SvnDownloader { util.execute_local(command.clone(), path, None, self.inner.io.is_verbose()) .map_err(|e| { RuntimeException { - message: format!( - "Failed to execute {}\n\n{}", - command.join(" "), - e - ), + message: format!("Failed to execute {}\n\n{}", command.join(" "), e), code: 0, } .into() @@ -375,9 +382,7 @@ impl SvnDownloader { pub(crate) fn discard_changes(&self, path: &str) -> anyhow::Result<Box<dyn PromiseInterface>> { let mut output = String::new(); if self.inner.process.execute( - &["svn", "revert", "-R", "."] - .map(|s| s.to_string()) - .to_vec(), + &["svn", "revert", "-R", "."].map(|s| s.to_string()).to_vec(), &mut output, Some(path.to_string()), ) != 0 diff --git a/crates/shirabe/src/downloader/tar_downloader.rs b/crates/shirabe/src/downloader/tar_downloader.rs index 4b13f3b..c327ca0 100644 --- a/crates/shirabe/src/downloader/tar_downloader.rs +++ b/crates/shirabe/src/downloader/tar_downloader.rs @@ -1,10 +1,10 @@ //! ref: composer/src/Composer/Downloader/TarDownloader.php -use anyhow::Result; -use shirabe_php_shim::PharData; -use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use crate::downloader::archive_downloader::ArchiveDownloader; use crate::package::package_interface::PackageInterface; +use anyhow::Result; +use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; +use shirabe_php_shim::PharData; #[derive(Debug)] pub struct TarDownloader { @@ -12,7 +12,12 @@ pub struct TarDownloader { } impl TarDownloader { - pub(crate) fn extract(&self, package: &dyn PackageInterface, file: &str, path: &str) -> Result<Box<dyn PromiseInterface>> { + pub(crate) fn extract( + &self, + package: &dyn PackageInterface, + file: &str, + path: &str, + ) -> Result<Box<dyn PromiseInterface>> { let archive = PharData::new(file.to_string()); archive.extract_to(path, None, true); diff --git a/crates/shirabe/src/downloader/vcs_downloader.rs b/crates/shirabe/src/downloader/vcs_downloader.rs index 3fb305f..37954d2 100644 --- a/crates/shirabe/src/downloader/vcs_downloader.rs +++ b/crates/shirabe/src/downloader/vcs_downloader.rs @@ -4,9 +4,8 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use shirabe_php_shim::{ - array_map, array_shift, count, explode, get_class, implode, rawurldecode, realpath, - str_replace, strlen, strpos, substr, trim, InvalidArgumentException, PhpMixed, - RuntimeException, + InvalidArgumentException, PhpMixed, RuntimeException, array_map, array_shift, count, explode, + get_class, implode, rawurldecode, realpath, str_replace, strlen, strpos, substr, trim, }; use crate::config::Config; @@ -91,11 +90,7 @@ impl VcsDownloader { } if self.io.is_debug() { self.io.write_error( - PhpMixed::String(format!( - "Failed: [{}] {}", - get_class(&e), - e, - )), + PhpMixed::String(format!("Failed: [{}] {}", get_class(&e), e,)), true, IOInterface::NORMAL, ); @@ -183,7 +178,10 @@ impl VcsDownloader { } self.io.write_error( - PhpMixed::String(format!(" - {}: ", InstallOperation::format(package, false))), + PhpMixed::String(format!( + " - {}: ", + InstallOperation::format(package, false) + )), false, IOInterface::NORMAL, ); @@ -203,11 +201,7 @@ impl VcsDownloader { } if self.io.is_debug() { self.io.write_error( - PhpMixed::String(format!( - "Failed: [{}] {}", - get_class(&e), - e, - )), + PhpMixed::String(format!("Failed: [{}] {}", get_class(&e), e,)), true, IOInterface::NORMAL, ); @@ -285,11 +279,7 @@ impl VcsDownloader { } if self.io.is_debug() { self.io.write_error( - PhpMixed::String(format!( - "Failed: [{}] {}", - get_class(&e), - e, - )), + PhpMixed::String(format!("Failed: [{}] {}", get_class(&e), e,)), true, IOInterface::NORMAL, ); @@ -364,7 +354,10 @@ impl VcsDownloader { path: &str, ) -> Result<Box<dyn PromiseInterface>> { self.io.write_error( - PhpMixed::String(format!(" - {}", UninstallOperation::format(package, false))), + PhpMixed::String(format!( + " - {}", + UninstallOperation::format(package, false) + )), true, IOInterface::NORMAL, ); @@ -372,24 +365,22 @@ impl VcsDownloader { let promise = self.filesystem.remove_directory_async(path); let path = path.to_string(); - Ok(promise.then(Box::new(move |result: PhpMixed| -> Result<()> { - let result_bool = result.as_bool().unwrap_or(false); - if !result_bool { - return Err(RuntimeException { - message: format!("Could not completely delete {}, aborting.", path), - code: 0, + Ok( + promise.then(Box::new(move |result: PhpMixed| -> Result<()> { + let result_bool = result.as_bool().unwrap_or(false); + if !result_bool { + return Err(RuntimeException { + message: format!("Could not completely delete {}, aborting.", path), + code: 0, + } + .into()); } - .into()); - } - Ok(()) - }))) + Ok(()) + })), + ) } - pub fn get_vcs_reference( - &self, - package: &dyn PackageInterface, - path: &str, - ) -> Option<String> { + pub fn get_vcs_reference(&self, package: &dyn PackageInterface, path: &str) -> Option<String> { let parser = VersionParser::new(); let guesser = VersionGuesser::new(&self.config, &self.process, &parser, &*self.io); let dumper = ArrayDumper::new(); diff --git a/crates/shirabe/src/downloader/xz_downloader.rs b/crates/shirabe/src/downloader/xz_downloader.rs index a53c9c6..0c5d876 100644 --- a/crates/shirabe/src/downloader/xz_downloader.rs +++ b/crates/shirabe/src/downloader/xz_downloader.rs @@ -1,9 +1,9 @@ //! ref: composer/src/Composer/Downloader/XzDownloader.php -use anyhow::{bail, Result}; -use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use crate::downloader::archive_downloader::ArchiveDownloader; use crate::package::package_interface::PackageInterface; +use anyhow::{Result, bail}; +use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; #[derive(Debug)] pub struct XzDownloader { @@ -11,7 +11,12 @@ pub struct XzDownloader { } impl XzDownloader { - pub(crate) fn extract(&self, package: &dyn PackageInterface, file: &str, path: &str) -> Result<Box<dyn PromiseInterface>> { + pub(crate) fn extract( + &self, + package: &dyn PackageInterface, + file: &str, + path: &str, + ) -> Result<Box<dyn PromiseInterface>> { let command = vec!["tar", "-xJf", file, "-C", path]; let mut ignored_output = String::new(); @@ -19,7 +24,11 @@ impl XzDownloader { return Ok(shirabe_external_packages::react::promise::resolve(None)); } - let process_error = format!("Failed to execute {}\n\n{}", command.join(" "), self.inner.process.get_error_output()); + let process_error = format!( + "Failed to execute {}\n\n{}", + command.join(" "), + self.inner.process.get_error_output() + ); bail!(process_error); } diff --git a/crates/shirabe/src/downloader/zip_downloader.rs b/crates/shirabe/src/downloader/zip_downloader.rs index 71a23b8..ecb7821 100644 --- a/crates/shirabe/src/downloader/zip_downloader.rs +++ b/crates/shirabe/src/downloader/zip_downloader.rs @@ -1,23 +1,22 @@ //! ref: composer/src/Composer/Downloader/ZipDownloader.php -use std::sync::Mutex; +use crate::downloader::archive_downloader::ArchiveDownloader; +use crate::downloader::file_downloader::FileDownloader; +use crate::package::package_interface::PackageInterface; +use crate::util::ini_helper::IniHelper; +use crate::util::platform::Platform; use anyhow::Result; use indexmap::IndexMap; +use shirabe_external_packages::composer::pcre::preg::Preg; +use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; +use shirabe_external_packages::symfony::component::process::executable_finder::ExecutableFinder; +use shirabe_external_packages::symfony::component::process::process::Process; use shirabe_php_shim::{ + DIRECTORY_SEPARATOR, ErrorException, RuntimeException, UnexpectedValueException, ZipArchive, bin2hex, class_exists, file_exists, file_get_contents, filesize, function_exists, hash_file, is_file, json_encode, random_int, version_compare, - ErrorException, RuntimeException, UnexpectedValueException, ZipArchive, - DIRECTORY_SEPARATOR, }; -use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; -use shirabe_external_packages::symfony::component::process::executable_finder::ExecutableFinder; -use shirabe_external_packages::symfony::component::process::process::Process; -use shirabe_external_packages::composer::pcre::preg::Preg; -use crate::downloader::archive_downloader::ArchiveDownloader; -use crate::downloader::file_downloader::FileDownloader; -use crate::package::package_interface::PackageInterface; -use crate::util::ini_helper::IniHelper; -use crate::util::platform::Platform; +use std::sync::Mutex; static UNZIP_COMMANDS: Mutex<Option<Vec<Vec<String>>>> = Mutex::new(None); static HAS_ZIP_ARCHIVE: Mutex<Option<bool>> = Mutex::new(None); @@ -46,22 +45,61 @@ impl ZipDownloader { let commands = unzip_commands.as_mut().unwrap(); if Platform::is_windows() { if let Some(cmd) = finder.find("7z", None, &[r"C:\Program Files\7-Zip"]) { - commands.push(vec!["7z".to_string(), cmd, "x".to_string(), "-bb0".to_string(), "-y".to_string(), "%file%".to_string(), "-o%path%".to_string()]); + commands.push(vec![ + "7z".to_string(), + cmd, + "x".to_string(), + "-bb0".to_string(), + "-y".to_string(), + "%file%".to_string(), + "-o%path%".to_string(), + ]); } } if let Some(cmd) = finder.find("unzip", None, &[]) { - commands.push(vec!["unzip".to_string(), cmd, "-qq".to_string(), "%file%".to_string(), "-d".to_string(), "%path%".to_string()]); + commands.push(vec![ + "unzip".to_string(), + cmd, + "-qq".to_string(), + "%file%".to_string(), + "-d".to_string(), + "%path%".to_string(), + ]); } if !Platform::is_windows() { if let Some(cmd) = finder.find("7z", None, &[]) { // 7z linux/macOS support is only used if unzip is not present - commands.push(vec!["7z".to_string(), cmd, "x".to_string(), "-bb0".to_string(), "-y".to_string(), "%file%".to_string(), "-o%path%".to_string()]); + commands.push(vec![ + "7z".to_string(), + cmd, + "x".to_string(), + "-bb0".to_string(), + "-y".to_string(), + "%file%".to_string(), + "-o%path%".to_string(), + ]); } else if let Some(cmd) = finder.find("7zz", None, &[]) { // 7zz linux/macOS support is only used if unzip is not present - commands.push(vec!["7zz".to_string(), cmd, "x".to_string(), "-bb0".to_string(), "-y".to_string(), "%file%".to_string(), "-o%path%".to_string()]); + commands.push(vec![ + "7zz".to_string(), + cmd, + "x".to_string(), + "-bb0".to_string(), + "-y".to_string(), + "%file%".to_string(), + "-o%path%".to_string(), + ]); } else if let Some(cmd) = finder.find("7za", None, &[]) { // 7za linux/macOS support is only used if unzip is not present - commands.push(vec!["7za".to_string(), cmd, "x".to_string(), "-bb0".to_string(), "-y".to_string(), "%file%".to_string(), "-o%path%".to_string()]); + commands.push(vec![ + "7za".to_string(), + cmd, + "x".to_string(), + "-bb0".to_string(), + "-y".to_string(), + "%file%".to_string(), + "-o%path%".to_string(), + ]); } } } @@ -80,16 +118,30 @@ impl ZipDownloader { } let has_zip_archive = HAS_ZIP_ARCHIVE.lock().unwrap().unwrap_or(false); - let unzip_commands_empty = UNZIP_COMMANDS.lock().unwrap().as_ref().map_or(true, |v| v.is_empty()); + let unzip_commands_empty = UNZIP_COMMANDS + .lock() + .unwrap() + .as_ref() + .map_or(true, |v| v.is_empty()); if !has_zip_archive && unzip_commands_empty { let ini_message = IniHelper::get_message(); let error = if proc_open_missing { - format!("The zip extension is missing and unzip/7z commands cannot be called as proc_open is disabled, skipping.\n{}", ini_message) + format!( + "The zip extension is missing and unzip/7z commands cannot be called as proc_open is disabled, skipping.\n{}", + ini_message + ) } else { - format!("The zip extension and unzip/7z commands are both missing, skipping.\n{}", ini_message) + format!( + "The zip extension and unzip/7z commands are both missing, skipping.\n{}", + ini_message + ) }; - return Err(RuntimeException { message: error, code: 0 }.into()); + return Err(RuntimeException { + message: error, + code: 0, + } + .into()); } { @@ -111,7 +163,9 @@ impl ZipDownloader { } } - self.inner.inner.download(package, path, prev_package, output) + self.inner + .inner + .download(package, path, prev_package, output) } fn extract_with_system_unzip( @@ -124,7 +178,11 @@ impl ZipDownloader { let is_last_chance = !HAS_ZIP_ARCHIVE.lock().unwrap().unwrap_or(false); - let unzip_commands_empty = UNZIP_COMMANDS.lock().unwrap().as_ref().map_or(true, |v| v.is_empty()); + let unzip_commands_empty = UNZIP_COMMANDS + .lock() + .unwrap() + .as_ref() + .map_or(true, |v| v.is_empty()); if unzip_commands_empty { return self.extract_with_zip_archive(package, file, path); } @@ -136,14 +194,19 @@ impl ZipDownloader { // see https://github.com/composer/composer/issues/10058 ("%file%", file.replace('/', DIRECTORY_SEPARATOR)), ("%path%", path.replace('/', DIRECTORY_SEPARATOR)), - ].into_iter().collect(); - let command: Vec<String> = command_spec[1..].iter().map(|value| { - let mut v = value.clone(); - for (from, to) in &map { - v = v.replace(from, to.as_str()); - } - v - }).collect(); + ] + .into_iter() + .collect(); + let command: Vec<String> = command_spec[1..] + .iter() + .map(|value| { + let mut v = value.clone(); + for (from, to) in &map { + v = v.replace(from, to.as_str()); + } + v + }) + .collect(); if !*WARNED_7ZIP_LINUX.lock().unwrap() && !Platform::is_windows() @@ -151,8 +214,16 @@ impl ZipDownloader { { *WARNED_7ZIP_LINUX.lock().unwrap() = true; let mut output = String::new(); - if self.inner.inner.process.execute(&[command_spec[1].as_str()], &mut output) == 0 { - if let Some(m) = Preg::is_match_strict_groups(r"^\s*7-Zip(?:\s\[64\])?\s([0-9.]+)", &output) { + if self + .inner + .inner + .process + .execute(&[command_spec[1].as_str()], &mut output) + == 0 + { + if let Some(m) = + Preg::is_match_strict_groups(r"^\s*7-Zip(?:\s\[64\])?\s([0-9.]+)", &output) + { if version_compare(&m[1], "21.01", "<") { self.inner.inner.io.write_error(&format!( " <warning>Unzipping using {} {} may result in incorrect file permissions. Install {} 21.01+ or unzip to ensure you get correct permissions.</warning>", @@ -180,21 +251,44 @@ impl ZipDownloader { } else { io.write_error(&format!(" <warning>{}</warning>", process_error)); io.write_error(" The archive may contain identical file names with different capitalization (which fails on case insensitive filesystems)"); - io.write_error(&format!(" Unzip with {} command failed, falling back to ZipArchive class", executable)); + io.write_error(&format!( + " Unzip with {} command failed, falling back to ZipArchive class", + executable + )); - if Platform::get_env("GITHUB_ACTIONS").is_some() && Platform::get_env("COMPOSER_TESTS_ARE_RUNNING").is_none() { + if Platform::get_env("GITHUB_ACTIONS").is_some() + && Platform::get_env("COMPOSER_TESTS_ARE_RUNNING").is_none() + { io.write_error(" <warning>Additional debug info, please report to https://github.com/composer/composer/issues/11148 if you see this:</warning>"); io.write_error(&format!("File size: {}", filesize(file).unwrap_or(0))); - io.write_error(&format!("File SHA1: {}", hash_file("sha1", file).unwrap_or_default())); + io.write_error(&format!( + "File SHA1: {}", + hash_file("sha1", file).unwrap_or_default() + )); let content = file_get_contents(file).unwrap_or_default(); let bytes = content.as_bytes(); - io.write_error(&format!("First 100 bytes (hex): {}", bin2hex(&bytes[..bytes.len().min(100)]))); + io.write_error(&format!( + "First 100 bytes (hex): {}", + bin2hex(&bytes[..bytes.len().min(100)]) + )); let len = bytes.len(); - io.write_error(&format!("Last 100 bytes (hex): {}", bin2hex(&bytes[len.saturating_sub(100)..]))); + io.write_error(&format!( + "Last 100 bytes (hex): {}", + bin2hex(&bytes[len.saturating_sub(100)..]) + )); if package.get_dist_url().map_or(false, |u| !u.is_empty()) { - io.write_error(&format!("Origin URL: {}", self.inner.inner.process_url(package, &package.get_dist_url().unwrap_or_default()))); + io.write_error(&format!( + "Origin URL: {}", + self.inner + .inner + .process_url(package, &package.get_dist_url().unwrap_or_default()) + )); let headers = FileDownloader::response_headers.lock().unwrap(); - io.write_error(&format!("Response Headers: {}", json_encode(&shirabe_php_shim::PhpMixed::Null).unwrap_or_else(|| "[]".to_string()))); + io.write_error(&format!( + "Response Headers: {}", + json_encode(&shirabe_php_shim::PhpMixed::Null) + .unwrap_or_else(|| "[]".to_string()) + )); } } } @@ -241,7 +335,10 @@ impl ZipDownloader { file: &str, path: &str, ) -> Result<Box<dyn PromiseInterface>> { - let mut zip_archive = self.zip_archive_object.take().unwrap_or_else(ZipArchive::new); + let mut zip_archive = self + .zip_archive_object + .take() + .unwrap_or_else(ZipArchive::new); let result: Result<Box<dyn PromiseInterface>> = (|| { let retval = if !file_exists(file) || filesize(file).map_or(true, |s| s == 0) { @@ -259,10 +356,15 @@ impl ZipDownloader { let mut files_to_inspect = total_files.min(5); let mut i: i64 = 0; while i < files_to_inspect { - let stat_index = if inspect_all { i } else { random_int(0, total_files - 1) }; + let stat_index = if inspect_all { + i + } else { + random_int(0, total_files - 1) + }; if let Some(stat) = zip_archive.stat_index(stat_index) { let size = stat.get("size").and_then(|v| v.as_int()).unwrap_or(0); - let comp_size = stat.get("comp_size").and_then(|v| v.as_int()).unwrap_or(0); + let comp_size = + stat.get("comp_size").and_then(|v| v.as_int()).unwrap_or(0); total_size += size; if !inspect_all && size > comp_size * 200 { total_size = 0; @@ -305,7 +407,8 @@ impl ZipDownloader { return Err(UnexpectedValueException { message: self.get_error_message(code, file).trim_end().to_string(), code, - }.into()); + } + .into()); } })(); @@ -345,8 +448,14 @@ impl ZipDownloader { ZipArchive::ER_OPEN => format!("Can't open zip file: {}", file), ZipArchive::ER_READ => format!("Zip read error ({})", file), ZipArchive::ER_SEEK => format!("Zip seek error ({})", file), - -1 => format!("'{}' is a corrupted zip archive (0 bytes), try again.", file), - _ => format!("'{}' is not a valid zip archive, got error code: {}", file, retval), + -1 => format!( + "'{}' is a corrupted zip archive (0 bytes), try again.", + file + ), + _ => format!( + "'{}' is not a valid zip archive, got error code: {}", + file, retval + ), } } } diff --git a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs index 93cc885..6615bd9 100644 --- a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs +++ b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs @@ -10,13 +10,13 @@ use shirabe_external_packages::symfony::component::console::output::console_outp use shirabe_external_packages::symfony::component::process::executable_finder::ExecutableFinder; use shirabe_external_packages::symfony::component::process::php_executable_finder::PhpExecutableFinder; use shirabe_php_shim::{ - array_pop, array_push, array_search_in_vec, array_splice, class_exists, count_mixed, - defined, file_exists, get_class, hash, implode, ini_get, is_a, is_array, is_callable, - is_object, is_string, krsort, max_i64, method_exists, preg_quote, realpath, spl_autoload_functions, - spl_autoload_register, spl_autoload_unregister, spl_object_hash, sprintf, str_contains, - str_ends_with, str_replace, str_starts_with, strlen, strpos, strtoupper, substr, trim, - Exception, InvalidArgumentException, LogicException, PhpMixed, RuntimeException, PATH_SEPARATOR, - PHP_VERSION_ID, + Exception, InvalidArgumentException, LogicException, PATH_SEPARATOR, PHP_VERSION_ID, PhpMixed, + RuntimeException, array_pop, array_push, array_search_in_vec, array_splice, class_exists, + count_mixed, defined, file_exists, get_class, hash, implode, ini_get, is_a, is_array, + is_callable, is_object, is_string, krsort, max_i64, method_exists, preg_quote, realpath, + spl_autoload_functions, spl_autoload_register, spl_autoload_unregister, spl_object_hash, + sprintf, str_contains, str_ends_with, str_replace, str_starts_with, strlen, strpos, strtoupper, + substr, trim, }; use crate::autoload::class_loader::ClassLoader; @@ -257,11 +257,7 @@ impl EventDispatcher { result } - fn do_dispatch_body( - &mut self, - event: &Event, - listeners: Vec<Callable>, - ) -> anyhow::Result<i64> { + fn do_dispatch_body(&mut self, event: &Event, listeners: Vec<Callable>) -> anyhow::Result<i64> { let mut return_max = 0_i64; for callable in listeners { let mut r#return: i64 = 0; @@ -271,8 +267,8 @@ impl EventDispatcher { let mut callable = callable; if let Callable::String(ref s) = callable { if str_contains(s, "@no_additional_args") { - let replaced = - Preg::replace("{ ?@no_additional_args}", "", s).unwrap_or_else(|_| s.clone()); + let replaced = Preg::replace("{ ?@no_additional_args}", "", s) + .unwrap_or_else(|_| s.clone()); callable = Callable::String(replaced); additional_args = Vec::new(); } @@ -337,65 +333,66 @@ impl EventDispatcher { } // TODO(plugin): actually invoke callable with $event and inspect result r#return = 0; - } else { match callable { - Callable::String(ref callable_str) if self.is_composer_script(callable_str) => { - self.io.write_error( - PhpMixed::String(sprintf( - "> %s: %s", - &[ - PhpMixed::String(formatted_event_name_with_args.clone()), - PhpMixed::String(callable_str.clone()), - ], - )), - true, - <dyn IOInterface>::VERBOSE, - ); + } else { + match callable { + Callable::String(ref callable_str) if self.is_composer_script(callable_str) => { + self.io.write_error( + PhpMixed::String(sprintf( + "> %s: %s", + &[ + PhpMixed::String(formatted_event_name_with_args.clone()), + PhpMixed::String(callable_str.clone()), + ], + )), + true, + <dyn IOInterface>::VERBOSE, + ); - let mut script: Vec<String> = substr(callable_str, 1, None) - .split(' ') - .map(|s| s.to_string()) - .collect(); - let script_name = script[0].clone(); - script.remove(0); + let mut script: Vec<String> = substr(callable_str, 1, None) + .split(' ') + .map(|s| s.to_string()) + .collect(); + let script_name = script[0].clone(); + script.remove(0); - let args: Vec<String>; - if let Some(index) = array_search_in_vec("@additional_args", &script) { - let _ = array_splice::<String>(&mut script, index, 0, &additional_args); - args = script.clone(); - } else { - let mut merged = script.clone(); - merged.extend(additional_args.clone()); - args = merged; - } - let mut flags = event.get_flags().clone(); - if flags.contains_key("script-alias-input") { - let args_string = script - .iter() - .map(|arg| ProcessExecutor::escape(arg)) - .collect::<Vec<_>>() - .join(" "); - let existing = flags - .get("script-alias-input") - .and_then(|v| v.as_string()) - .unwrap_or("") - .to_string(); - flags.insert( - "script-alias-input".to_string(), - PhpMixed::String(format!("{} {}", args_string, existing)), - ); - } - if strpos(callable_str, "@composer ") == Some(0) { - let exec = format!( - "{} {} {}", - self.get_php_exec_command()?, - ProcessExecutor::escape( - &Platform::get_env("COMPOSER_BINARY").unwrap_or_default() - ), - args.join(" ") - ); - let exit_code = self.execute_tty(&exec)?; - if exit_code != 0 { - self.io.write_error( + let args: Vec<String>; + if let Some(index) = array_search_in_vec("@additional_args", &script) { + let _ = array_splice::<String>(&mut script, index, 0, &additional_args); + args = script.clone(); + } else { + let mut merged = script.clone(); + merged.extend(additional_args.clone()); + args = merged; + } + let mut flags = event.get_flags().clone(); + if flags.contains_key("script-alias-input") { + let args_string = script + .iter() + .map(|arg| ProcessExecutor::escape(arg)) + .collect::<Vec<_>>() + .join(" "); + let existing = flags + .get("script-alias-input") + .and_then(|v| v.as_string()) + .unwrap_or("") + .to_string(); + flags.insert( + "script-alias-input".to_string(), + PhpMixed::String(format!("{} {}", args_string, existing)), + ); + } + if strpos(callable_str, "@composer ") == Some(0) { + let exec = format!( + "{} {} {}", + self.get_php_exec_command()?, + ProcessExecutor::escape( + &Platform::get_env("COMPOSER_BINARY").unwrap_or_default() + ), + args.join(" ") + ); + let exit_code = self.execute_tty(&exec)?; + if exit_code != 0 { + self.io.write_error( PhpMixed::String(sprintf( &format!( "<error>Script %s handling the %s event returned with error code {}</error>", @@ -410,26 +407,26 @@ impl EventDispatcher { <dyn IOInterface>::QUIET, ); - return Err(anyhow::anyhow!(ScriptExecutionException( - RuntimeException { - message: format!( - "Error Output: {}", - self.process.get_error_output() - ), - code: exit_code, - } - ))); - } - } else { - if self - .get_listeners(&Event::new( - script_name.clone(), - Vec::new(), - IndexMap::new(), - )) - .is_empty() - { - self.io.write_error( + return Err(anyhow::anyhow!(ScriptExecutionException( + RuntimeException { + message: format!( + "Error Output: {}", + self.process.get_error_output() + ), + code: exit_code, + } + ))); + } + } else { + if self + .get_listeners(&Event::new( + script_name.clone(), + Vec::new(), + IndexMap::new(), + )) + .is_empty() + { + self.io.write_error( PhpMixed::String(sprintf( "<warning>You made a reference to a non-existent script %s</warning>", &[PhpMixed::String(callable_str.clone())], @@ -437,53 +434,56 @@ impl EventDispatcher { true, <dyn IOInterface>::QUIET, ); - } + } - let composer_full = self.composer_as_full_or_panic(); - let mut script_event = ScriptEvent::new( - script_name.clone(), - composer_full, - self.io_clone(), - // event.isDevMode() is only on InstallerEvent/ScriptEvent/PackageEvent - // TODO(plugin): proper dev_mode propagation when polymorphic event is supported - false, - args, - flags, - ); - // TODO(plugin): script_event.set_originating_event(event.clone()) - match self.dispatch(Some(&script_name), Some(Event::new( - script_name.clone(), - script_event.inner_args_for_dispatch(), - script_event.inner_flags_for_dispatch(), - ))) { - Ok(v) => r#return = v, - Err(e) => { - if e.downcast_ref::<ScriptExecutionException>().is_some() { - self.io.write_error( - PhpMixed::String(sprintf( - "<error>Script %s was called via %s</error>", - &[ - PhpMixed::String(callable_str.clone()), - PhpMixed::String(event.get_name().to_string()), - ], - )), - true, - <dyn IOInterface>::QUIET, - ); + let composer_full = self.composer_as_full_or_panic(); + let mut script_event = ScriptEvent::new( + script_name.clone(), + composer_full, + self.io_clone(), + // event.isDevMode() is only on InstallerEvent/ScriptEvent/PackageEvent + // TODO(plugin): proper dev_mode propagation when polymorphic event is supported + false, + args, + flags, + ); + // TODO(plugin): script_event.set_originating_event(event.clone()) + match self.dispatch( + Some(&script_name), + Some(Event::new( + script_name.clone(), + script_event.inner_args_for_dispatch(), + script_event.inner_flags_for_dispatch(), + )), + ) { + Ok(v) => r#return = v, + Err(e) => { + if e.downcast_ref::<ScriptExecutionException>().is_some() { + self.io.write_error( + PhpMixed::String(sprintf( + "<error>Script %s was called via %s</error>", + &[ + PhpMixed::String(callable_str.clone()), + PhpMixed::String(event.get_name().to_string()), + ], + )), + true, + <dyn IOInterface>::QUIET, + ); + } + return Err(e); } - return Err(e); } } } - } - Callable::String(ref callable_str) if self.is_php_script(callable_str) => { - let pos = strpos(callable_str, "::").unwrap_or(0) as i64; - let class_name = substr(callable_str, 0, Some(pos)); - let method_name = substr(callable_str, pos + 2, None); + Callable::String(ref callable_str) if self.is_php_script(callable_str) => { + let pos = strpos(callable_str, "::").unwrap_or(0) as i64; + let class_name = substr(callable_str, 0, Some(pos)); + let method_name = substr(callable_str, pos + 2, None); - self.make_autoloader(event, &Callable::String(callable_str.clone())); - if !class_exists(&class_name) { - self.io.write_error( + self.make_autoloader(event, &Callable::String(callable_str.clone())); + if !class_exists(&class_name) { + self.io.write_error( PhpMixed::String(format!( "<warning>Class {} is not autoloadable, can not call {} script</warning>", class_name, @@ -492,10 +492,10 @@ impl EventDispatcher { true, <dyn IOInterface>::QUIET, ); - continue; - } - if !is_callable(&PhpMixed::String(callable_str.clone())) { - self.io.write_error( + continue; + } + if !is_callable(&PhpMixed::String(callable_str.clone())) { + self.io.write_error( PhpMixed::String(format!( "<warning>Method {} is not callable, can not call {} script</warning>", callable_str, @@ -504,46 +504,46 @@ impl EventDispatcher { true, <dyn IOInterface>::QUIET, ); - continue; - } - - match self.execute_event_php_script(&class_name, &method_name, event) { - Ok(v) => { - r#return = if let PhpMixed::Bool(false) = v { 1 } else { 0 }; + continue; } - Err(e) => { - let message = - "Script %s handling the %s event terminated with an exception"; - self.io.write_error( - PhpMixed::String(format!( - "<error>{}</error>", - sprintf( - message, - &[ - PhpMixed::String(callable_str.clone()), - PhpMixed::String(event.get_name().to_string()), - ], - ) - )), - true, - <dyn IOInterface>::QUIET, - ); - return Err(e); + + match self.execute_event_php_script(&class_name, &method_name, event) { + Ok(v) => { + r#return = if let PhpMixed::Bool(false) = v { 1 } else { 0 }; + } + Err(e) => { + let message = + "Script %s handling the %s event terminated with an exception"; + self.io.write_error( + PhpMixed::String(format!( + "<error>{}</error>", + sprintf( + message, + &[ + PhpMixed::String(callable_str.clone()), + PhpMixed::String(event.get_name().to_string()), + ], + ) + )), + true, + <dyn IOInterface>::QUIET, + ); + return Err(e); + } } } - } - Callable::String(ref callable_str) if self.is_command_class(callable_str) => { - let class_name = callable_str.clone(); + Callable::String(ref callable_str) if self.is_command_class(callable_str) => { + let class_name = callable_str.clone(); - self.make_autoloader( - event, - &Callable::ArrayCallable( - Box::new(PhpMixed::String(callable_str.clone())), - "run".to_string(), - ), - ); - if !class_exists(&class_name) { - self.io.write_error( + self.make_autoloader( + event, + &Callable::ArrayCallable( + Box::new(PhpMixed::String(callable_str.clone())), + "run".to_string(), + ), + ); + if !class_exists(&class_name) { + self.io.write_error( PhpMixed::String(format!( "<warning>Class {} is not autoloadable, can not call {} script</warning>", class_name, @@ -552,14 +552,14 @@ impl EventDispatcher { true, <dyn IOInterface>::QUIET, ); - continue; - } - if !is_a( - &PhpMixed::String(class_name.clone()), - "Symfony\\Component\\Console\\Command\\Command", - true, - ) { - self.io.write_error( + continue; + } + if !is_a( + &PhpMixed::String(class_name.clone()), + "Symfony\\Component\\Console\\Command\\Command", + true, + ) { + self.io.write_error( PhpMixed::String(format!( "<warning>Class {} does not extend Symfony\\Component\\Console\\Command\\Command, can not call {} script</warning>", class_name, @@ -568,13 +568,13 @@ impl EventDispatcher { true, <dyn IOInterface>::QUIET, ); - continue; - } - if defined(&format!( - "Composer\\Script\\ScriptEvents::{}", - str_replace("-", "_", &strtoupper(event.get_name())) - )) { - self.io.write_error( + continue; + } + if defined(&format!( + "Composer\\Script\\ScriptEvents::{}", + str_replace("-", "_", &strtoupper(event.get_name())) + )) { + self.io.write_error( PhpMixed::String(format!( "<warning>You cannot bind {} to a Command class, use a non-reserved name</warning>", event.get_name() @@ -582,239 +582,241 @@ impl EventDispatcher { true, <dyn IOInterface>::QUIET, ); - continue; - } + continue; + } - let mut app = Application::new(); - app.set_catch_exceptions(false); - if method_exists( - &PhpMixed::String("Application".to_string()), - "setCatchErrors", - ) { - app.set_catch_errors(false); - } - app.set_auto_exit(false); - // TODO(plugin): instantiate command class dynamically: `new $className($event->getName())` - let cmd = Command::new(event.get_name().to_string()); - if method_exists(&PhpMixed::String("Application".to_string()), "addCommand") { - app.add_command(cmd.clone()); - } else { - // Compatibility layer for symfony/console <7.4 - app.add(cmd.clone()); + let mut app = Application::new(); + app.set_catch_exceptions(false); + if method_exists( + &PhpMixed::String("Application".to_string()), + "setCatchErrors", + ) { + app.set_catch_errors(false); + } + app.set_auto_exit(false); + // TODO(plugin): instantiate command class dynamically: `new $className($event->getName())` + let cmd = Command::new(event.get_name().to_string()); + if method_exists(&PhpMixed::String("Application".to_string()), "addCommand") + { + app.add_command(cmd.clone()); + } else { + // Compatibility layer for symfony/console <7.4 + app.add(cmd.clone()); + } + app.set_default_command(cmd.get_name().to_string(), true); + let result = (|| -> anyhow::Result<i64> { + let args = additional_args + .iter() + .map(|arg| ProcessExecutor::escape(arg)) + .collect::<Vec<_>>() + .join(" "); + // reusing the output from $this->io is mostly needed for tests, but generally speaking + // it does not hurt to keep the same stream as the current Application + let output = if let Some(_console_io) = + self.io.as_any().downcast_ref::<ConsoleIO>() + { + // TODO(plugin): \ReflectionProperty to read private `output` from ConsoleIO + // is required by the original PHP — needs user-decided porting strategy. + let _refl_php_version_gate = PHP_VERSION_ID < 80100; + todo!("\\ReflectionProperty on ConsoleIO::$output") + } else { + ConsoleOutput::new() + }; + let input_str = event + .get_flags() + .get("script-alias-input") + .and_then(|v| v.as_string()) + .unwrap_or(&args) + .to_string(); + Ok(app.run(StringInput::new(input_str), output)) + })(); + match result { + Ok(v) => r#return = v, + Err(e) => { + let message = + "Script %s handling the %s event terminated with an exception"; + self.io.write_error( + PhpMixed::String(format!( + "<error>{}</error>", + sprintf( + message, + &[ + PhpMixed::String(callable_str.clone()), + PhpMixed::String(event.get_name().to_string()), + ], + ) + )), + true, + <dyn IOInterface>::QUIET, + ); + return Err(e); + } + } } - app.set_default_command(cmd.get_name().to_string(), true); - let result = (|| -> anyhow::Result<i64> { + Callable::String(callable_str) => { let args = additional_args .iter() .map(|arg| ProcessExecutor::escape(arg)) .collect::<Vec<_>>() .join(" "); - // reusing the output from $this->io is mostly needed for tests, but generally speaking - // it does not hurt to keep the same stream as the current Application - let output = if let Some(_console_io) = - self.io.as_any().downcast_ref::<ConsoleIO>() - { - // TODO(plugin): \ReflectionProperty to read private `output` from ConsoleIO - // is required by the original PHP — needs user-decided porting strategy. - let _refl_php_version_gate = PHP_VERSION_ID < 80100; - todo!("\\ReflectionProperty on ConsoleIO::$output") + + // @putenv does not receive arguments + let mut exec = if strpos(&callable_str, "@putenv ") == Some(0) { + callable_str.clone() + } else if str_contains(&callable_str, "@additional_args") { + str_replace("@additional_args", &args, &callable_str) } else { - ConsoleOutput::new() + format!( + "{}{}", + callable_str, + if args == "" { + "".to_string() + } else { + format!(" {}", args) + } + ) }; - let input_str = event - .get_flags() - .get("script-alias-input") - .and_then(|v| v.as_string()) - .unwrap_or(&args) - .to_string(); - Ok(app.run(StringInput::new(input_str), output)) - })(); - match result { - Ok(v) => r#return = v, - Err(e) => { - let message = - "Script %s handling the %s event terminated with an exception"; + + if self.io.is_verbose() { self.io.write_error( - PhpMixed::String(format!( - "<error>{}</error>", - sprintf( - message, - &[ - PhpMixed::String(callable_str.clone()), - PhpMixed::String(event.get_name().to_string()), - ], - ) + PhpMixed::String(sprintf( + "> %s: %s", + &[ + PhpMixed::String(event.get_name().to_string()), + PhpMixed::String(exec.clone()), + ], )), true, - <dyn IOInterface>::QUIET, + <dyn IOInterface>::NORMAL, + ); + } else if self.event_needs_to_output(event) { + self.io.write_error( + PhpMixed::String(sprintf( + "> %s", + &[PhpMixed::String(exec.clone())], + )), + true, + <dyn IOInterface>::NORMAL, ); - return Err(e); } - } - } - Callable::String(callable_str) => { - let args = additional_args - .iter() - .map(|arg| ProcessExecutor::escape(arg)) - .collect::<Vec<_>>() - .join(" "); - // @putenv does not receive arguments - let mut exec = if strpos(&callable_str, "@putenv ") == Some(0) { - callable_str.clone() - } else if str_contains(&callable_str, "@additional_args") { - str_replace("@additional_args", &args, &callable_str) - } else { - format!( - "{}{}", - callable_str, - if args == "" { - "".to_string() - } else { - format!(" {}", args) - } - ) - }; - - if self.io.is_verbose() { - self.io.write_error( - PhpMixed::String(sprintf( - "> %s: %s", - &[ - PhpMixed::String(event.get_name().to_string()), - PhpMixed::String(exec.clone()), - ], - )), - true, - <dyn IOInterface>::NORMAL, - ); - } else if self.event_needs_to_output(event) { - self.io.write_error( - PhpMixed::String(sprintf( - "> %s", - &[PhpMixed::String(exec.clone())], - )), - true, - <dyn IOInterface>::NORMAL, - ); - } - - let possible_local_binaries = - self.composer.get_package().get_binaries(); - if !possible_local_binaries.is_empty() { - for local_exec in &possible_local_binaries { - if Preg::is_match( - &format!("{{\\b{}$}}", preg_quote(&callable_str, None)), - local_exec, - ) - .unwrap_or(false) - { - let caller = BinaryInstaller::determine_binary_caller(local_exec); - exec = Preg::replace( - &format!("{{^{}}}", preg_quote(&callable_str, None)), - &format!("{} {}", caller, local_exec), - &exec, + let possible_local_binaries = self.composer.get_package().get_binaries(); + if !possible_local_binaries.is_empty() { + for local_exec in &possible_local_binaries { + if Preg::is_match( + &format!("{{\\b{}$}}", preg_quote(&callable_str, None)), + local_exec, ) - .unwrap_or(exec); - break; + .unwrap_or(false) + { + let caller = + BinaryInstaller::determine_binary_caller(local_exec); + exec = Preg::replace( + &format!("{{^{}}}", preg_quote(&callable_str, None)), + &format!("{} {}", caller, local_exec), + &exec, + ) + .unwrap_or(exec); + break; + } } } - } - if strpos(&exec, "@putenv ") == Some(0) { - if strpos(&exec, "=").is_none() { - Platform::clear_env(&substr(&exec, 8, None)); - } else { - let parts: Vec<&str> = - substr(&exec, 8, None).splitn(2, '=').collect::<Vec<_>>() + if strpos(&exec, "@putenv ") == Some(0) { + if strpos(&exec, "=").is_none() { + Platform::clear_env(&substr(&exec, 8, None)); + } else { + let parts: Vec<&str> = substr(&exec, 8, None) + .splitn(2, '=') + .collect::<Vec<_>>() .iter() .map(|s| *s) .collect(); - let var = parts[0].to_string(); - let value = parts[1].to_string(); - Platform::put_env(&var, &value); - } + let var = parts[0].to_string(); + let value = parts[1].to_string(); + Platform::put_env(&var, &value); + } - continue; - } - if strpos(&exec, "@php ") == Some(0) { - let mut path_and_args = substr(&exec, 5, None); - if Platform::is_windows() { - path_and_args = Preg::replace_callback( - "{^\\S+}", - |m| str_replace("/", "\\", &m[0]), + continue; + } + if strpos(&exec, "@php ") == Some(0) { + let mut path_and_args = substr(&exec, 5, None); + if Platform::is_windows() { + path_and_args = Preg::replace_callback( + "{^\\S+}", + |m| str_replace("/", "\\", &m[0]), + &path_and_args, + ) + .unwrap_or(path_and_args); + } + // match somename (not in quote, and not a qualified path) and if it is not a valid path from CWD then try to find it + // in $PATH. This allows support for `@php foo` where foo is a binary name found in PATH but not an actual relative path + let mat = Preg::is_match_strict_groups( + "{^[^\\'\"\\s/\\\\]+}", &path_and_args, ) - .unwrap_or(path_and_args); - } - // match somename (not in quote, and not a qualified path) and if it is not a valid path from CWD then try to find it - // in $PATH. This allows support for `@php foo` where foo is a binary name found in PATH but not an actual relative path - let mat = Preg::is_match_strict_groups( - "{^[^\\'\"\\s/\\\\]+}", - &path_and_args, - ) - .ok() - .flatten(); - if let Some(m) = mat { - if !file_exists(&m[0]) { - let finder = ExecutableFinder::new(); - if let Some(path_to_exec) = finder.find(&m[0]) { - let mut path_to_exec = path_to_exec; - if Platform::is_windows() { - let exec_without_ext = Preg::replace( - "{\\.(exe|bat|cmd|com)$}i", - "", - &path_to_exec, - ) - .unwrap_or(path_to_exec.clone()); - // prefer non-extension file if it exists when executing with PHP - if file_exists(&exec_without_ext) { - path_to_exec = exec_without_ext; + .ok() + .flatten(); + if let Some(m) = mat { + if !file_exists(&m[0]) { + let finder = ExecutableFinder::new(); + if let Some(path_to_exec) = finder.find(&m[0]) { + let mut path_to_exec = path_to_exec; + if Platform::is_windows() { + let exec_without_ext = Preg::replace( + "{\\.(exe|bat|cmd|com)$}i", + "", + &path_to_exec, + ) + .unwrap_or(path_to_exec.clone()); + // prefer non-extension file if it exists when executing with PHP + if file_exists(&exec_without_ext) { + path_to_exec = exec_without_ext; + } } + path_and_args = format!( + "{}{}", + path_to_exec, + substr(&path_and_args, strlen(&m[0]), None) + ); } - path_and_args = format!( - "{}{}", - path_to_exec, - substr(&path_and_args, strlen(&m[0]), None) - ); } } - } - exec = format!("{} {}", self.get_php_exec_command()?, path_and_args); - } else { - let finder = PhpExecutableFinder::new(); - let php_path = finder.find(false); - if let Some(ref pp) = php_path { - Platform::put_env("PHP_BINARY", pp); - } + exec = format!("{} {}", self.get_php_exec_command()?, path_and_args); + } else { + let finder = PhpExecutableFinder::new(); + let php_path = finder.find(false); + if let Some(ref pp) = php_path { + Platform::put_env("PHP_BINARY", pp); + } - if Platform::is_windows() { - exec = Preg::replace_callback( - "{^\\S+}", - |m| str_replace("/", "\\", &m[0]), - &exec, - ) - .unwrap_or(exec); + if Platform::is_windows() { + exec = Preg::replace_callback( + "{^\\S+}", + |m| str_replace("/", "\\", &m[0]), + &exec, + ) + .unwrap_or(exec); + } } - } - // if composer is being executed, make sure it runs the expected composer from current path - // resolution, even if bin-dir contains composer too because the project requires composer/composer - // see https://github.com/composer/composer/issues/8748 - if strpos(&exec, "composer ") == Some(0) { - exec = format!( - "{} {}{}", - self.get_php_exec_command()?, - ProcessExecutor::escape( - &Platform::get_env("COMPOSER_BINARY").unwrap_or_default() - ), - substr(&exec, 8, None) - ); - } + // if composer is being executed, make sure it runs the expected composer from current path + // resolution, even if bin-dir contains composer too because the project requires composer/composer + // see https://github.com/composer/composer/issues/8748 + if strpos(&exec, "composer ") == Some(0) { + exec = format!( + "{} {}{}", + self.get_php_exec_command()?, + ProcessExecutor::escape( + &Platform::get_env("COMPOSER_BINARY").unwrap_or_default() + ), + substr(&exec, 8, None) + ); + } - let exit_code = self.execute_tty(&exec)?; - if exit_code != 0 { - self.io.write_error( + let exit_code = self.execute_tty(&exec)?; + if exit_code != 0 { + self.io.write_error( PhpMixed::String(sprintf( &format!( "<error>Script %s handling the %s event returned with error code {}</error>", @@ -829,21 +831,22 @@ impl EventDispatcher { <dyn IOInterface>::QUIET, ); - return Err(anyhow::anyhow!(ScriptExecutionException( - RuntimeException { - message: format!( - "Error Output: {}", - self.process.get_error_output() - ), - code: exit_code, - } - ))); + return Err(anyhow::anyhow!(ScriptExecutionException( + RuntimeException { + message: format!( + "Error Output: {}", + self.process.get_error_output() + ), + code: exit_code, + } + ))); + } + } + _ => { + // unreachable in practice — the first match arm guard handles non-string callables. } } - _ => { - // unreachable in practice — the first match arm guard handles non-string callables. - } - } } + } return_max = max_i64(return_max, r#return); @@ -866,11 +869,7 @@ impl EventDispatcher { fn do_dispatch_package(&mut self, event: PackageEvent) -> anyhow::Result<i64> { // TODO(plugin): preserve PackageEvent identity for `instanceof` checks above. - let base = Event::new( - event.get_name().to_string(), - Vec::new(), - IndexMap::new(), - ); + let base = Event::new(event.get_name().to_string(), Vec::new(), IndexMap::new()); self.do_dispatch(base) } @@ -1088,11 +1087,7 @@ impl EventDispatcher { _ => return Vec::new(), }; - if self - .skip_scripts - .iter() - .any(|s| s == event.get_name()) - { + if self.skip_scripts.iter().any(|s| s == event.get_name()) { self.io.write_error( PhpMixed::String(format!( "Skipped script listeners for <info>{}</info> because of COMPOSER_SKIP_SCRIPTS", @@ -1290,11 +1285,8 @@ impl EventDispatcher { self.previous_hash = Some(hash_value); - let package_map = generator.build_package_map( - composer.get_installation_manager(), - package, - &packages, - ); + let package_map = + generator.build_package_map(composer.get_installation_manager(), package, &packages); let map = generator.parse_autoloads(&package_map, package); if self.loader.is_some() { diff --git a/crates/shirabe/src/event_dispatcher/mod.rs b/crates/shirabe/src/event_dispatcher/mod.rs new file mode 100644 index 0000000..97609be --- /dev/null +++ b/crates/shirabe/src/event_dispatcher/mod.rs @@ -0,0 +1,4 @@ +pub mod event; +pub mod event_dispatcher; +pub mod event_subscriber_interface; +pub mod script_execution_exception; diff --git a/crates/shirabe/src/exception/mod.rs b/crates/shirabe/src/exception/mod.rs new file mode 100644 index 0000000..d273f7e --- /dev/null +++ b/crates/shirabe/src/exception/mod.rs @@ -0,0 +1,2 @@ +pub mod irrecoverable_download_exception; +pub mod no_ssl_exception; diff --git a/crates/shirabe/src/factory.rs b/crates/shirabe/src/factory.rs index 6411027..eee2b88 100644 --- a/crates/shirabe/src/factory.rs +++ b/crates/shirabe/src/factory.rs @@ -6,11 +6,11 @@ use shirabe_external_packages::symfony::component::console::formatter::output_fo use shirabe_external_packages::symfony::component::console::formatter::output_formatter_style::OutputFormatterStyle; use shirabe_external_packages::symfony::component::console::output::console_output::ConsoleOutput; use shirabe_php_shim::{ - array_keys, array_replace_recursive, class_exists, dirname, extension_loaded, file_exists, - file_get_contents, file_put_contents, implode, in_array, is_array, is_dir, is_file, is_string, - json_decode, pathinfo, realpath, str_replace, strpos, strtr, substr, trim, InvalidArgumentException, - Phar, PhpMixed, RuntimeException, UnexpectedValueException, ZipArchive, PATHINFO_EXTENSION, - PHP_EOL, + InvalidArgumentException, PATHINFO_EXTENSION, PHP_EOL, Phar, PhpMixed, RuntimeException, + UnexpectedValueException, ZipArchive, array_keys, array_replace_recursive, class_exists, + dirname, extension_loaded, file_exists, file_get_contents, file_put_contents, implode, + in_array, is_array, is_dir, is_file, is_string, json_decode, pathinfo, realpath, str_replace, + strpos, strtr, substr, trim, }; use crate::autoload::autoload_generator::AutoloadGenerator; @@ -46,8 +46,8 @@ use crate::json::json_validation_exception::JsonValidationException; use crate::package::archiver::archive_manager::ArchiveManager; use crate::package::archiver::phar_archiver::PharArchiver; use crate::package::archiver::zip_archiver::ZipArchiver; -use crate::package::locker::Locker; use crate::package::loader::root_package_loader::RootPackageLoader; +use crate::package::locker::Locker; use crate::package::root_package_interface::RootPackageInterface; use crate::package::version::version_guesser::VersionGuesser; use crate::package::version::version_parser::VersionParser; @@ -101,7 +101,10 @@ impl Factory { } if Platform::is_windows() { - if Platform::get_env("APPDATA").map(|s| s.is_empty()).unwrap_or(true) { + if Platform::get_env("APPDATA") + .map(|s| s.is_empty()) + .unwrap_or(true) + { return Err(anyhow::anyhow!(RuntimeException { message: "The APPDATA or COMPOSER_HOME environment variable must be set for composer to run correctly" @@ -135,8 +138,8 @@ impl Factory { // select first dir which exists of: $XDG_CONFIG_HOME/composer or ~/.composer for dir in &dirs { let dir_copy = dir.clone(); - let exists = Silencer::call(|| Ok::<bool, anyhow::Error>(is_dir(&dir_copy))) - .unwrap_or(false); + let exists = + Silencer::call(|| Ok::<bool, anyhow::Error>(is_dir(&dir_copy))).unwrap_or(false); if exists { return Ok(dir.clone()); } @@ -176,16 +179,13 @@ impl Factory { { let from = format!("{}/cache", home); let to = format!("{}/Library/Caches/composer", user_dir); - let _ = Silencer::call(|| { - Ok::<bool, anyhow::Error>(Platform::rename(&from, &to)) - }); + let _ = Silencer::call(|| Ok::<bool, anyhow::Error>(Platform::rename(&from, &to))); } return Ok(format!("{}/Library/Caches/composer", user_dir)); } - if home == format!("{}/.composer", user_dir).as_str() - && is_dir(&format!("{}/cache", home)) + if home == format!("{}/.composer", user_dir).as_str() && is_dir(&format!("{}/cache", home)) { return Ok(format!("{}/cache", home)); } @@ -229,8 +229,13 @@ impl Factory { Ok(home.to_string()) } - pub fn create_config(io: Option<&dyn IOInterface>, cwd: Option<&str>) -> anyhow::Result<Config> { - let cwd = cwd.map(|s| s.to_string()).unwrap_or_else(|| Platform::get_cwd(true)); + pub fn create_config( + io: Option<&dyn IOInterface>, + cwd: Option<&str>, + ) -> anyhow::Result<Config> { + let cwd = cwd + .map(|s| s.to_string()) + .unwrap_or_else(|| Platform::get_cwd(true)); let mut config = Config::new(true, cwd); @@ -263,7 +268,12 @@ impl Factory { <dyn IOInterface>::DEBUG, ); } - Self::validate_json_schema(io, ValidateJsonInput::File(file.clone()), JsonFile::LAX_SCHEMA, None)?; + Self::validate_json_schema( + io, + ValidateJsonInput::File(file.clone()), + JsonFile::LAX_SCHEMA, + None, + )?; config.merge(file.read()?, file.get_path().to_string()); } config.set_config_source(JsonConfigSource::new(file.clone(), false)); @@ -298,11 +308,7 @@ impl Factory { } // load global auth file - let auth_file = JsonFile::new( - format!("{}/auth.json", config.get_str("home")?), - None, - io, - ); + let auth_file = JsonFile::new(format!("{}/auth.json", config.get_str("home")?), None, io); if auth_file.exists() { if let Some(io_ref) = io { io_ref.write_error( @@ -318,8 +324,16 @@ impl Factory { None, )?; let mut wrapped: IndexMap<String, PhpMixed> = IndexMap::new(); - wrapped.insert("config".to_string(), PhpMixed::Array(auth_file.read()? - .into_iter().map(|(k, v)| (k, Box::new(v))).collect())); + wrapped.insert( + "config".to_string(), + PhpMixed::Array( + auth_file + .read()? + .into_iter() + .map(|(k, v)| (k, Box::new(v))) + .collect(), + ), + ); config.merge(wrapped, auth_file.get_path().to_string()); } config.set_auth_config_source(JsonConfigSource::new(auth_file, true)); @@ -352,13 +366,19 @@ impl Factory { } pub fn get_lock_file(composer_file: &str) -> String { - let ext = pathinfo(PhpMixed::String(composer_file.to_string()), PATHINFO_EXTENSION); + let ext = pathinfo( + PhpMixed::String(composer_file.to_string()), + PATHINFO_EXTENSION, + ); let is_json = match ext { PhpMixed::String(s) => s == "json", _ => false, }; if is_json { - format!("{}lock", substr(composer_file, 0, Some(composer_file.len() as i64 - 4))) + format!( + "{}lock", + substr(composer_file, 0, Some(composer_file.len() as i64 - 4)) + ) } else { format!("{}.lock", composer_file) } @@ -372,7 +392,11 @@ impl Factory { ); styles.insert( "warning".to_string(), - OutputFormatterStyle::new(Some("black".to_string()), Some("yellow".to_string()), Vec::new()), + OutputFormatterStyle::new( + Some("black".to_string()), + Some("yellow".to_string()), + Vec::new(), + ), ); styles } @@ -438,10 +462,11 @@ impl Factory { if !Platform::is_input_completion_process() { if let Err(e) = file.validate_schema(JsonFile::LAX_SCHEMA) { if let Some(jve) = e.downcast_ref::<JsonValidationException>() { - let errors = - format!(" - {}", implode(&format!("{} - ", PHP_EOL), jve.get_errors())); - let message = - format!("{}:{}{}", jve.get_message(), PHP_EOL, errors); + let errors = format!( + " - {}", + implode(&format!("{} - ", PHP_EOL), jve.get_errors()) + ); + let message = format!("{}:{}{}", jve.get_message(), PHP_EOL, errors); return Err(anyhow::anyhow!(JsonValidationException::new( message, jve.get_errors().clone(), @@ -474,7 +499,11 @@ impl Factory { <dyn IOInterface>::DEBUG, ); config.set_config_source(JsonConfigSource::new( - JsonFile::new(realpath(composer_file_path).unwrap_or_default(), None, Some(io)), + JsonFile::new( + realpath(composer_file_path).unwrap_or_default(), + None, + Some(io), + ), false, )); @@ -560,37 +589,26 @@ impl Factory { composer.set_loop(r#loop.clone()); // initialize event dispatcher - let mut dispatcher = EventDispatcher::new( - composer.as_partial(), - io.clone_box(), - Some(process.clone()), - ); + let mut dispatcher = + EventDispatcher::new(composer.as_partial(), io.clone_box(), Some(process.clone())); dispatcher.set_run_scripts(!disable_scripts); composer.set_event_dispatcher(dispatcher.clone()); // initialize repository manager - let rm = RepositoryFactory::manager( - io, - &config, - &http_downloader, - &dispatcher, - &process, - )?; + let rm = RepositoryFactory::manager(io, &config, &http_downloader, &dispatcher, &process)?; composer.set_repository_manager(rm.clone()); // force-set the version of the global package if not defined as // guessing it adds no value and only takes time if !full_load && !local_config_data.contains_key("version") { - local_config_data.insert( - "version".to_string(), - PhpMixed::String("1.0.0".to_string()), - ); + local_config_data.insert("version".to_string(), PhpMixed::String("1.0.0".to_string())); } // load package let parser = VersionParser::new(); let guesser = VersionGuesser::new(&config, process.clone(), parser.clone()); - let mut loader = self.load_root_package(rm.clone(), config.clone(), parser, guesser, io.clone_box()); + let mut loader = + self.load_root_package(rm.clone(), config.clone(), parser, guesser, io.clone_box()); let package = loader.load( local_config_data .iter() @@ -602,15 +620,31 @@ impl Factory { composer.set_package(package); // load local repository - self.add_local_repository(io, rm.clone(), &vendor_dir, composer.get_package(), Some(&process)); + self.add_local_repository( + io, + rm.clone(), + &vendor_dir, + composer.get_package(), + Some(&process), + ); // initialize installation manager - let im = self.create_installation_manager(r#loop.clone(), io.clone_box(), Some(dispatcher.clone())); + let im = self.create_installation_manager( + r#loop.clone(), + io.clone_box(), + Some(dispatcher.clone()), + ); composer.set_installation_manager(im.clone()); if let PartialComposerOrComposer::Full(ref mut composer_full) = composer { // initialize download manager - let dm = self.create_download_manager(io, &config, &http_downloader, &process, Some(&dispatcher))?; + let dm = self.create_download_manager( + io, + &config, + &http_downloader, + &process, + Some(&dispatcher), + )?; composer_full.set_download_manager(dm.clone()); // initialize autoload generator @@ -629,8 +663,7 @@ impl Factory { if let PartialComposerOrComposer::Full(ref mut composer_full) = composer { if let Some(ref composer_file_path) = composer_file { let lock_file = Self::get_lock_file(composer_file_path); - let lock_enabled = - config.get("lock").and_then(|v| v.as_bool()).unwrap_or(true); + let lock_enabled = config.get("lock").and_then(|v| v.as_bool()).unwrap_or(true); if !lock_enabled && file_exists(&lock_file) { io.write_error( PhpMixed::String(format!( @@ -687,7 +720,12 @@ impl Factory { ); } - let mut pm = self.create_plugin_manager(io, composer_full, global_composer.as_ref(), disable_plugins); + let mut pm = self.create_plugin_manager( + io, + composer_full, + global_composer.as_ref(), + disable_plugins, + ); composer_full.set_plugin_manager(pm.clone()); if composer_full.is_global() { @@ -757,7 +795,10 @@ impl Factory { full_load: bool, ) -> Option<PartialComposer> { // make sure if disable plugins was 'local' it is now turned off - let disable_plugins = if matches!(disable_plugins, DisablePlugins::Global | DisablePlugins::All) { + let disable_plugins = if matches!( + disable_plugins, + DisablePlugins::Global | DisablePlugins::All + ) { DisablePlugins::All } else { DisablePlugins::None @@ -777,10 +818,7 @@ impl Factory { Ok(c) => Some(c.into_partial()), Err(e) => { io.write_error( - PhpMixed::String(format!( - "Failed to initialize global composer: {}", - e - )), + PhpMixed::String(format!("Failed to initialize global composer: {}", e)), true, <dyn IOInterface>::DEBUG, ); @@ -851,23 +889,48 @@ impl Factory { dm.set_downloader( "git", - Box::new(GitDownloader::new(io.clone_box(), config.clone(), process.clone(), fs.clone())), + Box::new(GitDownloader::new( + io.clone_box(), + config.clone(), + process.clone(), + fs.clone(), + )), ); dm.set_downloader( "svn", - Box::new(SvnDownloader::new(io.clone_box(), config.clone(), process.clone(), fs.clone())), + Box::new(SvnDownloader::new( + io.clone_box(), + config.clone(), + process.clone(), + fs.clone(), + )), ); dm.set_downloader( "fossil", - Box::new(FossilDownloader::new(io.clone_box(), config.clone(), process.clone(), fs.clone())), + Box::new(FossilDownloader::new( + io.clone_box(), + config.clone(), + process.clone(), + fs.clone(), + )), ); dm.set_downloader( "hg", - Box::new(HgDownloader::new(io.clone_box(), config.clone(), process.clone(), fs.clone())), + Box::new(HgDownloader::new( + io.clone_box(), + config.clone(), + process.clone(), + fs.clone(), + )), ); dm.set_downloader( "perforce", - Box::new(PerforceDownloader::new(io.clone_box(), config.clone(), process.clone(), fs.clone())), + Box::new(PerforceDownloader::new( + io.clone_box(), + config.clone(), + process.clone(), + fs.clone(), + )), ); dm.set_downloader( "zip", @@ -993,7 +1056,12 @@ impl Factory { global_composer: Option<&PartialComposer>, disable_plugins: DisablePlugins, ) -> PluginManager { - PluginManager::new(io.clone_box(), composer.clone(), global_composer.cloned(), disable_plugins) + PluginManager::new( + io.clone_box(), + composer.clone(), + global_composer.cloned(), + disable_plugins, + ) } pub fn create_installation_manager( @@ -1014,10 +1082,7 @@ impl Factory { ) { let fs = Filesystem::new(process.cloned()); let bin_dir = trim( - &composer - .get_config() - .get_str("bin-dir") - .unwrap_or_default(), + &composer.get_config().get_str("bin-dir").unwrap_or_default(), "/", ); let bin_compat = composer @@ -1031,13 +1096,8 @@ impl Factory { .unwrap_or_default(), "/", ); - let binary_installer = BinaryInstaller::new( - io.clone_box(), - bin_dir, - bin_compat, - fs.clone(), - vendor_dir, - ); + let binary_installer = + BinaryInstaller::new(io.clone_box(), bin_dir, bin_compat, fs.clone(), vendor_dir); let mut im = im.clone(); im.add_installer(Box::new(LibraryInstaller::new( @@ -1056,11 +1116,7 @@ impl Factory { im.add_installer(Box::new(MetapackageInstaller::new(io.clone_box()))); } - fn purge_packages( - &self, - repo: &dyn InstalledRepositoryInterface, - im: &InstallationManager, - ) { + fn purge_packages(&self, repo: &dyn InstalledRepositoryInterface, im: &InstallationManager) { for package in repo.get_packages() { if !im.is_package_installed(repo, package.as_ref()) { // TODO(phase-b): mutable access on repo trait object @@ -1182,14 +1238,21 @@ impl Factory { let mut ssl_map: IndexMap<String, Box<PhpMixed>> = existing_ssl; ssl_map.insert( "capath".to_string(), - Box::new(PhpMixed::String(config.get_str("capath").unwrap_or_default())), + Box::new(PhpMixed::String( + config.get_str("capath").unwrap_or_default(), + )), ); http_downloader_options.insert("ssl".to_string(), PhpMixed::Array(ssl_map)); } http_downloader_options = array_replace_recursive(http_downloader_options, options.clone()); } - let http_downloader = match HttpDownloader::new_full(io.clone_box(), config.clone(), http_downloader_options, disable_tls) { + let http_downloader = match HttpDownloader::new_full( + io.clone_box(), + config.clone(), + http_downloader_options, + disable_tls, + ) { Ok(h) => h, Err(e) => { if let Some(te) = e.downcast_ref::<TransportException>() { @@ -1333,7 +1396,10 @@ impl Factory { <dyn IOInterface>::NORMAL, ); } else { - return Err(anyhow::anyhow!(UnexpectedValueException { message: msg, code: 0 })); + return Err(anyhow::anyhow!(UnexpectedValueException { + message: msg, + code: 0 + })); } } else { return Err(e); diff --git a/crates/shirabe/src/filter/mod.rs b/crates/shirabe/src/filter/mod.rs new file mode 100644 index 0000000..134a679 --- /dev/null +++ b/crates/shirabe/src/filter/mod.rs @@ -0,0 +1 @@ +pub mod platform_requirement_filter; diff --git a/crates/shirabe/src/filter/platform_requirement_filter/ignore_list_platform_requirement_filter.rs b/crates/shirabe/src/filter/platform_requirement_filter/ignore_list_platform_requirement_filter.rs index 57d5258..29f3c52 100644 --- a/crates/shirabe/src/filter/platform_requirement_filter/ignore_list_platform_requirement_filter.rs +++ b/crates/shirabe/src/filter/platform_requirement_filter/ignore_list_platform_requirement_filter.rs @@ -37,7 +37,12 @@ impl IgnoreListPlatformRequirementFilter { }) } - pub fn filter_constraint(&self, req: &str, constraint: Box<dyn ConstraintInterface>, allow_upper_bound_override: bool) -> anyhow::Result<Box<dyn ConstraintInterface>> { + pub fn filter_constraint( + &self, + req: &str, + constraint: Box<dyn ConstraintInterface>, + allow_upper_bound_override: bool, + ) -> anyhow::Result<Box<dyn ConstraintInterface>> { if !PlatformRepository::is_platform_package(req) { return Ok(constraint); } @@ -55,7 +60,10 @@ impl IgnoreListPlatformRequirementFilter { if let Some(last) = last { if last.get_end().to_string() != Interval::until_positive_infinity().to_string() { let constraint = Box::new(MultiConstraint::new( - vec![constraint, Box::new(Constraint::new(">=", last.get_end().get_version()))], + vec![ + constraint, + Box::new(Constraint::new(">=", last.get_end().get_version())), + ], false, )); return Ok(constraint); diff --git a/crates/shirabe/src/filter/platform_requirement_filter/mod.rs b/crates/shirabe/src/filter/platform_requirement_filter/mod.rs new file mode 100644 index 0000000..fdd7feb --- /dev/null +++ b/crates/shirabe/src/filter/platform_requirement_filter/mod.rs @@ -0,0 +1,5 @@ +pub mod ignore_all_platform_requirement_filter; +pub mod ignore_list_platform_requirement_filter; +pub mod ignore_nothing_platform_requirement_filter; +pub mod platform_requirement_filter_factory; +pub mod platform_requirement_filter_interface; diff --git a/crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_factory.rs b/crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_factory.rs index 8dda266..59340cb 100644 --- a/crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_factory.rs +++ b/crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_factory.rs @@ -1,18 +1,20 @@ //! ref: composer/src/Composer/Filter/PlatformRequirementFilter/PlatformRequirementFilterFactory.php -use anyhow::Result; -use shirabe_php_shim::{InvalidArgumentException, PhpMixed}; use crate::filter::platform_requirement_filter::{ ignore_all_platform_requirement_filter::IgnoreAllPlatformRequirementFilter, ignore_list_platform_requirement_filter::IgnoreListPlatformRequirementFilter, ignore_nothing_platform_requirement_filter::IgnoreNothingPlatformRequirementFilter, platform_requirement_filter_interface::PlatformRequirementFilterInterface, }; +use anyhow::Result; +use shirabe_php_shim::{InvalidArgumentException, PhpMixed}; pub struct PlatformRequirementFilterFactory; impl PlatformRequirementFilterFactory { - pub fn from_bool_or_list(bool_or_list: PhpMixed) -> Result<Box<dyn PlatformRequirementFilterInterface>> { + pub fn from_bool_or_list( + bool_or_list: PhpMixed, + ) -> Result<Box<dyn PlatformRequirementFilterInterface>> { match bool_or_list { PhpMixed::Bool(b) => { if b { @@ -21,9 +23,9 @@ impl PlatformRequirementFilterFactory { Ok(Self::ignore_nothing()) } } - list_or_array @ (PhpMixed::List(_) | PhpMixed::Array(_)) => { - Ok(Box::new(IgnoreListPlatformRequirementFilter::new(list_or_array))) - } + list_or_array @ (PhpMixed::List(_) | PhpMixed::Array(_)) => Ok(Box::new( + IgnoreListPlatformRequirementFilter::new(list_or_array), + )), other => Err(anyhow::anyhow!(InvalidArgumentException { message: format!( "PlatformRequirementFilter: Unknown $boolOrList parameter {}. Please report at https://github.com/composer/composer/issues/new.", diff --git a/crates/shirabe/src/installed_versions.rs b/crates/shirabe/src/installed_versions.rs index 1014d0d..b151d96 100644 --- a/crates/shirabe/src/installed_versions.rs +++ b/crates/shirabe/src/installed_versions.rs @@ -5,9 +5,9 @@ use std::sync::Mutex; use anyhow::Result; use indexmap::IndexMap; use shirabe_php_shim::{ - array_flip, array_keys, array_merge, call_user_func_array, implode, is_file, method_exists, - php_dir, require_php_file, strtr_array, substr, trigger_error, OutOfBoundsException, PhpMixed, - E_USER_DEPRECATED, + E_USER_DEPRECATED, OutOfBoundsException, PhpMixed, array_flip, array_keys, array_merge, + call_user_func_array, implode, is_file, method_exists, php_dir, require_php_file, strtr_array, + substr, trigger_error, }; use shirabe_semver::version_parser::VersionParser; @@ -185,7 +185,10 @@ impl InstalledVersions { let Some(versions) = installed.get("versions").and_then(|v| v.as_array()) else { continue; }; - let Some(pkg) = versions.get(package_name).and_then(|v| v.as_array()).cloned() + let Some(pkg) = versions + .get(package_name) + .and_then(|v| v.as_array()) + .cloned() else { continue; }; @@ -455,14 +458,11 @@ impl InstalledVersions { fn get_self_dir() -> String { let mut self_dir = SELF_DIR.lock().unwrap(); if self_dir.is_none() { - *self_dir = Some(strtr_array( - &php_dir(), - &{ - let mut m = IndexMap::new(); - m.insert("\\".to_string(), "/".to_string()); - m - }, - )); + *self_dir = Some(strtr_array(&php_dir(), &{ + let mut m = IndexMap::new(); + m.insert("\\".to_string(), "/".to_string()); + m + })); } self_dir.clone().unwrap() @@ -492,14 +492,16 @@ impl InstalledVersions { m.insert("\\".to_string(), "/".to_string()); m }); - let cached = INSTALLED_BY_VENDOR.lock().unwrap().get(&vendor_dir).cloned(); + let cached = INSTALLED_BY_VENDOR + .lock() + .unwrap() + .get(&vendor_dir) + .cloned(); if let Some(cached) = cached { installed.push(cached); } else if is_file(&format!("{}/composer/installed.php", vendor_dir)) { - let required = require_php_file(&format!( - "{}/composer/installed.php", - vendor_dir, - )); + let required = + require_php_file(&format!("{}/composer/installed.php", vendor_dir,)); let required_map: IndexMap<String, PhpMixed> = required .as_array() .cloned() diff --git a/crates/shirabe/src/installer.rs b/crates/shirabe/src/installer.rs index bcba5e9..3f6180f 100644 --- a/crates/shirabe/src/installer.rs +++ b/crates/shirabe/src/installer.rs @@ -1,13 +1,28 @@ //! ref: composer/src/Composer/Installer.php +pub mod binary_installer; +pub mod binary_presence_interface; +pub mod installation_manager; +pub mod installer_event; +pub mod installer_events; +pub mod installer_interface; +pub mod library_installer; +pub mod metapackage_installer; +pub mod noop_installer; +pub mod package_event; +pub mod package_events; +pub mod plugin_installer; +pub mod project_installer; +pub mod suggested_packages_reporter; + use indexmap::IndexMap; use shirabe_external_packages::seld::json_lint::parsing_exception::ParsingException; use shirabe_php_shim::{ - array_flip, array_map, array_merge, array_unique, array_values, clone, count, defined, - gc_collect_cycles, gc_disable, gc_enable, get_class, implode, in_array, intval, is_dir, - is_numeric, is_string, max_i64, sprintf, strcmp, strpos, strtolower, touch, trigger_error, - usort, RuntimeException, + RuntimeException, array_flip, array_map, array_merge, array_unique, array_values, clone, count, + defined, gc_collect_cycles, gc_disable, gc_enable, get_class, implode, in_array, intval, + is_dir, is_numeric, is_string, max_i64, sprintf, strcmp, strpos, strtolower, touch, + trigger_error, usort, }; use shirabe_semver; @@ -240,7 +255,8 @@ impl Installer { } else { ScriptEvents::PRE_INSTALL_CMD }; - self.event_dispatcher.dispatch_script(event_name, self.dev_mode); + self.event_dispatcher + .dispatch_script(event_name, self.dev_mode); } self.download_manager.set_prefer_source(self.prefer_source); @@ -266,7 +282,11 @@ impl Installer { Err(e) => { if self.execute_operations && self.install - && self.config.get("notify-on-install").as_bool().unwrap_or(false) + && self + .config + .get("notify-on-install") + .as_bool() + .unwrap_or(false) { self.installation_manager.notify_installs(&*self.io); } @@ -277,14 +297,20 @@ impl Installer { if self.execute_operations && self.install - && self.config.get("notify-on-install").as_bool().unwrap_or(false) + && self + .config + .get("notify-on-install") + .as_bool() + .unwrap_or(false) { self.installation_manager.notify_installs(&*self.io); } if self.update { let installed_repo = InstalledRepository::new(vec![ - self.locker.get_locked_repository(self.dev_mode)?.clone_box(), + self.locker + .get_locked_repository(self.dev_mode)? + .clone_box(), Box::new(self.create_platform_repo(false)), Box::new(RootPackageRepository::new(clone(&self.package))), ]); @@ -315,19 +341,18 @@ impl Installer { self.io.write_error(&sprintf( "<warning>Package %s is abandoned, you should avoid using it. %s.</warning>", - &[ - complete.get_pretty_name().into(), - replacement.into(), - ], + &[complete.get_pretty_name().into(), replacement.into()], )); } if self.dump_autoloader { // write autoloader if self.optimize_autoloader { - self.io.write_error("<info>Generating optimized autoload files</info>"); + self.io + .write_error("<info>Generating optimized autoload files</info>"); } else { - self.io.write_error("<info>Generating autoload files</info>"); + self.io + .write_error("<info>Generating autoload files</info>"); } self.autoload_generator @@ -395,7 +420,8 @@ impl Installer { } else { ScriptEvents::POST_INSTALL_CMD }; - self.event_dispatcher.dispatch_script(event_name, self.dev_mode); + self.event_dispatcher + .dispatch_script(event_name, self.dev_mode); } // re-enable GC except on HHVM which triggers a warning here @@ -447,13 +473,11 @@ impl Installer { } Err(e) => { if let Some(te) = e.downcast_ref::<TransportException>() { - self.io.error(&format!("Failed to audit {} packages.", target)); + self.io + .error(&format!("Failed to audit {} packages.", target)); if self.io.is_verbose() { - self.io.error(&format!( - "[{}] {}", - get_class(te), - te.get_message() - )); + self.io + .error(&format!("[{}] {}", get_class(te), te.get_message())); } } else { return Err(e); @@ -479,19 +503,20 @@ impl Installer { let mut locked_repository: Option<Box<dyn LockArrayRepository>> = None; - let try_load_locked = || -> anyhow::Result<Result<Option<Box<dyn LockArrayRepository>>, ParsingException>> { - if self.locker.is_locked() { - match self.locker.get_locked_repository(true) { - Ok(r) => Ok(Ok(Some(r))), - Err(e) => match e.downcast::<ParsingException>() { - Ok(p) => Ok(Err(p)), - Err(other) => Err(other), - }, + let try_load_locked = + || -> anyhow::Result<Result<Option<Box<dyn LockArrayRepository>>, ParsingException>> { + if self.locker.is_locked() { + match self.locker.get_locked_repository(true) { + Ok(r) => Ok(Ok(Some(r))), + Err(e) => match e.downcast::<ParsingException>() { + Ok(p) => Ok(Err(p)), + Err(other) => Err(other), + }, + } + } else { + Ok(Ok(None)) } - } else { - Ok(Ok(None)) - } - }; + }; match try_load_locked()? { Ok(r) => locked_repository = r, @@ -505,8 +530,7 @@ impl Installer { } } - if (self.update_allow_list.is_some() || self.update_mirrors) - && locked_repository.is_none() + if (self.update_allow_list.is_some() || self.update_mirrors) && locked_repository.is_none() { self.io.write_error( &format!( @@ -529,8 +553,7 @@ impl Installer { // creating repository set let policy = self.create_policy(true, locked_repository.as_deref()); - let mut repository_set = - self.create_repository_set(true, &platform_repo, &aliases, None); + let mut repository_set = self.create_repository_set(true, &platform_repo, &aliases, None); let repositories = self.repository_manager.get_repositories(); for repository in repositories { repository_set.add_repository(repository); @@ -548,7 +571,10 @@ impl Installer { // pass the allow list into the request, so the pool builder can apply it if let Some(ref allow_list) = self.update_allow_list { - request.set_update_allow_list(allow_list.clone(), self.update_allow_transitive_dependencies); + request.set_update_allow_list( + allow_list.clone(), + self.update_allow_transitive_dependencies, + ); } let mut pool: Option<Pool> = Some(repository_set.create_pool( @@ -564,7 +590,8 @@ impl Installer { self.io.write_error("<info>Updating dependencies</info>"); // solve dependencies - let mut solver: Option<Solver> = Some(Solver::new(&policy, pool.as_ref().unwrap(), &*self.io)); + let mut solver: Option<Solver> = + Some(Solver::new(&policy, pool.as_ref().unwrap(), &*self.io)); let lock_transaction; let rule_set_size; match solver @@ -613,7 +640,10 @@ impl Installer { let _ = solver; self.io.write_error( - &format!("Analyzed {} packages to resolve dependencies", count(&pool.as_ref().unwrap())), + &format!( + "Analyzed {} packages to resolve dependencies", + count(&pool.as_ref().unwrap()) + ), true, IOInterface::VERBOSE, ); @@ -629,10 +659,7 @@ impl Installer { if lock_transaction.get_operations().is_empty() { self.io.write_error("Nothing to modify in lock file"); - if self.minimal_update - && self.update_allow_list.is_none() - && self.locker.is_fresh() - { + if self.minimal_update && self.update_allow_list.is_none() && self.locker.is_fresh() { self.io.write_error("<warning>The --minimal-changes option should be used with package arguments or after modifying composer.json requirements, otherwise it will likely not yield any dependency changes.</warning>"); } } @@ -652,7 +679,8 @@ impl Installer { // write lock let platform_reqs = self.extract_platform_requirements(&self.package.get_requires()); - let platform_dev_reqs = self.extract_platform_requirements(&self.package.get_dev_requires()); + let platform_dev_reqs = + self.extract_platform_requirements(&self.package.get_dev_requires()); let mut installs_updates: Vec<Box<dyn OperationInterface>> = vec![]; let mut uninstalls: Vec<Box<dyn OperationInterface>> = vec![]; @@ -673,7 +701,8 @@ impl Installer { // update in the output as it is only an internal lock file metadata update if self.update_mirrors && uo.get_initial_package().get_name() == uo.get_target_package().get_name() - && uo.get_initial_package().get_version() == uo.get_target_package().get_version() + && uo.get_initial_package().get_version() + == uo.get_target_package().get_version() { continue; } @@ -726,7 +755,9 @@ impl Installer { } } - let sort_by_name = |a: &Box<dyn OperationInterface>, b: &Box<dyn OperationInterface>| -> std::cmp::Ordering { + let sort_by_name = |a: &Box<dyn OperationInterface>, + b: &Box<dyn OperationInterface>| + -> std::cmp::Ordering { let a_name: String = if let Some(uo) = a.as_update_operation() { uo.get_target_package().get_name().to_string() } else { @@ -758,16 +789,18 @@ impl Installer { if self.io.is_very_verbose() && strpos(operation.get_operation_type(), "Alias") == false { - let operation_pkg: Box<dyn PackageInterface> = if let Some(uo) = operation.as_update_operation() { - uo.get_target_package().clone_box() - } else { - operation.get_package().clone_box() - }; + let operation_pkg: Box<dyn PackageInterface> = + if let Some(uo) = operation.as_update_operation() { + uo.get_target_package().clone_box() + } else { + operation.get_package().clone_box() + }; if let Some(repo) = operation_pkg.get_repository() { source_repo = format!(" from {}", repo.get_repo_name()); } } - self.io.write_error(&format!(" - {}{}", operation.show(true), source_repo)); + self.io + .write_error(&format!(" - {}{}", operation.show(true), source_repo)); } } @@ -781,7 +814,11 @@ impl Installer { self.package.get_stability_flags(), self.prefer_stable || self.package.get_prefer_stable(), self.prefer_lowest, - self.config.get("platform").as_array().cloned().unwrap_or_default(), + self.config + .get("platform") + .as_array() + .cloned() + .unwrap_or_default(), self.write_lock && self.execute_operations, )?; if updated_lock && self.write_lock && self.execute_operations { @@ -814,7 +851,10 @@ impl Installer { let loader = ArrayLoader::new(None, true); let dumper = ArrayDumper::new(); for pkg in lock_transaction.get_new_lock_packages(false, false) { - result_repo.add_package(loader.load(dumper.dump(&*pkg), "Composer\\Package\\CompletePackage".to_string())?); + result_repo.add_package(loader.load( + dumper.dump(&*pkg), + "Composer\\Package\\CompletePackage".to_string(), + )?); } let mut repository_set = self.create_repository_set(true, platform_repo, aliases, None); @@ -880,7 +920,11 @@ impl Installer { if self.config.get("lock").as_bool().unwrap_or(false) { self.io.write_error(&format!( "<info>Installing dependencies from lock file{}</info>", - if self.dev_mode { " (including require-dev)" } else { "" } + if self.dev_mode { + " (including require-dev)" + } else { + "" + } )); } @@ -889,7 +933,9 @@ impl Installer { // verify that the lock file works with the current platform repository // we can skip this part if we're doing this as the second step after an update if !already_solved { - self.io.write_error("<info>Verifying lock file contents can be installed on current platform.</info>"); + self.io.write_error( + "<info>Verifying lock file contents can be installed on current platform.</info>", + ); let platform_repo = self.create_platform_repo(false); // creating repository set @@ -918,11 +964,18 @@ impl Installer { ); } - let missing_requirement_info = self.locker.get_missing_requirement_info(&*self.package, self.dev_mode); + let missing_requirement_info = self + .locker + .get_missing_requirement_info(&*self.package, self.dev_mode); if !missing_requirement_info.is_empty() { self.io.write_error(missing_requirement_info); - if !self.config.get("allow-missing-requirements").as_bool().unwrap_or(false) { + if !self + .config + .get("allow-missing-requirements") + .as_bool() + .unwrap_or(false) + { return Ok(Self::ERROR_LOCK_FILE_INVALID); } } @@ -937,13 +990,15 @@ impl Installer { } for (_key, link) in &root_requires { if PlatformRepository::is_platform_package(link.get_target()) { - request.require_name(link.get_target().to_string(), Some(link.get_constraint())); + request + .require_name(link.get_target().to_string(), Some(link.get_constraint())); } } for link in self.locker.get_platform_requirements(self.dev_mode) { if !root_requires.contains_key(link.get_target()) { - request.require_name(link.get_target().to_string(), Some(link.get_constraint())); + request + .require_name(link.get_target().to_string(), Some(link.get_constraint())); } } drop(root_requires); @@ -1087,7 +1142,12 @@ impl Installer { // see https://github.com/composer/composer/issues/2764 if count(&local_repo_transaction.get_operations()) > 0 { - let vendor_dir = self.config.get("vendor-dir").as_string().unwrap_or("").to_string(); + let vendor_dir = self + .config + .get("vendor-dir") + .as_string() + .unwrap_or("") + .to_string(); if is_dir(&vendor_dir) { // suppress errors as this fails sometimes on OSX for no apparent reason // see https://github.com/composer/composer/issues/4070#issuecomment-129792748 @@ -1098,7 +1158,8 @@ impl Installer { for operation in local_repo_transaction.get_operations() { // output op, but alias op only in debug verbosity if strpos(operation.get_operation_type(), "Alias") == false || self.io.is_debug() { - self.io.write_error(&format!(" - {}", operation.show(false))); + self.io + .write_error(&format!(" - {}", operation.show(false))); } } } @@ -1108,7 +1169,11 @@ impl Installer { pub(crate) fn create_platform_repo(&self, for_update: bool) -> PlatformRepository { let platform_overrides = if for_update { - self.config.get("platform").as_array().cloned().unwrap_or_default() + self.config + .get("platform") + .as_array() + .cloned() + .unwrap_or_default() } else { self.locker.get_platform_overrides() }; @@ -1179,7 +1244,8 @@ impl Installer { stability_flags.insert( self.package.get_name().to_string(), - base_package::STABILITIES[VersionParser::parse_stability(self.package.get_version()).as_str()], + base_package::STABILITIES + [VersionParser::parse_stability(self.package.get_version()).as_str()], ); let mut repository_set = RepositorySet::new( @@ -1190,7 +1256,9 @@ impl Installer { root_requires, self.temporary_constraints.clone(), ); - repository_set.add_repository(Box::new(RootPackageRepository::new(clone(&self.fixed_root_package)))); + repository_set.add_repository(Box::new(RootPackageRepository::new(clone( + &self.fixed_root_package, + )))); repository_set.add_repository(Box::new(platform_repo.clone())); if let Some(ref additional_fixed_repository) = self.additional_fixed_repository { // allow using installed repos if needed to avoid warnings about installed repositories being used in the RepositorySet @@ -1209,7 +1277,9 @@ impl Installer { .as_any() .downcast_ref::<InstalledRepository>() .is_some() - || additional_fixed_repository.as_installed_repository_interface().is_some() + || additional_fixed_repository + .as_installed_repository_interface() + .is_some() { repository_set.allow_installed_repositories(); break; @@ -1261,7 +1331,11 @@ impl Installer { preferred_versions = Some(versions); } - DefaultPolicy::new(prefer_stable.unwrap(), prefer_lowest.unwrap(), preferred_versions) + DefaultPolicy::new( + prefer_stable.unwrap(), + prefer_lowest.unwrap(), + preferred_versions, + ) } fn create_request( @@ -1279,7 +1353,8 @@ impl Installer { let mut fixed_packages = platform_repo.get_packages(); if let Some(ref additional_fixed_repository) = self.additional_fixed_repository { - fixed_packages = array_merge(fixed_packages, additional_fixed_repository.get_packages()); + fixed_packages = + array_merge(fixed_packages, additional_fixed_repository.get_packages()); } // fix the version of all platform packages + additionally installed packages @@ -1332,7 +1407,10 @@ impl Installer { { request.require_name( locked_package.get_name().to_string(), - Some(Box::new(Constraint::new("==", locked_package.get_version().to_string()))), + Some(Box::new(Constraint::new( + "==", + locked_package.get_version().to_string(), + ))), ); } } @@ -1357,11 +1435,17 @@ impl Installer { aliases } - fn extract_platform_requirements(&self, links: &IndexMap<String, Link>) -> IndexMap<String, String> { + fn extract_platform_requirements( + &self, + links: &IndexMap<String, Link>, + ) -> IndexMap<String, String> { let mut platform_reqs: IndexMap<String, String> = IndexMap::new(); for (_key, link) in links { if PlatformRepository::is_platform_package(link.get_target()) { - platform_reqs.insert(link.get_target().to_string(), link.get_pretty_constraint().to_string()); + platform_reqs.insert( + link.get_target().to_string(), + link.get_pretty_constraint().to_string(), + ); } } @@ -1426,7 +1510,9 @@ impl Installer { Ok(self.audit_config.as_ref().unwrap()) } - fn create_security_audit_pool_filter(&mut self) -> anyhow::Result<Option<SecurityAdvisoryPoolFilter>> { + fn create_security_audit_pool_filter( + &mut self, + ) -> anyhow::Result<Option<SecurityAdvisoryPoolFilter>> { let audit_config = self.get_audit_config()?; if audit_config.block_insecure && !self.update_mirrors { @@ -1631,7 +1717,9 @@ impl Installer { shirabe_php_shim::E_USER_DEPRECATED, ); - self.set_platform_requirement_filter(PlatformRequirementFilterFactory::from_bool_or_list(ignore_platform_reqs)) + self.set_platform_requirement_filter(PlatformRequirementFilterFactory::from_bool_or_list( + ignore_platform_reqs, + )) } pub fn set_platform_requirement_filter( diff --git a/crates/shirabe/src/installer/binary_installer.rs b/crates/shirabe/src/installer/binary_installer.rs index eb533a4..0e5b3d9 100644 --- a/crates/shirabe/src/installer/binary_installer.rs +++ b/crates/shirabe/src/installer/binary_installer.rs @@ -2,9 +2,9 @@ use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - basename, basename_with_suffix, chmod, dirname, fclose, fgets, file_exists, file_get_contents, - file_put_contents, fopen, is_dir, is_file, is_link, realpath, rmdir, substr, trim, umask, - PhpMixed, + PhpMixed, basename, basename_with_suffix, chmod, dirname, fclose, fgets, file_exists, + file_get_contents, file_put_contents, fopen, is_dir, is_file, is_link, realpath, rmdir, substr, + trim, umask, }; use crate::io::io_interface::IOInterface; @@ -166,10 +166,9 @@ impl BinaryInstaller { let handle = fopen(bin, "r"); let line = fgets(handle.clone()).unwrap_or_default(); fclose(handle); - if let Some(m) = Preg::is_match_strict_groups( - r"{^#!/(?:usr/bin/env )?(?:[^/]+/)*(.+)$}m", - &line, - ) { + if let Some(m) = + Preg::is_match_strict_groups(r"{^#!/(?:usr/bin/env )?(?:[^/]+/)*(.+)$}m", &line) + { return trim(m.get(1).map(|s| s.as_str()).unwrap_or(""), None); } @@ -246,7 +245,10 @@ impl BinaryInstaller { SET BIN_TARGET=%~dp0/{}\r\n\ SET COMPOSER_RUNTIME_BIN_DIR=%~dp0\r\n\ {} \"%BIN_TARGET%\" %*\r\n", - trim(&ProcessExecutor::escape(&basename_with_suffix(link, ".bat")), Some("\"'")), + trim( + &ProcessExecutor::escape(&basename_with_suffix(link, ".bat")), + Some("\"'") + ), caller, ); } @@ -273,12 +275,10 @@ impl BinaryInstaller { let bin_contents = file_get_contents(bin).unwrap_or_default(); // For php files, we generate a PHP proxy instead of a shell one, // which allows calling the proxy with a custom php process - if let Some(m) = Preg::is_match_with_indexed_captures( - r"{^(#!.*\r?\n)?[\r\n\t ]*<\?php}", - &bin_contents, - ) - .ok() - .flatten() + if let Some(m) = + Preg::is_match_with_indexed_captures(r"{^(#!.*\r?\n)?[\r\n\t ]*<\?php}", &bin_contents) + .ok() + .flatten() { // carry over the existing shebang if present, otherwise add our own let proxy_code = if m.get(1).is_none() { @@ -291,9 +291,7 @@ impl BinaryInstaller { .find_shortest_path_code(link, bin, false, true); let mut stream_proxy_code = String::new(); let mut stream_hint = String::new(); - let mut globals_code = format!( - "$GLOBALS['_composer_bin_dir'] = __DIR__;\n", - ); + let mut globals_code = format!("$GLOBALS['_composer_bin_dir'] = __DIR__;\n",); let mut phpunit_hack1 = String::new(); let mut phpunit_hack2 = String::new(); // Don't expose autoload path when vendor dir was not set in custom installers @@ -326,11 +324,14 @@ impl BinaryInstaller { phpunit_hack1 = "'phpvfscomposer://'.".to_string(); phpunit_hack2 = " $data = str_replace('__DIR__', var_export(dirname($this->realpath), true), $data); - $data = str_replace('__FILE__', var_export($this->realpath, true), $data);".to_string(); + $data = str_replace('__FILE__', var_export($this->realpath, true), $data);" + .to_string(); } } if trim(m.get(0).map(|s| s.as_str()).unwrap_or(""), None) != "<?php" { - stream_hint = format!(" using a stream wrapper to prevent the shebang from being output on PHP<8\n *"); + stream_hint = format!( + " using a stream wrapper to prevent the shebang from being output on PHP<8\n *" + ); stream_proxy_code = format!( "if (PHP_VERSION_ID < 80000) {{\n if (!class_exists('Composer\\BinProxyWrapper')) {{\n /**\n * @internal\n */\n final class BinProxyWrapper\n {{\n private $handle;\n private $position;\n private $realpath;\n\n public function stream_open($path, $mode, $options, &$opened_path)\n {{\n // get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution\n $opened_path = substr($path, 17);\n $this->realpath = realpath($opened_path) ?: $opened_path;\n $opened_path = {phpunit_hack1}$this->realpath;\n $this->handle = fopen($this->realpath, $mode);\n $this->position = 0;\n\n return (bool) $this->handle;\n }}\n\n public function stream_read($count)\n {{\n $data = fread($this->handle, $count);\n\n if ($this->position === 0) {{\n $data = preg_replace('{{^#!.*\\r?\\n}}', '', $data);\n }}{phpunit_hack2}\n\n $this->position += strlen($data);\n\n return $data;\n }}\n\n public function stream_cast($castAs)\n {{\n return $this->handle;\n }}\n\n public function stream_close()\n {{\n fclose($this->handle);\n }}\n\n public function stream_lock($operation)\n {{\n return $operation ? flock($this->handle, $operation) : true;\n }}\n\n public function stream_seek($offset, $whence)\n {{\n if (0 === fseek($this->handle, $offset, $whence)) {{\n $this->position = ftell($this->handle);\n return true;\n }}\n\n return false;\n }}\n\n public function stream_tell()\n {{\n return $this->position;\n }}\n\n public function stream_eof()\n {{\n return feof($this->handle);\n }}\n\n public function stream_stat()\n {{\n return array();\n }}\n\n public function stream_set_option($option, $arg1, $arg2)\n {{\n return true;\n }}\n\n public function url_stat($path, $flags)\n {{\n $path = substr($path, 17);\n if (file_exists($path)) {{\n return stat($path);\n }}\n\n return false;\n }}\n }}\n }}\n\n if (\n (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))\n || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\\BinProxyWrapper'))\n ) {{\n return include(\"phpvfscomposer://\" . {bin_path_exported});\n }}\n}}\n", phpunit_hack1 = phpunit_hack1, diff --git a/crates/shirabe/src/installer/installation_manager.rs b/crates/shirabe/src/installer/installation_manager.rs index 52831aa..519091f 100644 --- a/crates/shirabe/src/installer/installation_manager.rs +++ b/crates/shirabe/src/installer/installation_manager.rs @@ -6,8 +6,8 @@ use shirabe_external_packages::react::promise; use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use shirabe_external_packages::seld::signal::signal_handler::SignalHandler; use shirabe_php_shim::{ - array_search_mixed, array_splice, array_unshift, count, http_build_query, json_encode, - str_contains, str_replace, strpos, strtolower, ucfirst, InvalidArgumentException, PhpMixed, + InvalidArgumentException, PhpMixed, array_search_mixed, array_splice, array_unshift, count, + http_build_query, json_encode, str_contains, str_replace, strpos, strtolower, ucfirst, }; use crate::dependency_resolver::operation::install_operation::InstallOperation; @@ -143,7 +143,9 @@ impl InstallationManager { && self.is_package_installed(repo, alias.get_alias_of())?); } - Ok(self.get_installer(package.get_type())?.is_installed(repo, package)) + Ok(self + .get_installer(package.get_type())? + .is_installed(repo, package)) } /// Install binary for the given package. @@ -177,8 +179,10 @@ impl InstallationManager { download_only: bool, ) -> Result<()> { // @var array<callable(): ?PromiseInterface<void|null>> $cleanupPromises - let mut cleanup_promises: IndexMap<i64, Box<dyn Fn() -> Option<Box<dyn PromiseInterface>>>> = - IndexMap::new(); + let mut cleanup_promises: IndexMap< + i64, + Box<dyn Fn() -> Option<Box<dyn PromiseInterface>>>, + > = IndexMap::new(); let signal_handler = SignalHandler::create( vec![ @@ -452,13 +456,16 @@ impl InstallationManager { }; if run_scripts && self.event_dispatcher.is_some() { - self.event_dispatcher.as_mut().unwrap().dispatch_package_event( - event_name, - dev_mode, - repo, - all_operations, - operation.as_ref(), - ); + self.event_dispatcher + .as_mut() + .unwrap() + .dispatch_package_event( + event_name, + dev_mode, + repo, + all_operations, + operation.as_ref(), + ); } let _dispatcher = self.event_dispatcher.as_ref(); @@ -524,11 +531,8 @@ impl InstallationManager { // progress.clear(); // ProgressBar in non-decorated output does not output a final line-break and clear() does nothing if !self.io.is_decorated() { - self.io.write_error( - PhpMixed::String(String::new()), - true, - IOInterface::NORMAL, - ); + self.io + .write_error(PhpMixed::String(String::new()), true, IOInterface::NORMAL); } } } @@ -536,7 +540,10 @@ impl InstallationManager { /// Executes download operation. /// /// @phpstan-return PromiseInterface<void|null>|null - pub fn download(&mut self, package: &dyn PackageInterface) -> Option<Box<dyn PromiseInterface>> { + pub fn download( + &mut self, + package: &dyn PackageInterface, + ) -> Option<Box<dyn PromiseInterface>> { let installer = self.get_installer(package.get_type()).ok()?; let promise = installer.cleanup("install", package, None); @@ -579,7 +586,10 @@ impl InstallationManager { self.mark_for_notification(target); promise } else { - let promise = self.get_installer(initial_type).ok()?.uninstall(repo, initial); + let promise = self + .get_installer(initial_type) + .ok()? + .uninstall(repo, initial); let promise = match promise { Some(p) => p, None => promise::resolve(None), @@ -667,10 +677,7 @@ impl InstallationManager { let mut opts: IndexMap<String, PhpMixed> = IndexMap::new(); opts.insert("retry-auth-failure".to_string(), PhpMixed::Bool(false)); let mut http: IndexMap<String, PhpMixed> = IndexMap::new(); - http.insert( - "method".to_string(), - PhpMixed::String("POST".to_string()), - ); + http.insert("method".to_string(), PhpMixed::String("POST".to_string())); http.insert( "header".to_string(), PhpMixed::List(vec![Box::new(PhpMixed::String( @@ -685,15 +692,16 @@ impl InstallationManager { opts.insert( "http".to_string(), PhpMixed::Array( - http.into_iter() - .map(|(k, v)| (k, Box::new(v))) - .collect(), + http.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), ), ); - promises.push(self.loop_.get_http_downloader().add(&url, &PhpMixed::Array( - opts.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), - ))); + promises.push(self.loop_.get_http_downloader().add( + &url, + &PhpMixed::Array( + opts.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), + ), + )); } continue; @@ -715,8 +723,7 @@ impl InstallationManager { if let Some(metadata) = FileDownloader::download_metadata().get(package.get_name()) { - package_notification - .insert("downloaded".to_string(), metadata.clone()); + package_notification.insert("downloaded".to_string(), metadata.clone()); } else { package_notification .insert("downloaded".to_string(), PhpMixed::Bool(false)); @@ -757,16 +764,13 @@ impl InstallationManager { http.insert("timeout".to_string(), PhpMixed::Int(6)); opts.insert( "http".to_string(), - PhpMixed::Array( - http.into_iter() - .map(|(k, v)| (k, Box::new(v))) - .collect(), - ), + PhpMixed::Array(http.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), ); - promises.push(self.loop_.get_http_downloader().add(repo_url, &PhpMixed::Array( - opts.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), - ))); + promises.push(self.loop_.get_http_downloader().add( + repo_url, + &PhpMixed::Array(opts.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), + )); } self.loop_.wait(promises, None); diff --git a/crates/shirabe/src/installer/installer_event.rs b/crates/shirabe/src/installer/installer_event.rs index 3d0dccc..456a4bd 100644 --- a/crates/shirabe/src/installer/installer_event.rs +++ b/crates/shirabe/src/installer/installer_event.rs @@ -25,7 +25,14 @@ impl InstallerEvent { transaction: Transaction, ) -> Self { let inner = Event::new(event_name, vec![], vec![]); - Self { inner, composer, io, dev_mode, execute_operations, transaction } + Self { + inner, + composer, + io, + dev_mode, + execute_operations, + transaction, + } } pub fn get_composer(&self) -> &Composer { diff --git a/crates/shirabe/src/installer/installer_interface.rs b/crates/shirabe/src/installer/installer_interface.rs index fc651bb..16cf10c 100644 --- a/crates/shirabe/src/installer/installer_interface.rs +++ b/crates/shirabe/src/installer/installer_interface.rs @@ -1,25 +1,56 @@ //! ref: composer/src/Composer/Installer/InstallerInterface.php -use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use crate::package::package_interface::PackageInterface; use crate::repository::installed_repository_interface::InstalledRepositoryInterface; +use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; pub trait InstallerInterface { fn supports(&self, package_type: &str) -> bool; - fn is_installed(&self, repo: &dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> bool; + fn is_installed( + &self, + repo: &dyn InstalledRepositoryInterface, + package: &dyn PackageInterface, + ) -> bool; - fn download(&self, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>; + fn download( + &self, + package: &dyn PackageInterface, + prev_package: Option<&dyn PackageInterface>, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>; - fn prepare(&self, r#type: &str, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>; + fn prepare( + &self, + r#type: &str, + package: &dyn PackageInterface, + prev_package: Option<&dyn PackageInterface>, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>; - fn install(&self, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>; + fn install( + &self, + repo: &mut dyn InstalledRepositoryInterface, + package: &dyn PackageInterface, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>; - fn update(&self, repo: &mut dyn InstalledRepositoryInterface, initial: &dyn PackageInterface, target: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>; + fn update( + &self, + repo: &mut dyn InstalledRepositoryInterface, + initial: &dyn PackageInterface, + target: &dyn PackageInterface, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>; - fn uninstall(&self, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>; + fn uninstall( + &self, + repo: &mut dyn InstalledRepositoryInterface, + package: &dyn PackageInterface, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>; - fn cleanup(&self, r#type: &str, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>; + fn cleanup( + &self, + r#type: &str, + package: &dyn PackageInterface, + prev_package: Option<&dyn PackageInterface>, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>; fn get_install_path(&self, package: &dyn PackageInterface) -> Option<String>; } diff --git a/crates/shirabe/src/installer/library_installer.rs b/crates/shirabe/src/installer/library_installer.rs index cc6e7a6..af11ef6 100644 --- a/crates/shirabe/src/installer/library_installer.rs +++ b/crates/shirabe/src/installer/library_installer.rs @@ -6,7 +6,7 @@ use anyhow::Result; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use shirabe_php_shim::{ - is_link, preg_quote, realpath, rmdir, rtrim, strpos, InvalidArgumentException, LogicException, + InvalidArgumentException, LogicException, is_link, preg_quote, realpath, rmdir, rtrim, strpos, }; use crate::composer::Composer; @@ -84,8 +84,11 @@ impl LibraryInstaller { /// Make sure binaries are installed for a given package. pub fn ensure_binaries_presence(&self, package: &dyn PackageInterface) { - self.binary_installer - .install_binaries(package, &self.get_install_path(package).unwrap(), false); + self.binary_installer.install_binaries( + package, + &self.get_install_path(package).unwrap(), + false, + ); } /// Returns the base path of the package without target-dir path diff --git a/crates/shirabe/src/installer/metapackage_installer.rs b/crates/shirabe/src/installer/metapackage_installer.rs index 8be21e7..0a5f872 100644 --- a/crates/shirabe/src/installer/metapackage_installer.rs +++ b/crates/shirabe/src/installer/metapackage_installer.rs @@ -1,8 +1,5 @@ //! ref: composer/src/Composer/Installer/MetapackageInstaller.php -use anyhow::Result; -use shirabe_php_shim::InvalidArgumentException; -use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use crate::dependency_resolver::operation::install_operation::InstallOperation; use crate::dependency_resolver::operation::uninstall_operation::UninstallOperation; use crate::dependency_resolver::operation::update_operation::UpdateOperation; @@ -10,6 +7,9 @@ use crate::installer::installer_interface::InstallerInterface; use crate::io::io_interface::IOInterface; use crate::package::package_interface::PackageInterface; use crate::repository::installed_repository_interface::InstalledRepositoryInterface; +use anyhow::Result; +use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; +use shirabe_php_shim::InvalidArgumentException; #[derive(Debug)] pub struct MetapackageInstaller { @@ -27,59 +27,116 @@ impl InstallerInterface for MetapackageInstaller { package_type == "metapackage" } - fn is_installed(&self, repo: &dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> bool { + fn is_installed( + &self, + repo: &dyn InstalledRepositoryInterface, + package: &dyn PackageInterface, + ) -> bool { repo.has_package(package) } - fn download(&self, _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>) -> Result<Option<Box<dyn PromiseInterface>>> { - Ok(Some(shirabe_external_packages::react::promise::resolve(None))) + fn download( + &self, + _package: &dyn PackageInterface, + _prev_package: Option<&dyn PackageInterface>, + ) -> Result<Option<Box<dyn PromiseInterface>>> { + Ok(Some(shirabe_external_packages::react::promise::resolve( + None, + ))) } - fn prepare(&self, _type: &str, _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>) -> Result<Option<Box<dyn PromiseInterface>>> { - Ok(Some(shirabe_external_packages::react::promise::resolve(None))) + fn prepare( + &self, + _type: &str, + _package: &dyn PackageInterface, + _prev_package: Option<&dyn PackageInterface>, + ) -> Result<Option<Box<dyn PromiseInterface>>> { + Ok(Some(shirabe_external_packages::react::promise::resolve( + None, + ))) } - fn cleanup(&self, _type: &str, _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>) -> Result<Option<Box<dyn PromiseInterface>>> { - Ok(Some(shirabe_external_packages::react::promise::resolve(None))) + fn cleanup( + &self, + _type: &str, + _package: &dyn PackageInterface, + _prev_package: Option<&dyn PackageInterface>, + ) -> Result<Option<Box<dyn PromiseInterface>>> { + Ok(Some(shirabe_external_packages::react::promise::resolve( + None, + ))) } - fn install(&self, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> Result<Option<Box<dyn PromiseInterface>>> { - self.io.write_error(&format!(" - {}", InstallOperation::format(package, false)), true, IOInterface::NORMAL); + fn install( + &self, + repo: &mut dyn InstalledRepositoryInterface, + package: &dyn PackageInterface, + ) -> Result<Option<Box<dyn PromiseInterface>>> { + self.io.write_error( + &format!(" - {}", InstallOperation::format(package, false)), + true, + IOInterface::NORMAL, + ); repo.add_package(package.clone_box()); - Ok(Some(shirabe_external_packages::react::promise::resolve(None))) + Ok(Some(shirabe_external_packages::react::promise::resolve( + None, + ))) } - fn update(&self, repo: &mut dyn InstalledRepositoryInterface, initial: &dyn PackageInterface, target: &dyn PackageInterface) -> Result<Option<Box<dyn PromiseInterface>>> { + fn update( + &self, + repo: &mut dyn InstalledRepositoryInterface, + initial: &dyn PackageInterface, + target: &dyn PackageInterface, + ) -> Result<Option<Box<dyn PromiseInterface>>> { if !repo.has_package(initial) { return Err(InvalidArgumentException { message: format!("Package is not installed: {}", initial), code: 0, - }.into()); + } + .into()); } - self.io.write_error(&format!(" - {}", UpdateOperation::format(initial, target, false)), true, IOInterface::NORMAL); + self.io.write_error( + &format!(" - {}", UpdateOperation::format(initial, target, false)), + true, + IOInterface::NORMAL, + ); repo.remove_package(initial); repo.add_package(target.clone_box()); - Ok(Some(shirabe_external_packages::react::promise::resolve(None))) + Ok(Some(shirabe_external_packages::react::promise::resolve( + None, + ))) } - fn uninstall(&self, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> Result<Option<Box<dyn PromiseInterface>>> { + fn uninstall( + &self, + repo: &mut dyn InstalledRepositoryInterface, + package: &dyn PackageInterface, + ) -> Result<Option<Box<dyn PromiseInterface>>> { if !repo.has_package(package) { return Err(InvalidArgumentException { message: format!("Package is not installed: {}", package), code: 0, - }.into()); + } + .into()); } - self.io.write_error(&format!(" - {}", UninstallOperation::format(package, false)), true, IOInterface::NORMAL); + self.io.write_error( + &format!(" - {}", UninstallOperation::format(package, false)), + true, + IOInterface::NORMAL, + ); repo.remove_package(package); - Ok(Some(shirabe_external_packages::react::promise::resolve(None))) + Ok(Some(shirabe_external_packages::react::promise::resolve( + None, + ))) } fn get_install_path(&self, _package: &dyn PackageInterface) -> Option<String> { diff --git a/crates/shirabe/src/installer/noop_installer.rs b/crates/shirabe/src/installer/noop_installer.rs index fd30f4a..09e6afd 100644 --- a/crates/shirabe/src/installer/noop_installer.rs +++ b/crates/shirabe/src/installer/noop_installer.rs @@ -1,10 +1,10 @@ //! ref: composer/src/Composer/Installer/NoopInstaller.php -use shirabe_php_shim::InvalidArgumentException; -use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use crate::installer::installer_interface::InstallerInterface; use crate::package::package_interface::PackageInterface; use crate::repository::installed_repository_interface::InstalledRepositoryInterface; +use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; +use shirabe_php_shim::InvalidArgumentException; #[derive(Debug)] pub struct NoopInstaller; @@ -14,36 +14,72 @@ impl InstallerInterface for NoopInstaller { true } - fn is_installed(&self, repo: &dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> bool { + fn is_installed( + &self, + repo: &dyn InstalledRepositoryInterface, + package: &dyn PackageInterface, + ) -> bool { repo.has_package(package) } - fn download(&self, _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { - Ok(Some(shirabe_external_packages::react::promise::resolve(None))) + fn download( + &self, + _package: &dyn PackageInterface, + _prev_package: Option<&dyn PackageInterface>, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { + Ok(Some(shirabe_external_packages::react::promise::resolve( + None, + ))) } - fn prepare(&self, _type: &str, _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { - Ok(Some(shirabe_external_packages::react::promise::resolve(None))) + fn prepare( + &self, + _type: &str, + _package: &dyn PackageInterface, + _prev_package: Option<&dyn PackageInterface>, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { + Ok(Some(shirabe_external_packages::react::promise::resolve( + None, + ))) } - fn cleanup(&self, _type: &str, _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { - Ok(Some(shirabe_external_packages::react::promise::resolve(None))) + fn cleanup( + &self, + _type: &str, + _package: &dyn PackageInterface, + _prev_package: Option<&dyn PackageInterface>, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { + Ok(Some(shirabe_external_packages::react::promise::resolve( + None, + ))) } - fn install(&self, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { + fn install( + &self, + repo: &mut dyn InstalledRepositoryInterface, + package: &dyn PackageInterface, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { if !repo.has_package(package) { repo.add_package(package.clone_box()); } - Ok(Some(shirabe_external_packages::react::promise::resolve(None))) + Ok(Some(shirabe_external_packages::react::promise::resolve( + None, + ))) } - fn update(&self, repo: &mut dyn InstalledRepositoryInterface, initial: &dyn PackageInterface, target: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { + fn update( + &self, + repo: &mut dyn InstalledRepositoryInterface, + initial: &dyn PackageInterface, + target: &dyn PackageInterface, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { if !repo.has_package(initial) { return Err(InvalidArgumentException { message: format!("Package is not installed: {}", initial), code: 0, - }.into()); + } + .into()); } repo.remove_package(initial); @@ -51,19 +87,28 @@ impl InstallerInterface for NoopInstaller { repo.add_package(target.clone_box()); } - Ok(Some(shirabe_external_packages::react::promise::resolve(None))) + Ok(Some(shirabe_external_packages::react::promise::resolve( + None, + ))) } - fn uninstall(&self, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { + fn uninstall( + &self, + repo: &mut dyn InstalledRepositoryInterface, + package: &dyn PackageInterface, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { if !repo.has_package(package) { return Err(InvalidArgumentException { message: format!("Package is not installed: {}", package), code: 0, - }.into()); + } + .into()); } repo.remove_package(package); - Ok(Some(shirabe_external_packages::react::promise::resolve(None))) + Ok(Some(shirabe_external_packages::react::promise::resolve( + None, + ))) } fn get_install_path(&self, package: &dyn PackageInterface) -> Option<String> { diff --git a/crates/shirabe/src/installer/package_event.rs b/crates/shirabe/src/installer/package_event.rs index 537e97e..2bf1c6b 100644 --- a/crates/shirabe/src/installer/package_event.rs +++ b/crates/shirabe/src/installer/package_event.rs @@ -1,11 +1,11 @@ //! ref: composer/src/Composer/Installer/PackageEvent.php -use indexmap::IndexMap; use crate::composer::Composer; use crate::dependency_resolver::operation::operation_interface::OperationInterface; use crate::event_dispatcher::event::Event; use crate::io::io_interface::IOInterface; use crate::repository::repository_interface::RepositoryInterface; +use indexmap::IndexMap; #[derive(Debug)] pub struct PackageEvent { diff --git a/crates/shirabe/src/installer/plugin_installer.rs b/crates/shirabe/src/installer/plugin_installer.rs index 55a936a..1fd2334 100644 --- a/crates/shirabe/src/installer/plugin_installer.rs +++ b/crates/shirabe/src/installer/plugin_installer.rs @@ -1,8 +1,5 @@ //! ref: composer/src/Composer/Installer/PluginInstaller.php -use anyhow::Result; -use shirabe_php_shim::{empty, LogicException, PhpMixed, UnexpectedValueException}; -use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use crate::installer::binary_installer::BinaryInstaller; use crate::installer::installer_interface::InstallerInterface; use crate::installer::library_installer::LibraryInstaller; @@ -13,6 +10,9 @@ use crate::plugin::plugin_manager::PluginManager; use crate::repository::installed_repository_interface::InstalledRepositoryInterface; use crate::util::filesystem::Filesystem; use crate::util::platform::Platform; +use anyhow::Result; +use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; +use shirabe_php_shim::{LogicException, PhpMixed, UnexpectedValueException, empty}; #[derive(Debug)] pub struct PluginInstaller { @@ -27,7 +27,13 @@ impl PluginInstaller { binary_installer: Option<BinaryInstaller>, ) -> Self { Self { - inner: LibraryInstaller::new(io, composer, Some("composer-plugin".to_string()), fs, binary_installer), + inner: LibraryInstaller::new( + io, + composer, + Some("composer-plugin".to_string()), + fs, + binary_installer, + ), } } @@ -36,8 +42,16 @@ impl PluginInstaller { self.get_plugin_manager().disable_plugins(); } - fn rollback_install(&self, e: anyhow::Error, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> Result<()> { - self.inner.io.write_error(&format!("Plugin initialization failed ({}), uninstalling plugin", e)); + fn rollback_install( + &self, + e: anyhow::Error, + repo: &mut dyn InstalledRepositoryInterface, + package: &dyn PackageInterface, + ) -> Result<()> { + self.inner.io.write_error(&format!( + "Plugin initialization failed ({}), uninstalling plugin", + e + )); self.inner.uninstall(repo, package)?; Err(e) } @@ -48,7 +62,9 @@ impl PluginInstaller { self.inner.composer.is_full_composer(), "{}", LogicException { - message: "PluginInstaller should be initialized with a fully loaded Composer instance.".to_string(), + message: + "PluginInstaller should be initialized with a fully loaded Composer instance." + .to_string(), code: 0, } ); @@ -62,24 +78,41 @@ impl InstallerInterface for PluginInstaller { package_type == "composer-plugin" || package_type == "composer-installer" } - fn is_installed(&self, repo: &dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> bool { + fn is_installed( + &self, + repo: &dyn InstalledRepositoryInterface, + package: &dyn PackageInterface, + ) -> bool { self.inner.is_installed(repo, package) } - fn prepare(&self, r#type: &str, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> Result<Option<Box<dyn PromiseInterface>>> { - if (r#type == "install" || r#type == "update") && !self.get_plugin_manager().are_plugins_disabled("local") { - let plugin_optional = package.get_extra() + fn prepare( + &self, + r#type: &str, + package: &dyn PackageInterface, + prev_package: Option<&dyn PackageInterface>, + ) -> Result<Option<Box<dyn PromiseInterface>>> { + if (r#type == "install" || r#type == "update") + && !self.get_plugin_manager().are_plugins_disabled("local") + { + let plugin_optional = package + .get_extra() .get("plugin-optional") .map(|v| matches!(v, PhpMixed::Bool(true))) .unwrap_or(false); // TODO(plugin): check if plugin is allowed - self.get_plugin_manager().is_plugin_allowed(package.get_name(), false, plugin_optional); + self.get_plugin_manager() + .is_plugin_allowed(package.get_name(), false, plugin_optional); } self.inner.prepare(r#type, package, prev_package) } - fn download(&self, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> Result<Option<Box<dyn PromiseInterface>>> { + fn download( + &self, + package: &dyn PackageInterface, + prev_package: Option<&dyn PackageInterface>, + ) -> Result<Option<Box<dyn PromiseInterface>>> { let extra = package.get_extra(); let class = extra.get("class").cloned().unwrap_or(PhpMixed::Null); if empty(&class) { @@ -95,7 +128,11 @@ impl InstallerInterface for PluginInstaller { self.inner.download(package, prev_package) } - fn install(&self, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> Result<Option<Box<dyn PromiseInterface>>> { + fn install( + &self, + repo: &mut dyn InstalledRepositoryInterface, + package: &dyn PackageInterface, + ) -> Result<Option<Box<dyn PromiseInterface>>> { let promise = self.inner.install(repo, package)?; let promise = match promise { Some(p) => p, @@ -111,7 +148,12 @@ impl InstallerInterface for PluginInstaller { })))) } - fn update(&self, repo: &mut dyn InstalledRepositoryInterface, initial: &dyn PackageInterface, target: &dyn PackageInterface) -> Result<Option<Box<dyn PromiseInterface>>> { + fn update( + &self, + repo: &mut dyn InstalledRepositoryInterface, + initial: &dyn PackageInterface, + target: &dyn PackageInterface, + ) -> Result<Option<Box<dyn PromiseInterface>>> { let promise = self.inner.update(repo, initial, target)?; let promise = match promise { Some(p) => p, @@ -128,14 +170,23 @@ impl InstallerInterface for PluginInstaller { })))) } - fn uninstall(&self, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> Result<Option<Box<dyn PromiseInterface>>> { + fn uninstall( + &self, + repo: &mut dyn InstalledRepositoryInterface, + package: &dyn PackageInterface, + ) -> Result<Option<Box<dyn PromiseInterface>>> { // TODO(plugin): uninstall package from plugin manager self.get_plugin_manager().uninstall_package(package); self.inner.uninstall(repo, package) } - fn cleanup(&self, r#type: &str, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> Result<Option<Box<dyn PromiseInterface>>> { + fn cleanup( + &self, + r#type: &str, + package: &dyn PackageInterface, + prev_package: Option<&dyn PackageInterface>, + ) -> Result<Option<Box<dyn PromiseInterface>>> { self.inner.cleanup(r#type, package, prev_package) } diff --git a/crates/shirabe/src/installer/project_installer.rs b/crates/shirabe/src/installer/project_installer.rs index 6472472..1a097b0 100644 --- a/crates/shirabe/src/installer/project_installer.rs +++ b/crates/shirabe/src/installer/project_installer.rs @@ -1,12 +1,12 @@ //! ref: composer/src/Composer/Installer/ProjectInstaller.php -use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; -use shirabe_php_shim::InvalidArgumentException; use crate::downloader::download_manager::DownloadManager; use crate::installer::installer_interface::InstallerInterface; use crate::package::package_interface::PackageInterface; use crate::repository::installed_repository_interface::InstalledRepositoryInterface; use crate::util::filesystem::Filesystem; +use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; +use shirabe_php_shim::InvalidArgumentException; #[derive(Debug)] pub struct ProjectInstaller { @@ -31,49 +31,88 @@ impl InstallerInterface for ProjectInstaller { true } - fn is_installed(&self, _repo: &dyn InstalledRepositoryInterface, _package: &dyn PackageInterface) -> bool { + fn is_installed( + &self, + _repo: &dyn InstalledRepositoryInterface, + _package: &dyn PackageInterface, + ) -> bool { false } - fn download(&self, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { + fn download( + &self, + package: &dyn PackageInterface, + prev_package: Option<&dyn PackageInterface>, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { let install_path = &self.install_path; - if std::path::Path::new(install_path).exists() && !self.filesystem.is_dir_empty(install_path) { + if std::path::Path::new(install_path).exists() + && !self.filesystem.is_dir_empty(install_path) + { return Err(InvalidArgumentException { message: format!("Project directory {} is not empty.", install_path), code: 0, - }.into()); + } + .into()); } if !std::path::Path::new(install_path).is_dir() { std::fs::create_dir_all(install_path)?; } - self.download_manager.download(package, install_path, prev_package) + self.download_manager + .download(package, install_path, prev_package) } - fn prepare(&self, r#type: &str, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { - self.download_manager.prepare(r#type, package, &self.install_path, prev_package) + fn prepare( + &self, + r#type: &str, + package: &dyn PackageInterface, + prev_package: Option<&dyn PackageInterface>, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { + self.download_manager + .prepare(r#type, package, &self.install_path, prev_package) } - fn cleanup(&self, r#type: &str, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { - self.download_manager.cleanup(r#type, package, &self.install_path, prev_package) + fn cleanup( + &self, + r#type: &str, + package: &dyn PackageInterface, + prev_package: Option<&dyn PackageInterface>, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { + self.download_manager + .cleanup(r#type, package, &self.install_path, prev_package) } - fn install(&self, _repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { + fn install( + &self, + _repo: &mut dyn InstalledRepositoryInterface, + package: &dyn PackageInterface, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { self.download_manager.install(package, &self.install_path) } - fn update(&self, _repo: &mut dyn InstalledRepositoryInterface, _initial: &dyn PackageInterface, _target: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { + fn update( + &self, + _repo: &mut dyn InstalledRepositoryInterface, + _initial: &dyn PackageInterface, + _target: &dyn PackageInterface, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { Err(InvalidArgumentException { message: "not supported".to_string(), code: 0, - }.into()) + } + .into()) } - fn uninstall(&self, _repo: &mut dyn InstalledRepositoryInterface, _package: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { + fn uninstall( + &self, + _repo: &mut dyn InstalledRepositoryInterface, + _package: &dyn PackageInterface, + ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { Err(InvalidArgumentException { message: "not supported".to_string(), code: 0, - }.into()) + } + .into()) } fn get_install_path(&self, _package: &dyn PackageInterface) -> Option<String> { diff --git a/crates/shirabe/src/installer/suggested_packages_reporter.rs b/crates/shirabe/src/installer/suggested_packages_reporter.rs index 77b4c34..cfee20b 100644 --- a/crates/shirabe/src/installer/suggested_packages_reporter.rs +++ b/crates/shirabe/src/installer/suggested_packages_reporter.rs @@ -1,11 +1,11 @@ //! ref: composer/src/Composer/Installer/SuggestedPackagesReporter.php -use indexmap::IndexMap; -use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_external_packages::symfony::component::console::formatter::output_formatter::OutputFormatter; use crate::io::io_interface::IOInterface; use crate::package::package_interface::PackageInterface; use crate::repository::installed_repository::InstalledRepository; +use indexmap::IndexMap; +use shirabe_external_packages::composer::pcre::preg::Preg; +use shirabe_external_packages::symfony::component::console::formatter::output_formatter::OutputFormatter; #[derive(Debug)] pub struct SuggestedPackagesReporter { @@ -48,7 +48,12 @@ impl SuggestedPackagesReporter { self } - pub fn output(&self, mode: i64, installed_repo: Option<&InstalledRepository>, only_dependents_of: Option<&dyn PackageInterface>) { + pub fn output( + &self, + mode: i64, + installed_repo: Option<&InstalledRepository>, + only_dependents_of: Option<&dyn PackageInterface>, + ) { let suggested_packages = self.get_filtered_suggestions(installed_repo, only_dependents_of); let mut suggesters: IndexMap<String, IndexMap<String, String>> = IndexMap::new(); @@ -78,7 +83,8 @@ impl SuggestedPackagesReporter { // Grouped by package if mode & Self::MODE_BY_PACKAGE != 0 { for (suggester, suggestions) in &suggesters { - self.io.write(&format!("<comment>{}</comment> suggests:", suggester)); + self.io + .write(&format!("<comment>{}</comment> suggests:", suggester)); for (suggestion, reason) in suggestions { self.io.write(&format!( @@ -102,7 +108,10 @@ impl SuggestedPackagesReporter { self.io.write(&"-".repeat(78)); } for (suggestion, suggesters) in &suggested { - self.io.write(&format!("<comment>{}</comment> is suggested by:", suggestion)); + self.io.write(&format!( + "<comment>{}</comment> is suggested by:", + suggestion + )); for (suggester, reason) in suggesters { self.io.write(&format!( @@ -128,7 +137,11 @@ impl SuggestedPackagesReporter { } } - pub fn output_minimalistic(&self, installed_repo: Option<&InstalledRepository>, only_dependents_of: Option<&dyn PackageInterface>) { + pub fn output_minimalistic( + &self, + installed_repo: Option<&InstalledRepository>, + only_dependents_of: Option<&dyn PackageInterface>, + ) { let suggested_packages = self.get_filtered_suggestions(installed_repo, only_dependents_of); if !suggested_packages.is_empty() { self.io.write_error(&format!( @@ -138,7 +151,11 @@ impl SuggestedPackagesReporter { } } - fn get_filtered_suggestions(&self, installed_repo: Option<&InstalledRepository>, only_dependents_of: Option<&dyn PackageInterface>) -> Vec<IndexMap<String, String>> { + fn get_filtered_suggestions( + &self, + installed_repo: Option<&InstalledRepository>, + only_dependents_of: Option<&dyn PackageInterface>, + ) -> Vec<IndexMap<String, String>> { let suggested_packages = self.get_packages(); let mut installed_names: Vec<String> = Vec::new(); if installed_repo.is_some() && !suggested_packages.is_empty() { @@ -149,7 +166,9 @@ impl SuggestedPackagesReporter { let mut source_filter: Vec<String> = Vec::new(); if let Some(only_dependents_of) = only_dependents_of { - source_filter = only_dependents_of.get_requires().values() + source_filter = only_dependents_of + .get_requires() + .values() .chain(only_dependents_of.get_dev_requires().values()) .map(|link| link.get_target().to_string()) .collect(); @@ -171,16 +190,10 @@ impl SuggestedPackagesReporter { } fn escape_output(&self, string: &str) -> String { - OutputFormatter::escape( - &self.remove_control_characters(string) - ) + OutputFormatter::escape(&self.remove_control_characters(string)) } fn remove_control_characters(&self, string: &str) -> String { - Preg::replace( - "/[[:cntrl:]]/", - "", - &string.replace('\n', " "), - ) + Preg::replace("/[[:cntrl:]]/", "", &string.replace('\n', " ")) } } diff --git a/crates/shirabe/src/io/base_io.rs b/crates/shirabe/src/io/base_io.rs index f1cff3c..58d1e40 100644 --- a/crates/shirabe/src/io/base_io.rs +++ b/crates/shirabe/src/io/base_io.rs @@ -1,16 +1,16 @@ //! ref: composer/src/Composer/IO/BaseIO.php +use crate::config::Config; +use crate::io::io_interface::IOInterface; +use crate::util::process_executor::ProcessExecutor; +use crate::util::silencer::Silencer; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::psr::log::log_level::LogLevel; use shirabe_php_shim::{ - array_merge, in_array, json_encode_ex, PhpMixed, UnexpectedValueException, - JSON_INVALID_UTF8_IGNORE, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, + JSON_INVALID_UTF8_IGNORE, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, PhpMixed, + UnexpectedValueException, array_merge, in_array, json_encode_ex, }; -use crate::config::Config; -use crate::io::io_interface::IOInterface; -use crate::util::process_executor::ProcessExecutor; -use crate::util::silencer::Silencer; #[derive(Debug)] pub struct BaseIO { @@ -344,20 +344,15 @@ impl BaseIO { let mut ssl_options: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); if let Some(cert) = local_cert { - ssl_options.insert( - "local_cert".to_string(), - Box::new(PhpMixed::String(cert)), - ); + ssl_options + .insert("local_cert".to_string(), Box::new(PhpMixed::String(cert))); } if let Some(pk) = local_pk { - ssl_options - .insert("local_pk".to_string(), Box::new(PhpMixed::String(pk))); + ssl_options.insert("local_pk".to_string(), Box::new(PhpMixed::String(pk))); } if let Some(pass) = passphrase { - ssl_options.insert( - "passphrase".to_string(), - Box::new(PhpMixed::String(pass)), - ); + ssl_options + .insert("passphrase".to_string(), Box::new(PhpMixed::String(pass))); } if !ssl_options.contains_key("local_cert") { diff --git a/crates/shirabe/src/io/buffer_io.rs b/crates/shirabe/src/io/buffer_io.rs index 30e2aeb..aa1d970 100644 --- a/crates/shirabe/src/io/buffer_io.rs +++ b/crates/shirabe/src/io/buffer_io.rs @@ -1,5 +1,6 @@ //! ref: composer/src/Composer/IO/BufferIO.php +use crate::io::console_io::ConsoleIO; use anyhow::Result; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::symfony::console::formatter::output_formatter_interface::OutputFormatterInterface; @@ -8,8 +9,10 @@ use shirabe_external_packages::symfony::console::helper::question_helper::Questi use shirabe_external_packages::symfony::console::input::streamable_input_interface::StreamableInputInterface; use shirabe_external_packages::symfony::console::input::string_input::StringInput; use shirabe_external_packages::symfony::console::output::stream_output::StreamOutput; -use shirabe_php_shim::{fopen, fseek, fwrite, rewind, stream_get_contents, strip_tags, PhpMixed, RuntimeException, PHP_EOL}; -use crate::io::console_io::ConsoleIO; +use shirabe_php_shim::{ + PHP_EOL, PhpMixed, RuntimeException, fopen, fseek, fwrite, rewind, stream_get_contents, + strip_tags, +}; #[derive(Debug)] pub struct BufferIO { @@ -17,7 +20,11 @@ pub struct BufferIO { } impl BufferIO { - pub fn new(input: String, verbosity: i64, formatter: Option<Box<dyn OutputFormatterInterface>>) -> Result<Self> { + pub fn new( + input: String, + verbosity: i64, + formatter: Option<Box<dyn OutputFormatterInterface>>, + ) -> Result<Self> { let mut input_obj = StringInput::new(input); input_obj.set_interactive(false); @@ -66,7 +73,13 @@ impl BufferIO { } pub fn set_user_inputs(&mut self, inputs: Vec<String>) -> Result<()> { - if self.inner.input.as_any().downcast_ref::<dyn StreamableInputInterface>().is_none() { + if self + .inner + .input + .as_any() + .downcast_ref::<dyn StreamableInputInterface>() + .is_none() + { return Err(RuntimeException { message: "Setting the user inputs requires at least the version 3.2 of the symfony/console component.".to_string(), code: 0, diff --git a/crates/shirabe/src/io/console_io.rs b/crates/shirabe/src/io/console_io.rs index 8dd9f0b..0794ca5 100644 --- a/crates/shirabe/src/io/console_io.rs +++ b/crates/shirabe/src/io/console_io.rs @@ -11,9 +11,9 @@ use shirabe_external_packages::symfony::component::console::output::output_inter use shirabe_external_packages::symfony::component::console::question::choice_question::ChoiceQuestion; use shirabe_external_packages::symfony::component::console::question::question::Question; use shirabe_php_shim::{ - array_filter, array_keys, array_search, count, function_exists, implode, in_array, is_array, - is_string, mb_check_encoding, mb_convert_encoding, microtime, sprintf, str_repeat, strip_tags, - strlen, PhpMixed, + PhpMixed, array_filter, array_keys, array_search, count, function_exists, implode, in_array, + is_array, is_string, mb_check_encoding, mb_convert_encoding, microtime, sprintf, str_repeat, + strip_tags, strlen, }; use crate::io::base_io::BaseIO; @@ -158,7 +158,12 @@ impl ConsoleIO { ) }) .collect(); - PhpMixed::List(mapped.into_iter().map(|s| Box::new(PhpMixed::String(s))).collect()) + PhpMixed::List( + mapped + .into_iter() + .map(|s| Box::new(PhpMixed::String(s))) + .collect(), + ) } else { messages }; @@ -396,8 +401,8 @@ impl ConsoleIO { PhpMixed::List(_) => vec![], _ => vec![], }; - let is_assoc = !choice_keys.is_empty() - && choice_keys.iter().any(|k| !k.parse::<i64>().is_ok()); + let is_assoc = + !choice_keys.is_empty() && choice_keys.iter().any(|k| !k.parse::<i64>().is_ok()); if is_assoc { return result; } @@ -410,9 +415,7 @@ impl ConsoleIO { PhpMixed::List(l) => l .iter() .enumerate() - .filter_map(|(i, v)| { - v.as_string().map(|s| (i.to_string(), s.to_string())) - }) + .filter_map(|(i, v)| v.as_string().map(|s| (i.to_string(), s.to_string()))) .collect(), _ => IndexMap::new(), }; @@ -431,11 +434,7 @@ impl ConsoleIO { _ => vec![], }; for (index, choice) in &choice_list { - if in_array( - choice.clone(), - &PhpMixed::List(result_list.clone()), - true, - ) { + if in_array(choice.clone(), &PhpMixed::List(result_list.clone()), true) { results.push(index.clone()); } } @@ -455,7 +454,9 @@ impl ConsoleIO { fn get_error_output(&self) -> &dyn OutputInterface { if self.output.is_console_output_interface() { // TODO(phase-b): downcast Box<dyn OutputInterface> to ConsoleOutputInterface - return todo!("downcast self.output to ConsoleOutputInterface and call get_error_output()"); + return todo!( + "downcast self.output to ConsoleOutputInterface and call get_error_output()" + ); } &*self.output @@ -480,7 +481,10 @@ impl ConsoleIO { let escape_pattern = r"\x1B\[[\x30-\x3F]*[\x20-\x2F]*[\x40-\x7E]|\x1B\].*?(?:\x1B\\|\x07)|\x1B."; let pattern = if allow_newlines { - format!("{{{}|[\\x01-\\x09\\x0B\\x0C\\x0E-\\x1A]|\\r(?!\\n)}}u", escape_pattern) + format!( + "{{{}|[\\x01-\\x09\\x0B\\x0C\\x0E-\\x1A]|\\r(?!\\n)}}u", + escape_pattern + ) } else { format!("{{{}|[\\x01-\\x1A]}}u", escape_pattern) }; @@ -495,15 +499,19 @@ impl ConsoleIO { PhpMixed::List(l) => { for (key, message) in l.iter().enumerate() { let s = Self::ensure_valid_utf8(message.as_string().unwrap_or("")); - sanitized - .insert(key.to_string(), PhpMixed::String(Preg::replace(&pattern, "", &s))); + sanitized.insert( + key.to_string(), + PhpMixed::String(Preg::replace(&pattern, "", &s)), + ); } } PhpMixed::Array(a) => { for (key, message) in a { let s = Self::ensure_valid_utf8(message.as_string().unwrap_or("")); - sanitized - .insert(key.clone(), PhpMixed::String(Preg::replace(&pattern, "", &s))); + sanitized.insert( + key.clone(), + PhpMixed::String(Preg::replace(&pattern, "", &s)), + ); } } _ => {} @@ -532,9 +540,8 @@ impl ConsoleIO { // Fallback to iconv if mbstring unavailable if function_exists("iconv") { - let cleaned = Silencer::call(|| { - Ok(shirabe_php_shim::iconv("UTF-8", "UTF-8//TRANSLIT", string)) - }); + let cleaned = + Silencer::call(|| Ok(shirabe_php_shim::iconv("UTF-8", "UTF-8//TRANSLIT", string))); if let Ok(Some(c)) = cleaned { return c; } diff --git a/crates/shirabe/src/io/io_interface.rs b/crates/shirabe/src/io/io_interface.rs index 1a6eb3f..446f47c 100644 --- a/crates/shirabe/src/io/io_interface.rs +++ b/crates/shirabe/src/io/io_interface.rs @@ -1,9 +1,9 @@ //! ref: composer/src/Composer/IO/IOInterface.php +use crate::config::Config; use indexmap::IndexMap; -use shirabe_php_shim::PhpMixed; use shirabe_external_packages::psr::log::logger_interface::LoggerInterface; -use crate::config::Config; +use shirabe_php_shim::PhpMixed; pub trait IOInterface: LoggerInterface { const QUIET: i64 = 1; @@ -38,11 +38,25 @@ pub trait IOInterface: LoggerInterface { fn ask_confirmation(&self, question: String, default: bool) -> bool; - fn ask_and_validate(&self, question: String, validator: Box<dyn Fn(PhpMixed) -> PhpMixed>, attempts: Option<i64>, default: PhpMixed) -> PhpMixed; + fn ask_and_validate( + &self, + question: String, + validator: Box<dyn Fn(PhpMixed) -> PhpMixed>, + attempts: Option<i64>, + default: PhpMixed, + ) -> PhpMixed; fn ask_and_hide_answer(&self, question: String) -> Option<String>; - fn select(&self, question: String, choices: Vec<String>, default: PhpMixed, attempts: PhpMixed, error_message: String, multiselect: bool) -> PhpMixed; + fn select( + &self, + question: String, + choices: Vec<String>, + default: PhpMixed, + attempts: PhpMixed, + error_message: String, + multiselect: bool, + ) -> PhpMixed; fn get_authentications(&self) -> IndexMap<String, IndexMap<String, Option<String>>>; @@ -50,7 +64,12 @@ pub trait IOInterface: LoggerInterface { fn get_authentication(&self, repository_name: &str) -> IndexMap<String, Option<String>>; - fn set_authentication(&self, repository_name: String, username: String, password: Option<String>); + fn set_authentication( + &self, + repository_name: String, + username: String, + password: Option<String>, + ); fn load_configuration(&self, config: &Config); } diff --git a/crates/shirabe/src/io/mod.rs b/crates/shirabe/src/io/mod.rs new file mode 100644 index 0000000..a75d459 --- /dev/null +++ b/crates/shirabe/src/io/mod.rs @@ -0,0 +1,5 @@ +pub mod base_io; +pub mod buffer_io; +pub mod console_io; +pub mod io_interface; +pub mod null_io; diff --git a/crates/shirabe/src/io/null_io.rs b/crates/shirabe/src/io/null_io.rs index ad41746..4218398 100644 --- a/crates/shirabe/src/io/null_io.rs +++ b/crates/shirabe/src/io/null_io.rs @@ -1,8 +1,8 @@ //! ref: composer/src/Composer/IO/NullIO.php -use shirabe_php_shim::PhpMixed; use crate::io::base_io::BaseIO; use crate::io::io_interface::IOInterface; +use shirabe_php_shim::PhpMixed; #[derive(Debug)] pub struct NullIO { @@ -30,16 +30,19 @@ impl IOInterface for NullIO { false } - fn write(&self, _messages: PhpMixed, _newline: bool, _verbosity: i64) { - } + fn write(&self, _messages: PhpMixed, _newline: bool, _verbosity: i64) {} - fn write_error(&self, _messages: PhpMixed, _newline: bool, _verbosity: i64) { - } + fn write_error(&self, _messages: PhpMixed, _newline: bool, _verbosity: i64) {} - fn overwrite(&self, _messages: PhpMixed, _newline: bool, _size: Option<i64>, _verbosity: i64) { - } + fn overwrite(&self, _messages: PhpMixed, _newline: bool, _size: Option<i64>, _verbosity: i64) {} - fn overwrite_error(&self, _messages: PhpMixed, _newline: bool, _size: Option<i64>, _verbosity: i64) { + fn overwrite_error( + &self, + _messages: PhpMixed, + _newline: bool, + _size: Option<i64>, + _verbosity: i64, + ) { } fn ask(&self, _question: String, default: PhpMixed) -> PhpMixed { @@ -50,7 +53,13 @@ impl IOInterface for NullIO { default } - fn ask_and_validate(&self, _question: String, _validator: Box<dyn Fn(PhpMixed) -> PhpMixed>, _attempts: Option<i64>, default: PhpMixed) -> PhpMixed { + fn ask_and_validate( + &self, + _question: String, + _validator: Box<dyn Fn(PhpMixed) -> PhpMixed>, + _attempts: Option<i64>, + default: PhpMixed, + ) -> PhpMixed { default } @@ -58,7 +67,15 @@ impl IOInterface for NullIO { None } - fn select(&self, _question: String, _choices: Vec<String>, default: PhpMixed, _attempts: PhpMixed, _error_message: String, _multiselect: bool) -> PhpMixed { + fn select( + &self, + _question: String, + _choices: Vec<String>, + default: PhpMixed, + _attempts: PhpMixed, + _error_message: String, + _multiselect: bool, + ) -> PhpMixed { default } } diff --git a/crates/shirabe/src/json/json_file.rs b/crates/shirabe/src/json/json_file.rs index c6ec3cd..cdb9ba7 100644 --- a/crates/shirabe/src/json/json_file.rs +++ b/crates/shirabe/src/json/json_file.rs @@ -6,12 +6,12 @@ use shirabe_external_packages::json_schema::validator::Validator; use shirabe_external_packages::seld::json_lint::json_parser::JsonParser; use shirabe_external_packages::seld::json_lint::parsing_exception::ParsingException; use shirabe_php_shim::{ + InvalidArgumentException, JSON_ERROR_CTRL_CHAR, JSON_ERROR_DEPTH, JSON_ERROR_NONE, + JSON_ERROR_STATE_MISMATCH, JSON_ERROR_UTF8, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, + JSON_UNESCAPED_UNICODE, PhpMixed, RuntimeException, Silencer, UnexpectedValueException, defined, dirname, file_exists, file_get_contents, file_put_contents, is_dir, is_file, json_decode, json_encode_ex, json_last_error, mkdir, php_dir, realpath, str_contains, - str_ends_with, str_repeat, strlen, strpos, usleep, InvalidArgumentException, PhpMixed, - RuntimeException, Silencer, UnexpectedValueException, JSON_ERROR_CTRL_CHAR, JSON_ERROR_DEPTH, - JSON_ERROR_NONE, JSON_ERROR_STATE_MISMATCH, JSON_ERROR_UTF8, JSON_PRETTY_PRINT, - JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, + str_ends_with, str_repeat, strlen, strpos, usleep, }; use crate::downloader::transport_exception::TransportException; @@ -236,7 +236,9 @@ impl JsonFile { /// @return int|false fn file_put_contents_if_modified(&self, path: &str, content: &str) -> Result<Option<i64>> { // PHP: @file_get_contents($path) - let current_content = Silencer::call(|| Ok(file_get_contents(path))).ok().flatten(); + let current_content = Silencer::call(|| Ok(file_get_contents(path))) + .ok() + .flatten(); if current_content.is_none() || current_content.as_deref() != Some(content) { return Ok(file_put_contents(path, content.as_bytes())); } @@ -518,11 +520,18 @@ impl JsonFile { let result = result.unwrap(); Err(match file { None => ParsingException::new( - format!("The input does not contain valid JSON\n{}", result.get_message()), + format!( + "The input does not contain valid JSON\n{}", + result.get_message() + ), result.get_details(), ), Some(f) => ParsingException::new( - format!("\"{}\" does not contain valid JSON\n{}", f, result.get_message()), + format!( + "\"{}\" does not contain valid JSON\n{}", + f, + result.get_message() + ), result.get_details(), ), } @@ -530,10 +539,7 @@ impl JsonFile { } pub fn detect_indenting(json: Option<&str>) -> String { - if let Some(m) = Preg::is_match_strict_groups( - r##"#^([ \t]+)"#m"##, - json.unwrap_or(""), - ) { + if let Some(m) = Preg::is_match_strict_groups(r##"#^([ \t]+)"#m"##, json.unwrap_or("")) { return m.get(1).cloned().unwrap_or_default(); } diff --git a/crates/shirabe/src/json/json_formatter.rs b/crates/shirabe/src/json/json_formatter.rs index a88bfff..1696982 100644 --- a/crates/shirabe/src/json/json_formatter.rs +++ b/crates/shirabe/src/json/json_formatter.rs @@ -1,7 +1,7 @@ //! ref: composer/src/Composer/Json/JsonFormatter.php use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_php_shim::{function_exists, mb_convert_encoding, pack, PhpMixed}; +use shirabe_php_shim::{PhpMixed, function_exists, mb_convert_encoding, pack}; pub struct JsonFormatter; diff --git a/crates/shirabe/src/json/json_manipulator.rs b/crates/shirabe/src/json/json_manipulator.rs index 1ba2d30..ebbfff9 100644 --- a/crates/shirabe/src/json/json_manipulator.rs +++ b/crates/shirabe/src/json/json_manipulator.rs @@ -4,11 +4,10 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - addcslashes, array_key_exists, array_keys, array_reverse, count, explode, implode, in_array, - is_array, is_int, is_numeric, json_decode, max_i64, preg_quote, rtrim, str_contains, - str_repeat, str_replace, strlen, strnatcmp, strpos, substr, trim, uksort, - ArrayObject, InvalidArgumentException, LogicException, PhpMixed, RuntimeException, StdClass, - PREG_BACKTRACK_LIMIT_ERROR, + ArrayObject, InvalidArgumentException, LogicException, PREG_BACKTRACK_LIMIT_ERROR, PhpMixed, + RuntimeException, StdClass, addcslashes, array_key_exists, array_keys, array_reverse, count, + explode, implode, in_array, is_array, is_int, is_numeric, json_decode, max_i64, preg_quote, + rtrim, str_contains, str_repeat, str_replace, strlen, strnatcmp, strpos, substr, trim, uksort, }; use crate::json::json_file::JsonFile; @@ -66,20 +65,32 @@ impl JsonManipulator { format!("{}{}", self.contents, self.newline) } - pub fn add_link(&mut self, r#type: &str, package: &str, constraint: &str, sort_packages: bool) -> anyhow::Result<bool> { + pub fn add_link( + &mut self, + r#type: &str, + package: &str, + constraint: &str, + sort_packages: bool, + ) -> anyhow::Result<bool> { let decoded = JsonFile::parse_json(&self.contents, "composer.json")?; // no link of that type yet if decoded.as_array().and_then(|a| a.get(r#type)).is_none() { let mut arr: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); - arr.insert(package.to_string(), Box::new(PhpMixed::String(constraint.to_string()))); + arr.insert( + package.to_string(), + Box::new(PhpMixed::String(constraint.to_string())), + ); return self.add_main_key(r#type, PhpMixed::Array(arr)); } let regex = format!( "{{{}^(?P<start>\\s*\\{{\\s*(?:(?&string)\\s*:\\s*(?&json)\\s*,\\s*)*?)(?P<property>{}\\s*:\\s*)(?P<value>(?&json))(?P<end>.*)}}sx", Self::DEFINES, - preg_quote(&JsonFile::encode(&PhpMixed::String(r#type.to_string()), 0)?, None), + preg_quote( + &JsonFile::encode(&PhpMixed::String(r#type.to_string()), 0)?, + None + ), ); let mut matches: IndexMap<String, String> = IndexMap::new(); if !Preg::is_match_named(®ex, &self.contents, &mut matches).unwrap_or(false) { @@ -114,7 +125,11 @@ impl JsonManipulator { Box::new(move |m: &IndexMap<String, String>| -> String { format!( "{}{}\"{}\"", - JsonFile::encode(&PhpMixed::String(str_replace("\\/", "/", &existing_owned)), 0).unwrap_or_default(), + JsonFile::encode( + &PhpMixed::String(str_replace("\\/", "/", &existing_owned)), + 0 + ) + .unwrap_or_default(), m.get("separator").cloned().unwrap_or_default(), constraint_owned ) @@ -123,7 +138,13 @@ impl JsonManipulator { ); } else { let mut groups: Vec<String> = vec![]; - if Preg::is_match_strict_groups("#^\\s*\\{\\s*\\S+.*?(\\s*\\}\\s*)$#s", &links, Some(&mut groups)).unwrap_or(false) { + if Preg::is_match_strict_groups( + "#^\\s*\\{\\s*\\S+.*?(\\s*\\}\\s*)$#s", + &links, + Some(&mut groups), + ) + .unwrap_or(false) + { // link missing but non empty links links = Preg::replace( &format!("{{{}$}}", preg_quote(&groups[1], None)), @@ -201,7 +222,12 @@ impl JsonManipulator { } } - pub fn add_repository(&mut self, name: &str, config: PhpMixed, append: bool) -> anyhow::Result<bool> { + pub fn add_repository( + &mut self, + name: &str, + config: PhpMixed, + append: bool, + ) -> anyhow::Result<bool> { if "" != name && !self.do_remove_repository(name)? { return Ok(false); } @@ -213,7 +239,10 @@ impl JsonManipulator { let final_config = if is_array(&config) && !is_numeric(name) && "" != name { // PHP: ['name' => $name] + $config — preserve $config keys let mut merged: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); - merged.insert("name".to_string(), Box::new(PhpMixed::String(name.to_string()))); + merged.insert( + "name".to_string(), + Box::new(PhpMixed::String(name.to_string())), + ); if let Some(arr) = config.as_array() { for (k, v) in arr { if !merged.contains_key(k) { @@ -270,10 +299,14 @@ impl JsonManipulator { return Ok(false); } } else { - let repo: IndexMap<String, Box<PhpMixed>> = repository.as_array().cloned().unwrap_or_default(); + let repo: IndexMap<String, Box<PhpMixed>> = + repository.as_array().cloned().unwrap_or_default(); // prepend name property let mut prepended: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); - prepended.insert("name".to_string(), Box::new(PhpMixed::String(repository_name.clone()))); + prepended.insert( + "name".to_string(), + Box::new(PhpMixed::String(repository_name.clone())), + ); for (k, v) in &repo { if !prepended.contains_key(k) { prepended.insert(k.clone(), v.clone()); @@ -305,7 +338,10 @@ impl JsonManipulator { break; } - let repo_name = repository.as_array().and_then(|a| a.get("name")).and_then(|v| v.as_string()); + let repo_name = repository + .as_array() + .and_then(|a| a.get("name")) + .and_then(|v| v.as_string()); if Some(name) == repo_name { repository_index = Some(PhpMixed::String(index.clone())); break; @@ -338,7 +374,9 @@ impl JsonManipulator { let list_match = list_regex.as_ref().map_or(false, |r| { Preg::is_match_named(r, &self.contents, &mut matches).unwrap_or(false) }); - if list_match || Preg::is_match_named(&object_regex, &self.contents, &mut matches).unwrap_or(false) { + if list_match + || Preg::is_match_named(&object_regex, &self.contents, &mut matches).unwrap_or(false) + { // invalid match due to un-regexable content, abort let raw_repo = matches.get("repository").cloned().unwrap_or_default(); if json_decode(&raw_repo, false).as_bool() == Some(false) { @@ -356,14 +394,17 @@ impl JsonManipulator { matches.get("start").cloned().unwrap_or_default(), Preg::replace_callback( &repository_regex, - Box::new(move |repository_matches: &IndexMap<String, String>| -> String { - format!( - "{}{}{}", - repository_matches.get("start").cloned().unwrap_or_default(), - JsonFile::encode(&PhpMixed::String(url_owned.clone()), 0).unwrap_or_default(), - repository_matches.get("end").cloned().unwrap_or_default() - ) - }), + Box::new( + move |repository_matches: &IndexMap<String, String>| -> String { + format!( + "{}{}{}", + repository_matches.get("start").cloned().unwrap_or_default(), + JsonFile::encode(&PhpMixed::String(url_owned.clone()), 0) + .unwrap_or_default(), + repository_matches.get("end").cloned().unwrap_or_default() + ) + } + ), &raw_repo, ), matches.get("end").cloned().unwrap_or_default() @@ -400,7 +441,10 @@ impl JsonManipulator { .cloned() .unwrap_or_default(); for (i, repository) in repos.iter().enumerate() { - let repo_name = repository.as_array().and_then(|a| a.get("name")).and_then(|v| v.as_string()); + let repo_name = repository + .as_array() + .and_then(|a| a.get("name")) + .and_then(|v| v.as_string()); if Some(reference_name) == repo_name { index_to_insert = Some(i as i64); break; @@ -410,7 +454,10 @@ impl JsonManipulator { // PHP: [$referenceName => false] === $repository if let Some(arr) = repository.as_array() { if arr.len() == 1 - && arr.get(reference_name).map(|v| v.as_bool() == Some(false)).unwrap_or(false) + && arr + .get(reference_name) + .map(|v| v.as_bool() == Some(false)) + .unwrap_or(false) { index_to_insert = Some(i as i64); break; @@ -425,7 +472,10 @@ impl JsonManipulator { let final_config = if is_array(&config) && !is_numeric(name) && "" != name { let mut merged: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); - merged.insert("name".to_string(), Box::new(PhpMixed::String(name.to_string()))); + merged.insert( + "name".to_string(), + Box::new(PhpMixed::String(name.to_string())), + ); if let Some(arr) = config.as_array() { for (k, v) in arr { if !merged.contains_key(k) { @@ -500,7 +550,10 @@ impl JsonManipulator { let repository_as_array: IndexMap<String, Box<PhpMixed>> = repository.as_array().cloned().unwrap_or_default(); - if repository_as_array.get(name).map(|v| v.as_bool() == Some(false)).unwrap_or(false) + if repository_as_array + .get(name) + .map(|v| v.as_bool() == Some(false)) + .unwrap_or(false) && 1 == count(&repository_as_array) { let idx: i64 = repository_index.parse().unwrap_or(0); @@ -577,7 +630,11 @@ impl JsonManipulator { let mut sub_name: Option<String> = None; if in_array( main_node, - &vec!["config".to_string(), "extra".to_string(), "scripts".to_string()], + &vec![ + "config".to_string(), + "extra".to_string(), + "scripts".to_string(), + ], false, ) && strpos(name, ".").is_some() { @@ -610,7 +667,10 @@ impl JsonManipulator { let node_regex = format!( "{{{}^(?P<start> \\s* \\{{ \\s* (?: (?&string) \\s* : (?&json) \\s* , \\s* )*?{}\\s*:\\s*)(?P<content>(?&object))(?P<end>.*)}}sx", Self::DEFINES, - preg_quote(&JsonFile::encode(&PhpMixed::String(main_node.to_string()), 0)?, None) + preg_quote( + &JsonFile::encode(&PhpMixed::String(main_node.to_string()), 0)?, + None + ) ); let mut match_map: IndexMap<String, String> = IndexMap::new(); @@ -631,7 +691,9 @@ impl JsonManipulator { let mut children = match_map.get("content").cloned().unwrap_or_default(); // invalid match due to un-regexable content, abort - if json_decode(&children, false).is_null() || json_decode(&children, false).as_bool() == Some(false) { + if json_decode(&children, false).is_null() + || json_decode(&children, false).as_bool() == Some(false) + { return Ok(false); } @@ -659,7 +721,10 @@ impl JsonManipulator { cur_val = PhpMixed::Array(IndexMap::new()); } if let Some(arr) = cur_val.as_array_mut() { - arr.insert(sub_name_capture.clone().unwrap(), Box::new(value_local.clone())); + arr.insert( + sub_name_capture.clone().unwrap(), + Box::new(value_local.clone()), + ); } value_local = cur_val; } @@ -675,9 +740,21 @@ impl JsonManipulator { ); } else { let mut leading_match: IndexMap<String, String> = IndexMap::new(); - if Preg::is_match_named("#^\\{(?P<leadingspace>\\s*?)(?P<content>\\S+.*?)?(?P<trailingspace>\\s*)\\}$#s", &children, &mut leading_match).unwrap_or(false) { - let mut whitespace = leading_match.get("trailingspace").cloned().unwrap_or_default(); - let leading_space = leading_match.get("leadingspace").cloned().unwrap_or_default(); + if Preg::is_match_named( + "#^\\{(?P<leadingspace>\\s*?)(?P<content>\\S+.*?)?(?P<trailingspace>\\s*)\\}$#s", + &children, + &mut leading_match, + ) + .unwrap_or(false) + { + let mut whitespace = leading_match + .get("trailingspace") + .cloned() + .unwrap_or_default(); + let leading_space = leading_match + .get("leadingspace") + .cloned() + .unwrap_or_default(); let content_present = leading_match.get("content").is_some(); if content_present { let mut value_local = value.clone(); @@ -782,7 +859,10 @@ impl JsonManipulator { let node_regex = format!( "{{{}^(?P<start> \\s* \\{{ \\s* (?: (?&string) \\s* : (?&json) \\s* , \\s* )*?{}\\s*:\\s*)(?P<content>(?&object))(?P<end>.*)}}sx", Self::DEFINES, - preg_quote(&JsonFile::encode(&PhpMixed::String(main_node.to_string()), 0)?, None) + preg_quote( + &JsonFile::encode(&PhpMixed::String(main_node.to_string()), 0)?, + None + ) ); let mut match_map: IndexMap<String, String> = IndexMap::new(); let match_result = match Preg::is_match_named(&node_regex, &self.contents, &mut match_map) { @@ -803,7 +883,9 @@ impl JsonManipulator { let children = match_map.get("content").cloned().unwrap_or_default(); // invalid match due to un-regexable content, abort - if json_decode(&children, true).is_null() || json_decode(&children, true).as_bool() == Some(false) { + if json_decode(&children, true).is_null() + || json_decode(&children, true).as_bool() == Some(false) + { return Ok(false); } @@ -811,7 +893,11 @@ impl JsonManipulator { let mut sub_name: Option<String> = None; if in_array( main_node, - &vec!["config".to_string(), "extra".to_string(), "scripts".to_string()], + &vec![ + "config".to_string(), + "extra".to_string(), + "scripts".to_string(), + ], false, ) && strpos(name, ".").is_some() { @@ -823,7 +909,10 @@ impl JsonManipulator { } // no node to remove - let main_arr = main_node_value.and_then(|v| v.as_array()).cloned().unwrap_or_default(); + let main_arr = main_node_value + .and_then(|v| v.as_array()) + .cloned() + .unwrap_or_default(); if !main_arr.contains_key(&name_owned) || (sub_name.is_some() && !main_arr @@ -838,11 +927,16 @@ impl JsonManipulator { // try and find a match for the subkey let key_regex = str_replace("/", "\\\\?/", &preg_quote(&name_owned, None)); let mut children_clean: Option<String> = None; - if Preg::is_match(&format!("{{\"{}\"\\s*:}}i", key_regex), &children, None).unwrap_or(false) { + if Preg::is_match(&format!("{{\"{}\"\\s*:}}i", key_regex), &children, None).unwrap_or(false) + { // find best match for the value of "name" let mut all_matches: Vec<Vec<String>> = vec![]; if Preg::is_match_all( - &format!("{{{}\"{}\"\\s*:\\s*(?:(?&json))}}x", Self::DEFINES, key_regex), + &format!( + "{{{}\"{}\"\\s*:\\s*(?:(?&json))}}x", + Self::DEFINES, + key_regex + ), &children, &mut all_matches, ) @@ -889,7 +983,13 @@ impl JsonManipulator { // no child data left, $name was the only key in let mut empty_match: IndexMap<String, String> = IndexMap::new(); - if Preg::is_match_named("#^\\{\\s*?(?P<content>\\S+.*?)?(?P<trailingspace>\\s*)\\}$#s", &children_clean, &mut empty_match).unwrap_or(false) { + if Preg::is_match_named( + "#^\\{\\s*?(?P<content>\\S+.*?)?(?P<trailingspace>\\s*)\\}$#s", + &children_clean, + &mut empty_match, + ) + .unwrap_or(false) + { if empty_match.get("content").is_none() { let newline = self.newline.clone(); let indent = self.indent.clone(); @@ -912,7 +1012,8 @@ impl JsonManipulator { if let Some(sub) = sub_name { let mut cur_val = json_decode(&children, true); if let Some(arr) = cur_val.as_array_mut() { - if let Some(inner) = arr.get_mut(&name_owned).and_then(|v| v.as_array_mut()) { + if let Some(inner) = arr.get_mut(&name_owned).and_then(|v| v.as_array_mut()) + { inner.shift_remove(&sub); } let now_empty = arr @@ -921,7 +1022,10 @@ impl JsonManipulator { .map(|a| a.is_empty()) .unwrap_or(false); if now_empty { - arr.insert(name_owned.clone(), Box::new(PhpMixed::Object(ArrayObject::new()))); + arr.insert( + name_owned.clone(), + Box::new(PhpMixed::Object(ArrayObject::new())), + ); } } let val = cur_val @@ -948,9 +1052,12 @@ impl JsonManipulator { Box::new(move |matches: &IndexMap<String, String>| -> String { let mut children_clean = children_clean_capture.clone(); if let Some(ref sub) = sub_name_capture { - let mut cur_val = json_decode(matches.get("content").unwrap_or(&String::new()), true); + let mut cur_val = + json_decode(matches.get("content").unwrap_or(&String::new()), true); if let Some(arr) = cur_val.as_array_mut() { - if let Some(inner) = arr.get_mut(&name_capture).and_then(|v| v.as_array_mut()) { + if let Some(inner) = + arr.get_mut(&name_capture).and_then(|v| v.as_array_mut()) + { inner.shift_remove(sub); } let now_empty = arr @@ -959,7 +1066,10 @@ impl JsonManipulator { .map(|a| a.is_empty()) .unwrap_or(false); if now_empty { - arr.insert(name_capture.clone(), Box::new(PhpMixed::Object(ArrayObject::new()))); + arr.insert( + name_capture.clone(), + Box::new(PhpMixed::Object(ArrayObject::new())), + ); } } children_clean = formatter.format(&cur_val, 0, true).unwrap_or_default(); @@ -978,7 +1088,12 @@ impl JsonManipulator { Ok(true) } - pub fn add_list_item(&mut self, main_node: &str, value: PhpMixed, append: bool) -> anyhow::Result<bool> { + pub fn add_list_item( + &mut self, + main_node: &str, + value: PhpMixed, + append: bool, + ) -> anyhow::Result<bool> { let decoded = JsonFile::parse_json(&self.contents, "composer.json")?; // no main node yet @@ -992,7 +1107,10 @@ impl JsonManipulator { let node_regex = format!( "{{{}^(?P<start> \\s* \\{{ \\s* (?: (?&string) \\s* : (?&json) \\s* , \\s* )*?{}\\s*:\\s*)(?P<content>(?&array))(?P<end>.*)}}sx", Self::DEFINES, - preg_quote(&JsonFile::encode(&PhpMixed::String(main_node.to_string()), 0)?, None) + preg_quote( + &JsonFile::encode(&PhpMixed::String(main_node.to_string()), 0)?, + None + ) ); let mut match_map: IndexMap<String, String> = IndexMap::new(); @@ -1018,10 +1136,23 @@ impl JsonManipulator { } let mut leading_match: IndexMap<String, String> = IndexMap::new(); - if Preg::is_match_named("#^\\[(?P<leadingspace>\\s*?)(?P<content>\\S+.*?)?(?P<trailingspace>\\s*)\\]$#s", &children, &mut leading_match).unwrap_or(false) { - let leading_whitespace = leading_match.get("leadingspace").cloned().unwrap_or_default(); - let mut whitespace = leading_match.get("trailingspace").cloned().unwrap_or_default(); - let mut leading_item_whitespace = format!("{}{}{}", self.newline, self.indent, self.indent); + if Preg::is_match_named( + "#^\\[(?P<leadingspace>\\s*?)(?P<content>\\S+.*?)?(?P<trailingspace>\\s*)\\]$#s", + &children, + &mut leading_match, + ) + .unwrap_or(false) + { + let leading_whitespace = leading_match + .get("leadingspace") + .cloned() + .unwrap_or_default(); + let mut whitespace = leading_match + .get("trailingspace") + .cloned() + .unwrap_or_default(); + let mut leading_item_whitespace = + format!("{}{}{}", self.newline, self.indent, self.indent); let mut trailing_item_whitespace = whitespace.clone(); let mut item_depth: i64 = 1; @@ -1098,7 +1229,12 @@ impl JsonManipulator { Ok(true) } - pub fn insert_list_item(&mut self, main_node: &str, value: PhpMixed, index: i64) -> anyhow::Result<bool> { + pub fn insert_list_item( + &mut self, + main_node: &str, + value: PhpMixed, + index: i64, + ) -> anyhow::Result<bool> { if index < 0 { return Err(InvalidArgumentException { message: "Index can only be positive integer".to_string(), @@ -1134,7 +1270,10 @@ impl JsonManipulator { let node_regex = format!( "{{{}^(?P<start> \\s* \\{{ \\s* (?: (?&string) \\s* : (?&json) \\s* , \\s* )*?{}\\s*:\\s*)(?P<content>(?&array))(?P<end>.*)}}sx", Self::DEFINES, - preg_quote(&JsonFile::encode(&PhpMixed::String(main_node.to_string()), 0)?, None) + preg_quote( + &JsonFile::encode(&PhpMixed::String(main_node.to_string()), 0)?, + None + ) ); let mut match_map: IndexMap<String, String> = IndexMap::new(); @@ -1177,7 +1316,9 @@ impl JsonManipulator { "{}{}{},{}{}", m.get("start").cloned().unwrap_or_default(), m.get("space_before_item").cloned().unwrap_or_default(), - formatter.format(&value_capture, 1, false).unwrap_or_default(), + formatter + .format(&value_capture, 1, false) + .unwrap_or_default(), m.get("space_before_item").cloned().unwrap_or_default(), m.get("end").cloned().unwrap_or_default() ) @@ -1220,7 +1361,10 @@ impl JsonManipulator { let node_regex = format!( "{{{}^(?P<start> \\s* \\{{ \\s* (?: (?&string) \\s* : (?&json) \\s* , \\s* )*?{}\\s*:\\s*)(?P<content>(?&array))(?P<end>.*)}}sx", Self::DEFINES, - preg_quote(&JsonFile::encode(&PhpMixed::String(main_node.to_string()), 0)?, None) + preg_quote( + &JsonFile::encode(&PhpMixed::String(main_node.to_string()), 0)?, + None + ) ); let mut match_map: IndexMap<String, String> = IndexMap::new(); let match_result = match Preg::is_match_named(&node_regex, &self.contents, &mut match_map) { @@ -1246,7 +1390,10 @@ impl JsonManipulator { } // no node to remove - let main_list = main_node_value.and_then(|v| v.as_list()).cloned().unwrap_or_default(); + let main_list = main_node_value + .and_then(|v| v.as_list()) + .cloned() + .unwrap_or_default(); if main_list.get(node_index as usize).is_none() { return Ok(true); } @@ -1308,7 +1455,10 @@ impl JsonManipulator { let regex = format!( "{{{}^(?P<start>\\s*\\{{\\s*(?:(?&string)\\s*:\\s*(?&json)\\s*,\\s*)*?)(?P<key>{}\\s*:\\s*(?&json))(?P<end>.*)}}sx", Self::DEFINES, - preg_quote(&JsonFile::encode(&PhpMixed::String(key.to_string()), 0)?, None) + preg_quote( + &JsonFile::encode(&PhpMixed::String(key.to_string()), 0)?, + None + ) ); let mut matches: IndexMap<String, String> = IndexMap::new(); if decoded.as_array().and_then(|a| a.get(key)).is_some() @@ -1333,7 +1483,9 @@ impl JsonManipulator { // append at the end of the file and keep whitespace let mut tail_match: Vec<String> = vec![]; - if Preg::is_match("#[^{\\s](\\s*)\\}$#", &self.contents, Some(&mut tail_match)).unwrap_or(false) { + if Preg::is_match("#[^{\\s](\\s*)\\}$#", &self.contents, Some(&mut tail_match)) + .unwrap_or(false) + { self.contents = Preg::replace( &format!("#{}\\}}$#", tail_match[1]), &addcslashes( @@ -1384,7 +1536,10 @@ impl JsonManipulator { let regex = format!( "{{{}^(?P<start>\\s*\\{{\\s*(?:(?&string)\\s*:\\s*(?&json)\\s*,\\s*)*?)(?P<removal>{}\\s*:\\s*(?&json))\\s*,?\\s*(?P<end>.*)}}sx", Self::DEFINES, - preg_quote(&JsonFile::encode(&PhpMixed::String(key.to_string()), 0)?, None) + preg_quote( + &JsonFile::encode(&PhpMixed::String(key.to_string()), 0)?, + None + ) ); let mut matches: IndexMap<String, String> = IndexMap::new(); if Preg::is_match_named(®ex, &self.contents, &mut matches).unwrap_or(false) { @@ -1425,7 +1580,10 @@ impl JsonManipulator { let regex = format!( "{{{}^(?P<start>\\s*\\{{\\s*(?:(?&string)\\s*:\\s*(?&json)\\s*,\\s*)*?{}\\s*:\\s*)(?P<removal>\\{{(?P<removal_space>\\s*+)\\}})(?P<end>\\s*,?\\s*.*)}}sx", Self::DEFINES, - preg_quote(&JsonFile::encode(&PhpMixed::String(key.to_string()), 0)?, None) + preg_quote( + &JsonFile::encode(&PhpMixed::String(key.to_string()), 0)?, + None + ) ); let mut matches: IndexMap<String, String> = IndexMap::new(); if Preg::is_match_named(®ex, &self.contents, &mut matches).unwrap_or(false) { @@ -1456,7 +1614,10 @@ impl JsonManipulator { return Ok(true); } - let value = decoded_arr.get(key).map(|v| (**v).clone()).unwrap_or(PhpMixed::Null); + let value = decoded_arr + .get(key) + .map(|v| (**v).clone()) + .unwrap_or(PhpMixed::Null); if is_array(&value) && value.as_array().map(|a| a.len()).unwrap_or(0) == 0 { return self.remove_main_key(key); } @@ -1551,7 +1712,11 @@ impl ManipulatorFormatter { && data.as_list().map(|l| l.len()).unwrap_or(0) == 0 { return Ok(if was_object { - format!("{{{}{}}}", self.newline, str_repeat(&self.indent, depth + 1)) + format!( + "{{{}{}}}", + self.newline, + str_repeat(&self.indent, depth + 1) + ) } else { "[]".to_string() }); diff --git a/crates/shirabe/src/json/mod.rs b/crates/shirabe/src/json/mod.rs new file mode 100644 index 0000000..3e5f22c --- /dev/null +++ b/crates/shirabe/src/json/mod.rs @@ -0,0 +1,4 @@ +pub mod json_file; +pub mod json_formatter; +pub mod json_manipulator; +pub mod json_validation_exception; diff --git a/crates/shirabe/src/lib.rs b/crates/shirabe/src/lib.rs new file mode 100644 index 0000000..a8fe3a7 --- /dev/null +++ b/crates/shirabe/src/lib.rs @@ -0,0 +1,28 @@ +pub mod advisory; +pub mod autoload; +pub mod cache; +pub mod command; +pub mod compiler; +pub mod composer; +pub mod config; +pub mod console; +pub mod dependency_resolver; +pub mod downloader; +pub mod event_dispatcher; +pub mod exception; +pub mod factory; +pub mod filter; +pub mod installed_versions; +pub mod installer; +pub mod io; +pub mod json; +pub mod package; +pub mod partial_composer; +pub mod phpstan; +pub mod platform; +pub mod plugin; +pub mod question; +pub mod repository; +pub mod script; +pub mod self_update; +pub mod util; diff --git a/crates/shirabe/src/package/alias_package.rs b/crates/shirabe/src/package/alias_package.rs index 0c8158f..a101a1f 100644 --- a/crates/shirabe/src/package/alias_package.rs +++ b/crates/shirabe/src/package/alias_package.rs @@ -2,7 +2,7 @@ use chrono::{DateTime, Utc}; use indexmap::IndexMap; -use shirabe_php_shim::{in_array, PhpMixed}; +use shirabe_php_shim::{PhpMixed, in_array}; use shirabe_semver::constraint::constraint::Constraint; use crate::package::base_package::BasePackage; diff --git a/crates/shirabe/src/package/archiver/archivable_files_filter.rs b/crates/shirabe/src/package/archiver/archivable_files_filter.rs index a267cf1..f820e08 100644 --- a/crates/shirabe/src/package/archiver/archivable_files_filter.rs +++ b/crates/shirabe/src/package/archiver/archivable_files_filter.rs @@ -1,7 +1,7 @@ //! ref: composer/src/Composer/Package/Archiver/ArchivableFilesFilter.php -use std::path::PathBuf; use shirabe_php_shim::PharData; +use std::path::PathBuf; pub struct ArchivableFilesFilter { inner: Box<dyn Iterator<Item = PathBuf>>, diff --git a/crates/shirabe/src/package/archiver/archivable_files_finder.rs b/crates/shirabe/src/package/archiver/archivable_files_finder.rs index 7d1b94e..35ec36f 100644 --- a/crates/shirabe/src/package/archiver/archivable_files_finder.rs +++ b/crates/shirabe/src/package/archiver/archivable_files_finder.rs @@ -1,12 +1,12 @@ //! ref: composer/src/Composer/Package/Archiver/ArchivableFilesFinder.php -use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_external_packages::symfony::component::finder::finder::Finder; -use shirabe_external_packages::symfony::component::finder::spl_file_info::SplFileInfo; -use shirabe_php_shim::{preg_quote, realpath, RuntimeException}; use crate::package::archiver::composer_exclude_filter::ComposerExcludeFilter; use crate::package::archiver::git_exclude_filter::GitExcludeFilter; use crate::util::filesystem::Filesystem; +use shirabe_external_packages::composer::pcre::preg::Preg; +use shirabe_external_packages::symfony::component::finder::finder::Finder; +use shirabe_external_packages::symfony::component::finder::spl_file_info::SplFileInfo; +use shirabe_php_shim::{RuntimeException, preg_quote, realpath}; pub struct ArchivableFilesFinder { pub(crate) finder: Finder, @@ -30,7 +30,8 @@ impl ArchivableFilesFinder { return Err(RuntimeException { message: format!("Could not realpath() the source directory \"{}\"", sources), code: 0, - }.into()); + } + .into()); } let sources = fs.normalize_path(&sources_real_path.unwrap()); diff --git a/crates/shirabe/src/package/archiver/archive_manager.rs b/crates/shirabe/src/package/archiver/archive_manager.rs index ed7c6f7..3f7bfe0 100644 --- a/crates/shirabe/src/package/archiver/archive_manager.rs +++ b/crates/shirabe/src/package/archiver/archive_manager.rs @@ -1,11 +1,11 @@ //! ref: composer/src/Composer/Package/Archiver/ArchiveManager.php use indexmap::IndexMap; +use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - bin2hex, file_exists, random_bytes, realpath, sys_get_temp_dir, InvalidArgumentException, - RuntimeException, + InvalidArgumentException, RuntimeException, bin2hex, file_exists, random_bytes, realpath, + sys_get_temp_dir, }; -use shirabe_external_packages::composer::pcre::preg::Preg; use crate::downloader::download_manager::DownloadManager; use crate::json::json_file::JsonFile; @@ -58,11 +58,7 @@ impl ArchiveManager { ) -> IndexMap<String, String> { let base_name = match package.get_archive_name() { Some(name) => name.to_string(), - None => Preg::replace( - "#[^a-z0-9-_]#i", - "-", - package.get_name(), - ), + None => Preg::replace("#[^a-z0-9-_]#i", "-", package.get_name()), }; let mut parts: IndexMap<String, String> = IndexMap::new(); @@ -129,7 +125,10 @@ impl ArchiveManager { let mut usable_archiver_idx: Option<usize> = None; for (i, archiver) in self.archivers.iter().enumerate() { - if archiver.supports(format.clone(), package.get_source_type().map(|s| s.to_string())) { + if archiver.supports( + format.clone(), + package.get_source_type().map(|s| s.to_string()), + ) { usable_archiver_idx = Some(i); break; } diff --git a/crates/shirabe/src/package/archiver/base_exclude_filter.rs b/crates/shirabe/src/package/archiver/base_exclude_filter.rs index 7f737fe..f20af20 100644 --- a/crates/shirabe/src/package/archiver/base_exclude_filter.rs +++ b/crates/shirabe/src/package/archiver/base_exclude_filter.rs @@ -53,7 +53,10 @@ impl BaseExcludeFilter { } pub fn generate_patterns(&self, rules: Vec<String>) -> Vec<(String, bool, bool)> { - rules.into_iter().map(|rule| self.generate_pattern(&rule)).collect() + rules + .into_iter() + .map(|rule| self.generate_pattern(&rule)) + .collect() } pub fn generate_pattern(&self, rule: &str) -> (String, bool, bool) { diff --git a/crates/shirabe/src/package/archiver/git_exclude_filter.rs b/crates/shirabe/src/package/archiver/git_exclude_filter.rs index b3e43dc..8028c15 100644 --- a/crates/shirabe/src/package/archiver/git_exclude_filter.rs +++ b/crates/shirabe/src/package/archiver/git_exclude_filter.rs @@ -1,8 +1,8 @@ //! ref: composer/src/Composer/Package/Archiver/GitExcludeFilter.php -use std::path::Path; -use shirabe_external_packages::composer::pcre::preg::Preg; use crate::package::archiver::base_exclude_filter::BaseExcludeFilter; +use shirabe_external_packages::composer::pcre::preg::Preg; +use std::path::Path; pub struct GitExcludeFilter { inner: BaseExcludeFilter, @@ -20,10 +20,9 @@ impl GitExcludeFilter { .lines() .map(|l| l.to_string()) .collect(); - let patterns = filter.inner.parse_lines( - lines, - |line| GitExcludeFilter::parse_git_attributes_line_static(line), - ); + let patterns = filter.inner.parse_lines(lines, |line| { + GitExcludeFilter::parse_git_attributes_line_static(line) + }); filter.inner.exclude_patterns.extend(patterns); } diff --git a/crates/shirabe/src/package/archiver/mod.rs b/crates/shirabe/src/package/archiver/mod.rs new file mode 100644 index 0000000..ecf86c8 --- /dev/null +++ b/crates/shirabe/src/package/archiver/mod.rs @@ -0,0 +1,9 @@ +pub mod archivable_files_filter; +pub mod archivable_files_finder; +pub mod archive_manager; +pub mod archiver_interface; +pub mod base_exclude_filter; +pub mod composer_exclude_filter; +pub mod git_exclude_filter; +pub mod phar_archiver; +pub mod zip_archiver; diff --git a/crates/shirabe/src/package/archiver/phar_archiver.rs b/crates/shirabe/src/package/archiver/phar_archiver.rs index 24cd738..2e9d96e 100644 --- a/crates/shirabe/src/package/archiver/phar_archiver.rs +++ b/crates/shirabe/src/package/archiver/phar_archiver.rs @@ -2,8 +2,8 @@ use indexmap::IndexMap; use shirabe_php_shim::{ - bzcompress, file_exists, file_put_contents, function_exists, gzcompress, pack, str_repeat, - strrpos, unlink, FilesystemIterator, Phar, PharData, PhpMixed, RuntimeException, + FilesystemIterator, Phar, PharData, PhpMixed, RuntimeException, bzcompress, file_exists, + file_put_contents, function_exists, gzcompress, pack, str_repeat, strrpos, unlink, }; use crate::package::archiver::archivable_files_filter::ArchivableFilesFilter; @@ -79,14 +79,14 @@ impl ArchiverInterface for PharArchiver { let eocd = pack( "VvvvvVVv", &[ - PhpMixed::Int(0x06054b50), // End of central directory signature - PhpMixed::Int(0), // Number of this disk - PhpMixed::Int(0), // Disk where central directory starts - PhpMixed::Int(0), // Number of central directory records on this disk - PhpMixed::Int(0), // Total number of central directory records - PhpMixed::Int(0), // Size of central directory (bytes) - PhpMixed::Int(0), // Offset of start of central directory - PhpMixed::Int(0), // Comment length + PhpMixed::Int(0x06054b50), // End of central directory signature + PhpMixed::Int(0), // Number of this disk + PhpMixed::Int(0), // Disk where central directory starts + PhpMixed::Int(0), // Number of central directory records on this disk + PhpMixed::Int(0), // Total number of central directory records + PhpMixed::Int(0), // Size of central directory (bytes) + PhpMixed::Int(0), // Offset of start of central directory + PhpMixed::Int(0), // Comment length ], ); file_put_contents(&target, &eocd); @@ -100,10 +100,12 @@ impl ArchiverInterface for PharArchiver { .into()); } if format == "tar.gz" && function_exists("gzcompress") { - let data = gzcompress(&str_repeat("\0", 10240).into_bytes()).unwrap_or_default(); + let data = + gzcompress(&str_repeat("\0", 10240).into_bytes()).unwrap_or_default(); file_put_contents(&target, &data); } else if format == "tar.bz2" && function_exists("bzcompress") { - let data = bzcompress(&str_repeat("\0", 10240).into_bytes()).unwrap_or_default(); + let data = + bzcompress(&str_repeat("\0", 10240).into_bytes()).unwrap_or_default(); file_put_contents(&target, &data); } } diff --git a/crates/shirabe/src/package/archiver/zip_archiver.rs b/crates/shirabe/src/package/archiver/zip_archiver.rs index 104c544..ef5b40a 100644 --- a/crates/shirabe/src/package/archiver/zip_archiver.rs +++ b/crates/shirabe/src/package/archiver/zip_archiver.rs @@ -1,11 +1,13 @@ //! ref: composer/src/Composer/Package/Archiver/ZipArchiver.php -use indexmap::IndexMap; -use shirabe_php_shim::{class_exists, fileperms, method_exists, pack, realpath, PhpMixed, RuntimeException, ZipArchive}; use crate::package::archiver::archivable_files_finder::ArchivableFilesFinder; use crate::package::archiver::archiver_interface::ArchiverInterface; use crate::util::filesystem::Filesystem; use crate::util::platform::Platform; +use indexmap::IndexMap; +use shirabe_php_shim::{ + PhpMixed, RuntimeException, ZipArchive, class_exists, fileperms, method_exists, pack, realpath, +}; #[derive(Debug)] pub struct ZipArchiver; @@ -60,22 +62,29 @@ impl ArchiverInterface for ZipArchiver { // setExternalAttributesName() is only available with libzip 0.11.2 or above if method_exists(&PhpMixed::Null, "setExternalAttributesName") { let perms = fileperms(&filepath); - zip.set_external_attributes_name(&relative_path, ZipArchive::OPSYS_UNIX, perms << 16); + zip.set_external_attributes_name( + &relative_path, + ZipArchive::OPSYS_UNIX, + perms << 16, + ); } } if zip.close() { if !std::path::Path::new(&target).exists() { // create minimal valid ZIP file (Empty Central Directory + End of Central Directory record) - let eocd = pack("VvvvvVVv", &[ - PhpMixed::Int(0x06054b50), // End of central directory signature - PhpMixed::Int(0), // Number of this disk - PhpMixed::Int(0), // Disk where central directory starts - PhpMixed::Int(0), // Number of central directory records on this disk - PhpMixed::Int(0), // Total number of central directory records - PhpMixed::Int(0), // Size of central directory (bytes) - PhpMixed::Int(0), // Offset of start of central directory - PhpMixed::Int(0), // Comment length - ]); + let eocd = pack( + "VvvvvVVv", + &[ + PhpMixed::Int(0x06054b50), // End of central directory signature + PhpMixed::Int(0), // Number of this disk + PhpMixed::Int(0), // Disk where central directory starts + PhpMixed::Int(0), // Number of central directory records on this disk + PhpMixed::Int(0), // Total number of central directory records + PhpMixed::Int(0), // Size of central directory (bytes) + PhpMixed::Int(0), // Offset of start of central directory + PhpMixed::Int(0), // Comment length + ], + ); std::fs::write(&target, &eocd)?; } diff --git a/crates/shirabe/src/package/base_package.rs b/crates/shirabe/src/package/base_package.rs index 820e51b..4cbd11c 100644 --- a/crates/shirabe/src/package/base_package.rs +++ b/crates/shirabe/src/package/base_package.rs @@ -3,7 +3,7 @@ use std::sync::LazyLock; use indexmap::IndexMap; -use shirabe_php_shim::{preg_quote, LogicException, UnexpectedValueException}; +use shirabe_php_shim::{LogicException, UnexpectedValueException, preg_quote}; use crate::package::link::Link; use crate::package::package_interface::PackageInterface; diff --git a/crates/shirabe/src/package/comparer/comparer.rs b/crates/shirabe/src/package/comparer/comparer.rs index 8f7f312..0e0d295 100644 --- a/crates/shirabe/src/package/comparer/comparer.rs +++ b/crates/shirabe/src/package/comparer/comparer.rs @@ -81,17 +81,26 @@ impl Comparer { let dest_file_hash = destination.get(dir).and_then(|d| d.get(file)); if let Some(dest_hash) = dest_file_hash { if hash != dest_hash { - self.changed.entry("changed".to_string()).or_default().push(format!("{}/{}", dir, file)); + self.changed + .entry("changed".to_string()) + .or_default() + .push(format!("{}/{}", dir, file)); } } else { - self.changed.entry("removed".to_string()).or_default().push(format!("{}/{}", dir, file)); + self.changed + .entry("removed".to_string()) + .or_default() + .push(format!("{}/{}", dir, file)); } } } for (dir, value) in &destination { for (file, _hash) in value { if !source.get(dir).map_or(false, |d| d.contains_key(file)) { - self.changed.entry("added".to_string()).or_default().push(format!("{}/{}", dir, file)); + self.changed + .entry("added".to_string()) + .or_default() + .push(format!("{}/{}", dir, file)); } } } @@ -106,8 +115,13 @@ impl Comparer { } let path = format!("{}/{}", dir, file); if Path::new(&path).is_symlink() { - let link_target = std::fs::read_link(&path).ok().and_then(|p| p.to_str().map(|s| s.to_string())); - array.entry(dir.to_string()).or_default().insert(file, link_target); + let link_target = std::fs::read_link(&path) + .ok() + .and_then(|p| p.to_str().map(|s| s.to_string())); + array + .entry(dir.to_string()) + .or_default() + .insert(file, link_target); } else if Path::new(&path).is_dir() { if array.is_empty() { array.insert("0".to_string(), IndexMap::new()); @@ -118,7 +132,11 @@ impl Comparer { } else if Path::new(&path).is_file() { let size = std::fs::metadata(&path).map(|m| m.len()).unwrap_or(0); if size > 0 { - let algo = if shirabe_php_shim::PHP_VERSION_ID > 80100 { "xxh3" } else { "sha1" }; + let algo = if shirabe_php_shim::PHP_VERSION_ID > 80100 { + "xxh3" + } else { + "sha1" + }; let hash = shirabe_php_shim::hash_file(algo, &path); array.entry(dir.to_string()).or_default().insert(file, hash); } diff --git a/crates/shirabe/src/package/comparer/mod.rs b/crates/shirabe/src/package/comparer/mod.rs new file mode 100644 index 0000000..4c5c6d2 --- /dev/null +++ b/crates/shirabe/src/package/comparer/mod.rs @@ -0,0 +1 @@ +pub mod comparer; diff --git a/crates/shirabe/src/package/complete_alias_package.rs b/crates/shirabe/src/package/complete_alias_package.rs index 9da998a..134ed3a 100644 --- a/crates/shirabe/src/package/complete_alias_package.rs +++ b/crates/shirabe/src/package/complete_alias_package.rs @@ -33,7 +33,10 @@ impl CompleteAliasPackage { self.alias_of.get_repositories() } - pub fn set_repositories(&mut self, repositories: Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>) { + pub fn set_repositories( + &mut self, + repositories: Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>, + ) { self.alias_of.set_repositories(repositories); } @@ -89,7 +92,10 @@ impl CompleteAliasPackage { self.alias_of.get_funding() } - pub fn set_funding(&mut self, funding: Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>) { + pub fn set_funding( + &mut self, + funding: Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>, + ) { self.alias_of.set_funding(funding); } diff --git a/crates/shirabe/src/package/complete_package.rs b/crates/shirabe/src/package/complete_package.rs index 2120eec..ec2a42a 100644 --- a/crates/shirabe/src/package/complete_package.rs +++ b/crates/shirabe/src/package/complete_package.rs @@ -1,9 +1,9 @@ //! ref: composer/src/Composer/Package/CompletePackage.php -use indexmap::IndexMap; -use shirabe_php_shim::PhpMixed; use crate::package::complete_package_interface::CompletePackageInterface; use crate::package::package::Package; +use indexmap::IndexMap; +use shirabe_php_shim::PhpMixed; #[derive(Debug)] pub struct CompletePackage { diff --git a/crates/shirabe/src/package/dumper/array_dumper.rs b/crates/shirabe/src/package/dumper/array_dumper.rs index bee9aa7..02f1092 100644 --- a/crates/shirabe/src/package/dumper/array_dumper.rs +++ b/crates/shirabe/src/package/dumper/array_dumper.rs @@ -20,28 +20,63 @@ impl ArrayDumper { pub fn dump(&self, package: &dyn PackageInterface) -> IndexMap<String, PhpMixed> { let mut data: IndexMap<String, PhpMixed> = IndexMap::new(); - data.insert("name".to_string(), PhpMixed::String(package.get_pretty_name().to_string())); - data.insert("version".to_string(), PhpMixed::String(package.get_pretty_version().to_string())); - data.insert("version_normalized".to_string(), PhpMixed::String(package.get_version().to_string())); + data.insert( + "name".to_string(), + PhpMixed::String(package.get_pretty_name().to_string()), + ); + data.insert( + "version".to_string(), + PhpMixed::String(package.get_pretty_version().to_string()), + ); + data.insert( + "version_normalized".to_string(), + PhpMixed::String(package.get_version().to_string()), + ); if let Some(target_dir) = package.get_target_dir() { - data.insert("target-dir".to_string(), PhpMixed::String(target_dir.to_string())); + data.insert( + "target-dir".to_string(), + PhpMixed::String(target_dir.to_string()), + ); } if let Some(source_type) = package.get_source_type() { let mut source: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); - source.insert("type".to_string(), Box::new(PhpMixed::String(source_type.to_string()))); - source.insert("url".to_string(), Box::new(PhpMixed::String(package.get_source_url().unwrap_or("").to_string()))); + source.insert( + "type".to_string(), + Box::new(PhpMixed::String(source_type.to_string())), + ); + source.insert( + "url".to_string(), + Box::new(PhpMixed::String( + package.get_source_url().unwrap_or("").to_string(), + )), + ); if let Some(reference) = package.get_source_reference() { - source.insert("reference".to_string(), Box::new(PhpMixed::String(reference.to_string()))); + source.insert( + "reference".to_string(), + Box::new(PhpMixed::String(reference.to_string())), + ); } if let Some(mirrors) = package.get_source_mirrors() { if !mirrors.is_empty() { - source.insert("mirrors".to_string(), Box::new(PhpMixed::Array( - mirrors.into_iter().enumerate().map(|(i, m)| (i.to_string(), Box::new(PhpMixed::Array( - m.into_iter().map(|(k, v)| (k, Box::new(v))).collect() - )))).collect() - ))); + source.insert( + "mirrors".to_string(), + Box::new(PhpMixed::Array( + mirrors + .into_iter() + .enumerate() + .map(|(i, m)| { + ( + i.to_string(), + Box::new(PhpMixed::Array( + m.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), + )), + ) + }) + .collect(), + )), + ); } } data.insert("source".to_string(), PhpMixed::Array(source)); @@ -49,21 +84,47 @@ impl ArrayDumper { if let Some(dist_type) = package.get_dist_type() { let mut dist: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); - dist.insert("type".to_string(), Box::new(PhpMixed::String(dist_type.to_string()))); - dist.insert("url".to_string(), Box::new(PhpMixed::String(package.get_dist_url().unwrap_or("").to_string()))); + dist.insert( + "type".to_string(), + Box::new(PhpMixed::String(dist_type.to_string())), + ); + dist.insert( + "url".to_string(), + Box::new(PhpMixed::String( + package.get_dist_url().unwrap_or("").to_string(), + )), + ); if let Some(reference) = package.get_dist_reference() { - dist.insert("reference".to_string(), Box::new(PhpMixed::String(reference.to_string()))); + dist.insert( + "reference".to_string(), + Box::new(PhpMixed::String(reference.to_string())), + ); } if let Some(shasum) = package.get_dist_sha1_checksum() { - dist.insert("shasum".to_string(), Box::new(PhpMixed::String(shasum.to_string()))); + dist.insert( + "shasum".to_string(), + Box::new(PhpMixed::String(shasum.to_string())), + ); } if let Some(mirrors) = package.get_dist_mirrors() { if !mirrors.is_empty() { - dist.insert("mirrors".to_string(), Box::new(PhpMixed::Array( - mirrors.into_iter().enumerate().map(|(i, m)| (i.to_string(), Box::new(PhpMixed::Array( - m.into_iter().map(|(k, v)| (k, Box::new(v))).collect() - )))).collect() - ))); + dist.insert( + "mirrors".to_string(), + Box::new(PhpMixed::Array( + mirrors + .into_iter() + .enumerate() + .map(|(i, m)| { + ( + i.to_string(), + Box::new(PhpMixed::Array( + m.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), + )), + ) + }) + .collect(), + )), + ); } } data.insert("dist".to_string(), PhpMixed::Array(dist)); @@ -77,7 +138,10 @@ impl ArrayDumper { } let mut link_map: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); for link in &links { - link_map.insert(link.get_target().to_string(), Box::new(PhpMixed::String(link.get_pretty_constraint().to_string()))); + link_map.insert( + link.get_target().to_string(), + Box::new(PhpMixed::String(link.get_pretty_constraint().to_string())), + ); } link_map.sort_keys(); data.insert(type_name, PhpMixed::Array(link_map)); @@ -87,13 +151,22 @@ impl ArrayDumper { if !suggests.is_empty() { let mut sorted_suggests = suggests.clone(); sorted_suggests.sort_keys(); - data.insert("suggest".to_string(), PhpMixed::Array( - sorted_suggests.into_iter().map(|(k, v)| (k, Box::new(PhpMixed::String(v)))).collect() - )); + data.insert( + "suggest".to_string(), + PhpMixed::Array( + sorted_suggests + .into_iter() + .map(|(k, v)| (k, Box::new(PhpMixed::String(v)))) + .collect(), + ), + ); } if let Some(release_date) = package.get_release_date() { - data.insert("time".to_string(), PhpMixed::String(release_date.to_rfc3339())); + data.insert( + "time".to_string(), + PhpMixed::String(release_date.to_rfc3339()), + ); } if package.is_default_branch() { @@ -103,7 +176,15 @@ impl ArrayDumper { // dumpValues for base package keys (corresponds to dynamic PHP dispatch) let binaries = package.get_binaries(); if !binaries.is_empty() { - data.insert("bin".to_string(), PhpMixed::List(binaries.into_iter().map(|b| Box::new(PhpMixed::String(b))).collect())); + data.insert( + "bin".to_string(), + PhpMixed::List( + binaries + .into_iter() + .map(|b| Box::new(PhpMixed::String(b))) + .collect(), + ), + ); } if let Some(pkg_type) = package.get_type() { if !pkg_type.is_empty() { @@ -112,103 +193,223 @@ impl ArrayDumper { } let extra = package.get_extra(); if !extra.is_empty() { - data.insert("extra".to_string(), PhpMixed::Array(extra.into_iter().map(|(k, v)| (k, Box::new(v))).collect())); + data.insert( + "extra".to_string(), + PhpMixed::Array(extra.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), + ); } if let Some(installation_source) = package.get_installation_source() { - data.insert("installation-source".to_string(), PhpMixed::String(installation_source.to_string())); + data.insert( + "installation-source".to_string(), + PhpMixed::String(installation_source.to_string()), + ); } let autoload = package.get_autoload(); if !autoload.is_empty() { - data.insert("autoload".to_string(), PhpMixed::Array(autoload.into_iter().map(|(k, v)| (k, Box::new(v))).collect())); + data.insert( + "autoload".to_string(), + PhpMixed::Array( + autoload + .into_iter() + .map(|(k, v)| (k, Box::new(v))) + .collect(), + ), + ); } let dev_autoload = package.get_dev_autoload(); if !dev_autoload.is_empty() { - data.insert("autoload-dev".to_string(), PhpMixed::Array(dev_autoload.into_iter().map(|(k, v)| (k, Box::new(v))).collect())); + data.insert( + "autoload-dev".to_string(), + PhpMixed::Array( + dev_autoload + .into_iter() + .map(|(k, v)| (k, Box::new(v))) + .collect(), + ), + ); } if let Some(notification_url) = package.get_notification_url() { - data.insert("notification-url".to_string(), PhpMixed::String(notification_url.to_string())); + data.insert( + "notification-url".to_string(), + PhpMixed::String(notification_url.to_string()), + ); } let include_paths = package.get_include_paths(); if !include_paths.is_empty() { - data.insert("include-path".to_string(), PhpMixed::List(include_paths.into_iter().map(|p| Box::new(PhpMixed::String(p))).collect())); + data.insert( + "include-path".to_string(), + PhpMixed::List( + include_paths + .into_iter() + .map(|p| Box::new(PhpMixed::String(p))) + .collect(), + ), + ); } let php_ext = package.get_php_ext(); if !php_ext.is_empty() { - data.insert("php-ext".to_string(), PhpMixed::Array(php_ext.into_iter().map(|(k, v)| (k, Box::new(v))).collect())); + data.insert( + "php-ext".to_string(), + PhpMixed::Array(php_ext.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), + ); } - 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 let Some(archive_name) = complete_pkg.get_archive_name() { - let entry = data.entry("archive".to_string()).or_insert_with(|| PhpMixed::Array(IndexMap::new())); + let entry = data + .entry("archive".to_string()) + .or_insert_with(|| PhpMixed::Array(IndexMap::new())); if let PhpMixed::Array(ref mut archive) = entry { - archive.insert("name".to_string(), Box::new(PhpMixed::String(archive_name.to_string()))); + archive.insert( + "name".to_string(), + Box::new(PhpMixed::String(archive_name.to_string())), + ); } } let archive_excludes = complete_pkg.get_archive_excludes(); if !archive_excludes.is_empty() { - let entry = data.entry("archive".to_string()).or_insert_with(|| PhpMixed::Array(IndexMap::new())); + let entry = data + .entry("archive".to_string()) + .or_insert_with(|| PhpMixed::Array(IndexMap::new())); if let PhpMixed::Array(ref mut archive) = entry { - archive.insert("exclude".to_string(), Box::new(PhpMixed::List( - archive_excludes.into_iter().map(|e| Box::new(PhpMixed::String(e))).collect() - ))); + archive.insert( + "exclude".to_string(), + Box::new(PhpMixed::List( + archive_excludes + .into_iter() + .map(|e| Box::new(PhpMixed::String(e))) + .collect(), + )), + ); } } // dumpValues for complete package keys let scripts = complete_pkg.get_scripts(); if !scripts.is_empty() { - data.insert("scripts".to_string(), PhpMixed::Array( - scripts.into_iter().map(|(k, v)| (k, Box::new(PhpMixed::List(v.into_iter().map(|s| Box::new(PhpMixed::String(s))).collect())))).collect() - )); + data.insert( + "scripts".to_string(), + PhpMixed::Array( + scripts + .into_iter() + .map(|(k, v)| { + ( + k, + Box::new(PhpMixed::List( + v.into_iter() + .map(|s| Box::new(PhpMixed::String(s))) + .collect(), + )), + ) + }) + .collect(), + ), + ); } let license = complete_pkg.get_license(); if !license.is_empty() { - data.insert("license".to_string(), PhpMixed::List(license.into_iter().map(|l| Box::new(PhpMixed::String(l))).collect())); + data.insert( + "license".to_string(), + PhpMixed::List( + license + .into_iter() + .map(|l| Box::new(PhpMixed::String(l))) + .collect(), + ), + ); } let authors = complete_pkg.get_authors(); if !authors.is_empty() { - data.insert("authors".to_string(), PhpMixed::List( - authors.into_iter().map(|a| Box::new(PhpMixed::Array( - a.into_iter().map(|(k, v)| (k, Box::new(PhpMixed::String(v)))).collect() - ))).collect() - )); + data.insert( + "authors".to_string(), + PhpMixed::List( + authors + .into_iter() + .map(|a| { + Box::new(PhpMixed::Array( + a.into_iter() + .map(|(k, v)| (k, Box::new(PhpMixed::String(v)))) + .collect(), + )) + }) + .collect(), + ), + ); } if let Some(description) = complete_pkg.get_description() { - data.insert("description".to_string(), PhpMixed::String(description.to_string())); + data.insert( + "description".to_string(), + PhpMixed::String(description.to_string()), + ); } if let Some(homepage) = complete_pkg.get_homepage() { - data.insert("homepage".to_string(), PhpMixed::String(homepage.to_string())); + data.insert( + "homepage".to_string(), + PhpMixed::String(homepage.to_string()), + ); } let mut keywords = complete_pkg.get_keywords(); if !keywords.is_empty() { keywords.sort(); - data.insert("keywords".to_string(), PhpMixed::List(keywords.into_iter().map(|k| Box::new(PhpMixed::String(k))).collect())); + data.insert( + "keywords".to_string(), + PhpMixed::List( + keywords + .into_iter() + .map(|k| Box::new(PhpMixed::String(k))) + .collect(), + ), + ); } let repositories = complete_pkg.get_repositories(); if !repositories.is_empty() { - data.insert("repositories".to_string(), PhpMixed::List( - repositories.into_iter().map(|r| Box::new(PhpMixed::Array( - r.into_iter().map(|(k, v)| (k, Box::new(v))).collect() - ))).collect() - )); + data.insert( + "repositories".to_string(), + PhpMixed::List( + repositories + .into_iter() + .map(|r| { + Box::new(PhpMixed::Array( + r.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), + )) + }) + .collect(), + ), + ); } let support = complete_pkg.get_support(); if !support.is_empty() { - data.insert("support".to_string(), PhpMixed::Array( - support.into_iter().map(|(k, v)| (k, Box::new(PhpMixed::String(v)))).collect() - )); + data.insert( + "support".to_string(), + PhpMixed::Array( + support + .into_iter() + .map(|(k, v)| (k, Box::new(PhpMixed::String(v)))) + .collect(), + ), + ); } let funding = complete_pkg.get_funding(); if !funding.is_empty() { - data.insert("funding".to_string(), PhpMixed::List( - funding.into_iter().map(|f| Box::new(PhpMixed::Array( - f.into_iter().map(|(k, v)| (k, Box::new(v))).collect() - ))).collect() - )); + data.insert( + "funding".to_string(), + PhpMixed::List( + funding + .into_iter() + .map(|f| { + Box::new(PhpMixed::Array( + f.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), + )) + }) + .collect(), + ), + ); } if complete_pkg.is_abandoned() { - let abandoned_value = complete_pkg.get_replacement_package() + let abandoned_value = complete_pkg + .get_replacement_package() .map(|r| PhpMixed::String(r.to_string())) .unwrap_or(PhpMixed::Bool(true)); data.insert("abandoned".to_string(), abandoned_value); @@ -218,15 +419,24 @@ impl ArrayDumper { if let Some(root_pkg) = (package.as_any() as &dyn Any).downcast_ref::<RootPackage>() { let minimum_stability = root_pkg.get_minimum_stability(); if !minimum_stability.is_empty() { - data.insert("minimum-stability".to_string(), PhpMixed::String(minimum_stability.to_string())); + data.insert( + "minimum-stability".to_string(), + PhpMixed::String(minimum_stability.to_string()), + ); } } let transport_options = package.get_transport_options(); if !transport_options.is_empty() { - data.insert("transport-options".to_string(), PhpMixed::Array( - transport_options.into_iter().map(|(k, v)| (k, Box::new(v))).collect() - )); + data.insert( + "transport-options".to_string(), + PhpMixed::Array( + transport_options + .into_iter() + .map(|(k, v)| (k, Box::new(v))) + .collect(), + ), + ); } data diff --git a/crates/shirabe/src/package/dumper/mod.rs b/crates/shirabe/src/package/dumper/mod.rs new file mode 100644 index 0000000..b50c878 --- /dev/null +++ b/crates/shirabe/src/package/dumper/mod.rs @@ -0,0 +1 @@ +pub mod array_dumper; diff --git a/crates/shirabe/src/package/link.rs b/crates/shirabe/src/package/link.rs index 37f8124..2233eb1 100644 --- a/crates/shirabe/src/package/link.rs +++ b/crates/shirabe/src/package/link.rs @@ -87,7 +87,10 @@ impl Link { pub fn get_pretty_constraint(&self) -> anyhow::Result<&str> { match &self.pretty_constraint { None => Err(anyhow::anyhow!(UnexpectedValueException { - message: format!("Link {} has been misconfigured and had no prettyConstraint given.", self.to_string()), + message: format!( + "Link {} has been misconfigured and had no prettyConstraint given.", + self.to_string() + ), code: 0, })), Some(s) => Ok(s.as_str()), @@ -95,7 +98,10 @@ impl Link { } pub fn to_string(&self) -> String { - format!("{} {} {} ({})", self.source, self.description, self.target, self.constraint) + format!( + "{} {} {} ({})", + self.source, self.description, self.target, self.constraint + ) } pub fn get_pretty_string(&self, source_package: &dyn PackageInterface) -> String { diff --git a/crates/shirabe/src/package/loader/array_loader.rs b/crates/shirabe/src/package/loader/array_loader.rs index c0dc2a2..72d9754 100644 --- a/crates/shirabe/src/package/loader/array_loader.rs +++ b/crates/shirabe/src/package/loader/array_loader.rs @@ -5,9 +5,9 @@ use chrono::{DateTime, TimeZone, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - is_scalar, is_string, json_encode, ltrim, sprintf, stripos, strpos, strtolower, strval, substr, - trigger_error, trim, ucfirst, Exception, LogicException, PhpMixed, UnexpectedValueException, - E_USER_DEPRECATED, + E_USER_DEPRECATED, Exception, LogicException, PhpMixed, UnexpectedValueException, is_scalar, + is_string, json_encode, ltrim, sprintf, stripos, strpos, strtolower, strval, substr, + trigger_error, trim, ucfirst, }; use crate::package::base_package::{BasePackage, SUPPORTED_LINK_TYPES}; @@ -213,7 +213,9 @@ impl ArrayLoader { let _name = config.get("name").and_then(|v| v.as_string()).unwrap_or(""); let _pretty_version = config_version.as_string().unwrap_or("").to_string(); let _ = version; - todo!("phase-b: dynamic class-string instantiation new $class($name, $version, $prettyVersion)") + todo!( + "phase-b: dynamic class-string instantiation new $class($name, $version, $prettyVersion)" + ) } /// @param CompletePackage $package @@ -482,9 +484,7 @@ impl ArrayLoader { if let Some(description) = config.get("description") { if !shirabe_php_shim::empty(description) && is_string(description) { - package.set_description( - description.as_string().unwrap_or("").to_string(), - ); + package.set_description(description.as_string().unwrap_or("").to_string()); } } @@ -629,10 +629,9 @@ impl ArrayLoader { let mut links: IndexMap<String, Link> = IndexMap::new(); let entries: IndexMap<String, PhpMixed> = match entry { - PhpMixed::Array(m) => m - .iter() - .map(|(k, v)| (k.clone(), (**v).clone())) - .collect(), + PhpMixed::Array(m) => { + m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect() + } _ => continue, }; for (pretty_target, constraint) in entries { @@ -781,10 +780,7 @@ impl ArrayLoader { /// @param mixed[] $config the entire package config /// /// @return string|null normalized version of the branch alias or null if there is none - pub fn get_branch_alias( - &self, - config: &IndexMap<String, PhpMixed>, - ) -> Result<Option<String>> { + pub fn get_branch_alias(&self, config: &IndexMap<String, PhpMixed>) -> Result<Option<String>> { if !config.contains_key("version") || !is_scalar(config.get("version").unwrap()) { return Err(UnexpectedValueException { message: "no/invalid version defined".to_string(), @@ -824,7 +820,8 @@ impl ArrayLoader { } // normalize without -dev and ensure it's a numeric branch that is parseable - let validated_target_branch = if target_branch == VersionParser::DEFAULT_BRANCH_ALIAS + let validated_target_branch = if target_branch + == VersionParser::DEFAULT_BRANCH_ALIAS { VersionParser::DEFAULT_BRANCH_ALIAS.to_string() } else { @@ -841,9 +838,12 @@ impl ArrayLoader { } // If using numeric aliases ensure the alias is a valid subversion - let source_prefix = - self.version_parser.parse_numeric_alias_prefix(&source_branch); - let target_prefix = self.version_parser.parse_numeric_alias_prefix(&target_branch); + let source_prefix = self + .version_parser + .parse_numeric_alias_prefix(&source_branch); + let target_prefix = self + .version_parser + .parse_numeric_alias_prefix(&target_branch); if let (Some(sp), Some(tp)) = (source_prefix.as_ref(), target_prefix.as_ref()) { if stripos(tp, sp) != Some(0) { continue; diff --git a/crates/shirabe/src/package/loader/invalid_package_exception.rs b/crates/shirabe/src/package/loader/invalid_package_exception.rs index 88a7f5f..b0eeb23 100644 --- a/crates/shirabe/src/package/loader/invalid_package_exception.rs +++ b/crates/shirabe/src/package/loader/invalid_package_exception.rs @@ -14,7 +14,12 @@ impl InvalidPackageException { pub fn new(errors: Vec<String>, warnings: Vec<String>, data: Vec<PhpMixed>) -> Self { let message = format!( "Invalid package information: \n{}", - errors.iter().chain(warnings.iter()).cloned().collect::<Vec<_>>().join("\n") + errors + .iter() + .chain(warnings.iter()) + .cloned() + .collect::<Vec<_>>() + .join("\n") ); Self { inner: Exception { message, code: 0 }, diff --git a/crates/shirabe/src/package/loader/json_loader.rs b/crates/shirabe/src/package/loader/json_loader.rs index 5e5829a..8f10995 100644 --- a/crates/shirabe/src/package/loader/json_loader.rs +++ b/crates/shirabe/src/package/loader/json_loader.rs @@ -1,10 +1,10 @@ //! ref: composer/src/Composer/Package/Loader/JsonLoader.php -use std::path::Path; -use anyhow::Result; use crate::json::json_file::JsonFile; use crate::package::base_package::BasePackage; use crate::package::loader::loader_interface::LoaderInterface; +use anyhow::Result; +use std::path::Path; pub enum JsonLoaderInput { File(JsonFile), @@ -26,9 +26,7 @@ impl JsonLoader { JsonLoaderInput::String(ref s) if Path::new(s).exists() => { JsonFile::parse_json(&std::fs::read_to_string(s)?, Some(s))? } - JsonLoaderInput::String(ref s) => { - JsonFile::parse_json(s, None)? - } + JsonLoaderInput::String(ref s) => JsonFile::parse_json(s, None)?, }; self.loader.load(config, None) diff --git a/crates/shirabe/src/package/loader/loader_interface.rs b/crates/shirabe/src/package/loader/loader_interface.rs index 9f1e6da..530f1eb 100644 --- a/crates/shirabe/src/package/loader/loader_interface.rs +++ b/crates/shirabe/src/package/loader/loader_interface.rs @@ -1,9 +1,13 @@ //! ref: composer/src/Composer/Package/Loader/LoaderInterface.php +use crate::package::base_package::BasePackage; use indexmap::IndexMap; use shirabe_php_shim::PhpMixed; -use crate::package::base_package::BasePackage; pub trait LoaderInterface { - fn load(&self, config: IndexMap<String, PhpMixed>, class: Option<String>) -> anyhow::Result<Box<BasePackage>>; + fn load( + &self, + config: IndexMap<String, PhpMixed>, + class: Option<String>, + ) -> anyhow::Result<Box<BasePackage>>; } diff --git a/crates/shirabe/src/package/loader/mod.rs b/crates/shirabe/src/package/loader/mod.rs new file mode 100644 index 0000000..402b59c --- /dev/null +++ b/crates/shirabe/src/package/loader/mod.rs @@ -0,0 +1,6 @@ +pub mod array_loader; +pub mod invalid_package_exception; +pub mod json_loader; +pub mod loader_interface; +pub mod root_package_loader; +pub mod validating_array_loader; diff --git a/crates/shirabe/src/package/loader/root_package_loader.rs b/crates/shirabe/src/package/loader/root_package_loader.rs index eef7d70..59c9cf5 100644 --- a/crates/shirabe/src/package/loader/root_package_loader.rs +++ b/crates/shirabe/src/package/loader/root_package_loader.rs @@ -2,7 +2,9 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_php_shim::{strtolower, ucfirst, LogicException, RuntimeException, UnexpectedValueException}; +use shirabe_php_shim::{ + LogicException, RuntimeException, UnexpectedValueException, strtolower, ucfirst, +}; use crate::config::Config; use crate::io::io_interface::IOInterface; @@ -92,12 +94,16 @@ impl RootPackageLoader { Box::new(shirabe_php_shim::PhpMixed::String(version)), ); } else { - let cwd_str = cwd.map(|s| s.to_string()).unwrap_or_else(|| Platform::get_cwd(true)); + let cwd_str = cwd + .map(|s| s.to_string()) + .unwrap_or_else(|| Platform::get_cwd(true)); let version_data = self.version_guesser.guess_version(&config, &cwd_str); if let Some(data) = version_data { config.insert( "version".to_string(), - Box::new(shirabe_php_shim::PhpMixed::String(data.pretty_version.clone())), + Box::new(shirabe_php_shim::PhpMixed::String( + data.pretty_version.clone(), + )), ); config.insert( "version_normalized".to_string(), @@ -110,10 +116,7 @@ impl RootPackageLoader { if !config.contains_key("version") { if let Some(ref io) = self.io { let name = config["name"].as_string().unwrap_or(""); - let package_type = config - .get("type") - .and_then(|v| v.as_string()) - .unwrap_or(""); + let package_type = config.get("type").and_then(|v| v.as_string()).unwrap_or(""); if name != "__root__" && package_type != "project" { io.warning(&format!( "Composer could not detect the root package ({}) version, defaulting to '1.0.0'. See https://getcomposer.org/root-version", @@ -167,42 +170,43 @@ impl RootPackageLoader { } } - let package = self.inner.load(config.clone(), "Composer\\Package\\RootPackage")?; + let package = self + .inner + .load(config.clone(), "Composer\\Package\\RootPackage")?; - let real_package: &mut RootPackage = if let Some(alias_pkg) = - package.as_any_mut().downcast_mut::<RootAliasPackage>() - { - alias_pkg - .get_alias_of_mut() - .as_any_mut() - .downcast_mut::<RootPackage>() - .ok_or_else(|| { - anyhow::anyhow!(LogicException { - message: "Expecting a Composer\\Package\\RootPackage at this point" - .to_string(), - code: 0, - }) - })? - } else if let Some(root_pkg) = package.as_any_mut().downcast_mut::<RootPackage>() { - root_pkg - } else { - return Err(anyhow::anyhow!(LogicException { - message: "Expecting a Composer\\Package\\RootPackage at this point".to_string(), - code: 0, - })); - }; + let real_package: &mut RootPackage = + if let Some(alias_pkg) = package.as_any_mut().downcast_mut::<RootAliasPackage>() { + alias_pkg + .get_alias_of_mut() + .as_any_mut() + .downcast_mut::<RootPackage>() + .ok_or_else(|| { + anyhow::anyhow!(LogicException { + message: "Expecting a Composer\\Package\\RootPackage at this point" + .to_string(), + code: 0, + }) + })? + } else if let Some(root_pkg) = package.as_any_mut().downcast_mut::<RootPackage>() { + root_pkg + } else { + return Err(anyhow::anyhow!(LogicException { + message: "Expecting a Composer\\Package\\RootPackage at this point".to_string(), + code: 0, + })); + }; if auto_versioned { - real_package - .replace_version(real_package.get_version().to_string(), RootPackage::DEFAULT_PRETTY_VERSION.to_string()); + real_package.replace_version( + real_package.get_version().to_string(), + RootPackage::DEFAULT_PRETTY_VERSION.to_string(), + ); } - if let Some(min_stability) = config - .get("minimum-stability") - .and_then(|v| v.as_string()) - { - real_package - .set_minimum_stability(VersionParser::normalize_stability(min_stability).to_string()); + if let Some(min_stability) = config.get("minimum-stability").and_then(|v| v.as_string()) { + real_package.set_minimum_stability( + VersionParser::normalize_stability(min_stability).to_string(), + ); } let mut aliases: Vec<IndexMap<String, String>> = vec![]; @@ -362,16 +366,11 @@ impl RootPackageLoader { } let stability_names: Vec<&str> = stabilities.keys().copied().collect(); - let pattern = format!( - "^[^@]*?@({})$", - stability_names.join("|") - ); + let pattern = format!("^[^@]*?@({})$", stability_names.join("|")); let mut matched = false; for constraint in &constraints { - if let Some(Some(m)) = - Preg::is_match_strict_groups(&pattern, constraint).ok() - { + if let Some(Some(m)) = Preg::is_match_strict_groups(&pattern, constraint).ok() { let name = strtolower(req_name); let stability = stabilities[VersionParser::normalize_stability(&m[1])]; diff --git a/crates/shirabe/src/package/loader/validating_array_loader.rs b/crates/shirabe/src/package/loader/validating_array_loader.rs index 16806d7..c1be9f2 100644 --- a/crates/shirabe/src/package/loader/validating_array_loader.rs +++ b/crates/shirabe/src/package/loader/validating_array_loader.rs @@ -6,10 +6,10 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::composer::spdx_licenses::spdx_licenses::SpdxLicenses; use shirabe_php_shim::{ - array_intersect_key, array_values, filter_var, get_debug_type, is_array, is_bool, is_int, - is_numeric, is_scalar, is_string, json_encode, parse_url_all, php_to_string, sprintf, - str_replace, strcasecmp, strtolower, strtotime, substr, trigger_error, trim, var_export, - Exception, PhpMixed, E_USER_DEPRECATED, FILTER_VALIDATE_EMAIL, PHP_EOL, + E_USER_DEPRECATED, Exception, FILTER_VALIDATE_EMAIL, PHP_EOL, PhpMixed, array_intersect_key, + array_values, filter_var, get_debug_type, is_array, is_bool, is_int, is_numeric, is_scalar, + is_string, json_encode, parse_url_all, php_to_string, sprintf, str_replace, strcasecmp, + strtolower, strtotime, substr, trigger_error, trim, var_export, }; use shirabe_semver::constraint::constraint::Constraint; use shirabe_semver::constraint::match_none_constraint::MatchNoneConstraint; @@ -97,11 +97,8 @@ impl ValidatingArrayLoader { match self.version_parser.normalize(&version_str, None) { Ok(_) => {} Err(e) => { - self.errors.push(format!( - "version : invalid value ({}): {}", - version_str, - e - )); + self.errors + .push(format!("version : invalid value ({}): {}", version_str, e)); self.config.shift_remove("version"); } } @@ -173,10 +170,8 @@ impl ValidatingArrayLoader { release_date = Some(dt); } Err(e) => { - self.errors.push(format!( - "time : invalid value ({}): {}", - time_str, e - )); + self.errors + .push(format!("time : invalid value ({}): {}", time_str, e)); self.config.shift_remove("time"); } } @@ -209,9 +204,7 @@ impl ValidatingArrayLoader { // check for license validity on newly updated branches/tags let cutoff = strtotime("-8days").unwrap_or(0); - if release_date.is_none() - || release_date.unwrap().timestamp() >= cutoff - { + if release_date.is_none() || release_date.unwrap().timestamp() >= cutoff { let license_validator = SpdxLicenses::new(); for license in licenses.values() { let license_str = license.as_string().unwrap_or("").to_string(); @@ -219,10 +212,11 @@ impl ValidatingArrayLoader { if license_str == "proprietary" { continue; } - let license_to_validate = - str_replace("proprietary", "MIT", &license_str); + let license_to_validate = str_replace("proprietary", "MIT", &license_str); if !license_validator.validate(&license_to_validate) { - if license_validator.validate(&trim(&license_to_validate, " \t\n\r\0\u{0B}")) { + if license_validator + .validate(&trim(&license_to_validate, " \t\n\r\0\u{0B}")) + { self.warnings.push(sprintf( "License %s must not contain extra spaces, make sure to trim it.", &[PhpMixed::String( @@ -258,7 +252,9 @@ impl ValidatingArrayLoader { } else { self.warnings.push(sprintf( "License must be a string or array of strings, got %s.", - &[PhpMixed::String(json_encode(&*license_val).unwrap_or_default())], + &[PhpMixed::String( + json_encode(&*license_val).unwrap_or_default(), + )], )); self.config.shift_remove("license"); } @@ -277,20 +273,15 @@ impl ValidatingArrayLoader { key, get_debug_type(&*author) )); - if let Some(PhpMixed::Array(m)) = self - .config - .get_mut("authors") - .map(|v| v.as_mut()) + if let Some(PhpMixed::Array(m)) = + self.config.get_mut("authors").map(|v| v.as_mut()) { m.shift_remove(key); } continue; } for author_data in ["homepage", "email", "name", "role"] { - let val_opt = author - .as_array() - .and_then(|m| m.get(author_data)) - .cloned(); + let val_opt = author.as_array().and_then(|m| m.get(author_data)).cloned(); if let Some(val) = val_opt { if !is_string(&*val) { self.errors.push(format!( @@ -395,10 +386,8 @@ impl ValidatingArrayLoader { .cloned(); if let Some(val) = val_opt { if !is_string(&*val) { - self.errors.push(format!( - "support.{} : invalid value, must be a string", - key - )); + self.errors + .push(format!("support.{} : invalid value, must be a string", key)); if let Some(PhpMixed::Array(support)) = self.config.get_mut("support").map(|v| v.as_mut()) { @@ -450,7 +439,9 @@ impl ValidatingArrayLoader { } } - for key in ["issues", "forum", "wiki", "source", "docs", "chat", "security"] { + for key in [ + "issues", "forum", "wiki", "source", "docs", "chat", "security", + ] { let url_opt = self .config .get("support") @@ -647,8 +638,11 @@ impl ValidatingArrayLoader { )); php_ext.shift_remove("download-url-method"); } else { - let valid_download_url_methods = - ["composer-default", "pre-packaged-source", "pre-packaged-binary"]; + let valid_download_url_methods = [ + "composer-default", + "pre-packaged-source", + "pre-packaged-binary", + ]; let defined_download_url_methods: IndexMap<String, Box<PhpMixed>> = if is_array(&*v) { v.as_array().unwrap().clone() @@ -673,9 +667,9 @@ impl ValidatingArrayLoader { get_debug_type(&**download_url_method) )); php_ext.shift_remove("download-url-method"); - } else if !valid_download_url_methods.contains( - &download_url_method.as_string().unwrap_or(""), - ) { + } else if !valid_download_url_methods + .contains(&download_url_method.as_string().unwrap_or("")) + { self.errors.push(format!( "php-ext.download-url-method.{} : invalid value ({}), must be one of {}", key, @@ -718,15 +712,10 @@ impl ValidatingArrayLoader { )); php_ext.shift_remove(field_name); } else { - let field_keys: Vec<String> = field_val - .as_array() - .unwrap() - .keys() - .cloned() - .collect(); + let field_keys: Vec<String> = + field_val.as_array().unwrap().keys().cloned().collect(); for key in &field_keys { - let os_family = - field_val.as_array().unwrap()[key].clone(); + let os_family = field_val.as_array().unwrap()[key].clone(); if !is_string(&*os_family) { self.errors.push(format!( "php-ext.{}.{} : should be a string, {} given", @@ -785,8 +774,7 @@ impl ValidatingArrayLoader { .cloned() .collect(); for key in &configure_keys { - let option = - configure_options.as_array().unwrap()[key].clone(); + let option = configure_options.as_array().unwrap()[key].clone(); if !is_array(&*option) { self.errors.push(format!( "php-ext.configure-options.{} : should be an array, {} given", @@ -837,9 +825,8 @@ impl ValidatingArrayLoader { key, get_debug_type(&*needs_value) )); - if let Some(PhpMixed::Array(co)) = php_ext - .get_mut("configure-options") - .map(|v| v.as_mut()) + if let Some(PhpMixed::Array(co)) = + php_ext.get_mut("configure-options").map(|v| v.as_mut()) { if let Some(entry) = co.get_mut(key) { if let PhpMixed::Array(em) = entry.as_mut() { @@ -850,18 +837,15 @@ impl ValidatingArrayLoader { } } - if let Some(description) = - option_map.get("description").cloned() - { + if let Some(description) = option_map.get("description").cloned() { if !is_string(&*description) { self.errors.push(format!( "php-ext.configure-options.{}.description : should be a string, {} given", key, get_debug_type(&*description) )); - if let Some(PhpMixed::Array(co)) = php_ext - .get_mut("configure-options") - .map(|v| v.as_mut()) + if let Some(PhpMixed::Array(co)) = + php_ext.get_mut("configure-options").map(|v| v.as_mut()) { if let Some(entry) = co.get_mut(key) { if let PhpMixed::Array(em) = entry.as_mut() { @@ -886,10 +870,8 @@ impl ValidatingArrayLoader { // If php-ext is now empty, unset it if !php_ext.is_empty() { - self.config.insert( - "php-ext".to_string(), - Box::new(PhpMixed::Array(php_ext)), - ); + self.config + .insert("php-ext".to_string(), Box::new(PhpMixed::Array(php_ext))); } } } @@ -938,8 +920,7 @@ impl ValidatingArrayLoader { arr.shift_remove(&package); } } else if constraint.as_string().unwrap_or("") != "self.version" { - let constraint_str = - constraint.as_string().unwrap_or("").to_string(); + let constraint_str = constraint.as_string().unwrap_or("").to_string(); let link_constraint = match self.version_parser.parse_constraints(&constraint_str) { Ok(c) => c, @@ -972,9 +953,7 @@ impl ValidatingArrayLoader { && link_constraint .as_any() .downcast_ref::<Constraint>() - .map_or(false, |c| { - ["==", "="].contains(&c.get_operator()) - }) + .map_or(false, |c| ["==", "="].contains(&c.get_operator())) && Constraint::new(">=", "1.0.0.0-dev") .matches(link_constraint.as_ref()) { @@ -1063,7 +1042,13 @@ impl ValidatingArrayLoader { } if self.validate_array("autoload", false) && self.config.contains_key("autoload") { - let types = ["psr-0", "psr-4", "classmap", "files", "exclude-from-classmap"]; + let types = [ + "psr-0", + "psr-4", + "classmap", + "files", + "exclude-from-classmap", + ]; let autoload_keys: Vec<String> = self.config["autoload"] .as_array() .map(|m| m.keys().cloned().collect()) @@ -1086,9 +1071,7 @@ impl ValidatingArrayLoader { if let Some(type_map) = type_config.as_array() { for (namespace, _dirs) in type_map { let ns_str = namespace.as_str(); - if ns_str != "" - && substr(ns_str, -1, None) != "\\" - { + if ns_str != "" && substr(ns_str, -1, None) != "\\" { self.errors.push(format!( "autoload.psr-4 : invalid value ({}), namespaces must end with a namespace separator, should be {}\\\\", ns_str, ns_str @@ -1112,8 +1095,7 @@ impl ValidatingArrayLoader { ); // Unset the psr-4 setting, since unsetting target-dir might // interfere with other settings. - if let Some(PhpMixed::Array(arr)) = - self.config.get_mut("autoload").map(|v| v.as_mut()) + if let Some(PhpMixed::Array(arr)) = self.config.get_mut("autoload").map(|v| v.as_mut()) { arr.shift_remove("psr-4"); } @@ -1205,8 +1187,9 @@ impl ValidatingArrayLoader { if has_branch_alias { let branch_alias_val = self.config["extra"].as_array().unwrap()["branch-alias"].clone(); if !is_array(&*branch_alias_val) { - self.errors - .push("extra.branch-alias : must be an array of versions => aliases".to_string()); + self.errors.push( + "extra.branch-alias : must be an array of versions => aliases".to_string(), + ); } else { let branch_alias_map = branch_alias_val.as_array().cloned().unwrap_or_default(); for (source_branch, target_branch) in &branch_alias_map { @@ -1255,9 +1238,7 @@ impl ValidatingArrayLoader { 0, Some((target_branch_str.len() as i64) - 4), ); - let validated_target_branch = self - .version_parser - .normalize_branch(&trimmed); + let validated_target_branch = self.version_parser.normalize_branch(&trimmed); if substr(&validated_target_branch, -4, None) != "-dev" { self.warnings.push(format!( "extra.branch-alias.{} : the target branch ({}) must be a parseable number like 2.0-dev", @@ -1311,15 +1292,13 @@ impl ValidatingArrayLoader { ))); } - let package = self - .loader - .load( - self.config - .iter() - .map(|(k, v)| (k.clone(), (**v).clone())) - .collect(), - Some(class.to_string()), - )?; + let package = self.loader.load( + self.config + .iter() + .map(|(k, v)| (k.clone(), (**v).clone())) + .collect(), + Some(class.to_string()), + )?; self.config = IndexMap::new(); Ok(package) @@ -1344,7 +1323,10 @@ impl ValidatingArrayLoader { ) .unwrap_or(false) { - return Some(format!("{} is invalid, it should have a vendor name, a forward slash, and a package name. The vendor and package name can be words separated by -, . or _. The complete name should match \"^[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9](([_.]?|-{{0,2}})[a-z0-9]+)*$\".", name)); + return Some(format!( + "{} is invalid, it should have a vendor name, a forward slash, and a package name. The vendor and package name can be words separated by -, . or _. The complete name should match \"^[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9](([_.]?|-{{0,2}})[a-z0-9]+)*$\".", + name + )); } let reserved_names = [ @@ -1401,7 +1383,10 @@ impl ValidatingArrayLoader { let value = self.config[property].as_string().unwrap_or("").to_string(); if !Preg::is_match(&format!("{{^{}$}}u", regex), &value).unwrap_or(false) { - let message = format!("{} : invalid value ({}), must match {}", property, value, regex); + let message = format!( + "{} : invalid value ({}), must match {}", + property, value, regex + ); if mandatory { self.errors.push(message); } else { @@ -1428,11 +1413,13 @@ impl ValidatingArrayLoader { } let is_empty = !self.config.contains_key(property) - || trim(self.config[property].as_string().unwrap_or(""), " \t\n\r\0\u{0B}") == ""; + || trim( + self.config[property].as_string().unwrap_or(""), + " \t\n\r\0\u{0B}", + ) == ""; if is_empty { if mandatory { - self.errors - .push(format!("{} : must be present", property)); + self.errors.push(format!("{} : must be present", property)); } self.config.shift_remove(property); diff --git a/crates/shirabe/src/package/locker.rs b/crates/shirabe/src/package/locker.rs index a0401ba..b813bbb 100644 --- a/crates/shirabe/src/package/locker.rs +++ b/crates/shirabe/src/package/locker.rs @@ -6,10 +6,10 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::seld::json_lint::parsing_exception::ParsingException; use shirabe_php_shim::{ - array_intersect, array_keys, array_map, array_merge, call_user_func, file_get_contents, - filemtime, function_exists, hash, in_array, is_array, is_int, ksort, realpath, reset_first, - sprintf, strcmp, strtolower, touch, trim, usort, LogicException, PhpMixed, RuntimeException, - DATE_RFC3339, + DATE_RFC3339, LogicException, PhpMixed, RuntimeException, array_intersect, array_keys, + array_map, array_merge, call_user_func, file_get_contents, filemtime, function_exists, hash, + in_array, is_array, is_int, ksort, realpath, reset_first, sprintf, strcmp, strtolower, touch, + trim, usort, }; use crate::installer::installation_manager::InstallationManager; @@ -104,19 +104,18 @@ impl Locker { let mut relevant_content: IndexMap<String, PhpMixed> = IndexMap::new(); let content_keys: Vec<String> = array_keys(&content); - let relevant_keys_strings: Vec<String> = relevant_keys.iter().map(|s| s.to_string()).collect(); + let relevant_keys_strings: Vec<String> = + relevant_keys.iter().map(|s| s.to_string()).collect(); let intersected = array_intersect(&relevant_keys_strings, &content_keys); for key in intersected { if let Some(value) = content.get(&key) { relevant_content.insert(key, value.clone()); } } - let platform_value = content - .get("config") - .and_then(|v| match v { - PhpMixed::Array(m) => m.get("platform").cloned(), - _ => None, - }); + let platform_value = content.get("config").and_then(|v| match v { + PhpMixed::Array(m) => m.get("platform").cloned(), + _ => None, + }); if let Some(platform) = platform_value { let mut config_map: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); config_map.insert("platform".to_string(), platform); @@ -174,10 +173,7 @@ impl Locker { } /// Searches and returns an array of locked packages, retrieved from registered repositories. - pub fn get_locked_repository( - &mut self, - with_dev_reqs: bool, - ) -> Result<LockArrayRepository> { + pub fn get_locked_repository(&mut self, with_dev_reqs: bool) -> Result<LockArrayRepository> { let lock_data = self.get_lock_data()?; let mut packages = LockArrayRepository::new(vec![])?; @@ -221,8 +217,7 @@ impl Locker { m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect(); let package = self.loader.load(info_map, None)?; packages.add_package(package.clone())?; - package_by_name - .insert(package.get_name().to_string(), package.clone()); + package_by_name.insert(package.get_name().to_string(), package.clone()); // TODO(phase-b): `$package instanceof AliasPackage` downcast let package_as_alias: Option<&AliasPackage> = None; @@ -245,8 +240,7 @@ impl Locker { .and_then(|v| v.as_string()) .unwrap_or("") .to_string(); - if let Some(base_pkg) = package_by_name.get(&alias_pkg_name).cloned() - { + if let Some(base_pkg) = package_by_name.get(&alias_pkg_name).cloned() { let mut alias_pkg = CompleteAliasPackage::new( todo!("phase-b: downcast Box<BasePackage> to CompletePackage"), m.get("alias_normalized") @@ -272,8 +266,9 @@ impl Locker { } Err(RuntimeException { - message: "Your composer.lock is invalid. Run \"composer update\" to generate a new one." - .to_string(), + message: + "Your composer.lock is invalid. Run \"composer update\" to generate a new one." + .to_string(), code: 0, } .into()) @@ -308,7 +303,9 @@ impl Locker { "1.0.0", Link::TYPE_REQUIRE, match platform_value.unwrap() { - PhpMixed::Array(m) => m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect(), + PhpMixed::Array(m) => { + m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect() + } _ => IndexMap::new(), }, )?; @@ -324,7 +321,9 @@ impl Locker { "1.0.0", Link::TYPE_REQUIRE, match platform_dev_value.unwrap() { - PhpMixed::Array(m) => m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect(), + PhpMixed::Array(m) => { + m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect() + } _ => IndexMap::new(), }, )?; @@ -514,10 +513,7 @@ impl Locker { "content-hash".to_string(), PhpMixed::String(self.content_hash.clone()), ); - lock.insert( - "packages".to_string(), - self.lock_packages(&packages)?, - ); + lock.insert("packages".to_string(), self.lock_packages(&packages)?); lock.insert("packages-dev".to_string(), PhpMixed::Null); lock.insert( "aliases".to_string(), @@ -526,7 +522,9 @@ impl Locker { .iter() .map(|m| { Box::new(PhpMixed::Array( - m.iter().map(|(k, v)| (k.clone(), Box::new(v.clone()))).collect(), + m.iter() + .map(|(k, v)| (k.clone(), Box::new(v.clone()))) + .collect(), )) }) .collect(), @@ -610,11 +608,7 @@ impl Locker { if !is_locked || Some(&lock) != current_data.as_ref() { if write { self.lock_file.write( - PhpMixed::Array( - lock.into_iter() - .map(|(k, v)| (k, Box::new(v))) - .collect(), - ), + PhpMixed::Array(lock.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), None, )?; self.lock_data_cache = None; @@ -623,11 +617,7 @@ impl Locker { self.virtual_file_written = true; self.lock_data_cache = Some(JsonFile::parse_json( &JsonFile::encode( - &PhpMixed::Array( - lock.into_iter() - .map(|(k, v)| (k, Box::new(v))) - .collect(), - ), + &PhpMixed::Array(lock.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), shirabe_php_shim::JSON_UNESCAPED_SLASHES | shirabe_php_shim::JSON_PRETTY_PRINT | shirabe_php_shim::JSON_UNESCAPED_UNICODE, @@ -886,7 +876,12 @@ impl Locker { r"{^\s*(\d+)\s*}", output.as_string().unwrap_or(""), ) { - let ts = m.get(1).cloned().unwrap_or_default().parse::<i64>().unwrap_or(0); + let ts = m + .get(1) + .cloned() + .unwrap_or_default() + .parse::<i64>() + .unwrap_or(0); datetime = chrono::DateTime::from_timestamp(ts, 0); } } @@ -941,10 +936,8 @@ impl Locker { ) .is_empty() { - let results = installed_repo.find_packages_with_replacers_and_providers( - &link.get_target(), - None, - ); + let results = installed_repo + .find_packages_with_replacers_and_providers(&link.get_target(), None); if !results.is_empty() { let provider = reset_first(&results).unwrap(); diff --git a/crates/shirabe/src/package/mod.rs b/crates/shirabe/src/package/mod.rs new file mode 100644 index 0000000..77a01a2 --- /dev/null +++ b/crates/shirabe/src/package/mod.rs @@ -0,0 +1,17 @@ +pub mod alias_package; +pub mod archiver; +pub mod base_package; +pub mod comparer; +pub mod complete_alias_package; +pub mod complete_package; +pub mod complete_package_interface; +pub mod dumper; +pub mod link; +pub mod loader; +pub mod locker; +pub mod package; +pub mod package_interface; +pub mod root_alias_package; +pub mod root_package; +pub mod root_package_interface; +pub mod version; diff --git a/crates/shirabe/src/package/package.rs b/crates/shirabe/src/package/package.rs index fac6d46..81d6131 100644 --- a/crates/shirabe/src/package/package.rs +++ b/crates/shirabe/src/package/package.rs @@ -4,7 +4,7 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::composer::util::composer_mirror::ComposerMirror; -use shirabe_php_shim::{strpos, trigger_error, PhpMixed, E_USER_DEPRECATED}; +use shirabe_php_shim::{E_USER_DEPRECATED, PhpMixed, strpos, trigger_error}; use crate::package::base_package::BasePackage; use crate::package::link::Link; @@ -109,7 +109,10 @@ impl Package { } pub fn get_type(&self) -> String { - self.r#type.clone().filter(|s| !s.is_empty()).unwrap_or_else(|| "library".to_string()) + self.r#type + .clone() + .filter(|s| !s.is_empty()) + .unwrap_or_else(|| "library".to_string()) } pub fn get_stability(&self) -> &str { diff --git a/crates/shirabe/src/package/root_package.rs b/crates/shirabe/src/package/root_package.rs index f1c3ba8..55a6805 100644 --- a/crates/shirabe/src/package/root_package.rs +++ b/crates/shirabe/src/package/root_package.rs @@ -1,9 +1,9 @@ //! ref: composer/src/Composer/Package/RootPackage.php -use indexmap::IndexMap; -use shirabe_php_shim::PhpMixed; use crate::package::complete_package::CompletePackage; use crate::package::root_package_interface::RootPackageInterface; +use indexmap::IndexMap; +use shirabe_php_shim::PhpMixed; #[derive(Debug)] pub struct RootPackage { diff --git a/crates/shirabe/src/package/version/mod.rs b/crates/shirabe/src/package/version/mod.rs new file mode 100644 index 0000000..a734e23 --- /dev/null +++ b/crates/shirabe/src/package/version/mod.rs @@ -0,0 +1,5 @@ +pub mod stability_filter; +pub mod version_bumper; +pub mod version_guesser; +pub mod version_parser; +pub mod version_selector; diff --git a/crates/shirabe/src/package/version/stability_filter.rs b/crates/shirabe/src/package/version/stability_filter.rs index 65f024f..d08492c 100644 --- a/crates/shirabe/src/package/version/stability_filter.rs +++ b/crates/shirabe/src/package/version/stability_filter.rs @@ -1,7 +1,7 @@ //! ref: composer/src/Composer/Package/Version/StabilityFilter.php -use indexmap::IndexMap; use crate::package::base_package::BasePackage; +use indexmap::IndexMap; pub struct StabilityFilter; diff --git a/crates/shirabe/src/package/version/version_bumper.rs b/crates/shirabe/src/package/version/version_bumper.rs index 8717d19..5911458 100644 --- a/crates/shirabe/src/package/version/version_bumper.rs +++ b/crates/shirabe/src/package/version/version_bumper.rs @@ -1,15 +1,15 @@ //! ref: composer/src/Composer/Package/Version/VersionBumper.php -use anyhow::Result; -use indexmap::IndexMap; -use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_semver::constraint::constraint_interface::ConstraintInterface; -use shirabe_semver::intervals::Intervals; use crate::package::dumper::array_dumper::ArrayDumper; use crate::package::loader::array_loader::ArrayLoader; use crate::package::package_interface::PackageInterface; use crate::package::version::version_parser::VersionParser; use crate::util::platform::Platform; +use anyhow::Result; +use indexmap::IndexMap; +use shirabe_external_packages::composer::pcre::preg::Preg; +use shirabe_semver::constraint::constraint_interface::ConstraintInterface; +use shirabe_semver::intervals::Intervals; #[derive(Debug)] pub struct VersionBumper; @@ -46,7 +46,8 @@ impl VersionBumper { } let major = Preg::replace(r"{^([1-9][0-9]*|0\.\d+).*}", "$1", version.clone())?; - let version_without_suffix = Preg::replace(r"{(?:\.(?:0|9999999))+(-dev)?$}", "", version.clone())?; + let version_without_suffix = + Preg::replace(r"{(?:\.(?:0|9999999))+(-dev)?$}", "", version.clone())?; let new_pretty_constraint = format!("^{}", version_without_suffix); if !Preg::is_match(r"{^\^\d+(\.\d+)*$}", &new_pretty_constraint)? { @@ -82,23 +83,27 @@ impl VersionBumper { } else { "" }; - let replacement = if match_str.starts_with('~') && match_str.matches('.').count() != 1 { - let mut version_bits: Vec<String> = - version_without_suffix.split('.').map(String::from).collect(); - let needed_len = match_str.matches('.').count() + 1; - while version_bits.len() < needed_len { - version_bits.push("0".to_string()); - } - let dots_in_match = match_str.matches('.').count(); - format!("~{}", version_bits[..dots_in_match + 1].join(".")) - } else if match_str == "*" || match_str.starts_with(">=") { - format!(">={}{}", version_without_suffix, suffix) - } else { - format!("{}{}", new_pretty_constraint, suffix) - }; + let replacement = + if match_str.starts_with('~') && match_str.matches('.').count() != 1 { + let mut version_bits: Vec<String> = version_without_suffix + .split('.') + .map(String::from) + .collect(); + let needed_len = match_str.matches('.').count() + 1; + while version_bits.len() < needed_len { + version_bits.push("0".to_string()); + } + let dots_in_match = match_str.matches('.').count(); + format!("~{}", version_bits[..dots_in_match + 1].join(".")) + } else if match_str == "*" || match_str.starts_with(">=") { + format!(">={}{}", version_without_suffix, suffix) + } else { + format!("{}{}", new_pretty_constraint, suffix) + }; let offset = match_offset as usize; let length = Platform::strlen(match_str) as usize; - modified = shirabe_php_shim::substr_replace(&modified, &replacement, offset, length); + modified = + shirabe_php_shim::substr_replace(&modified, &replacement, offset, length); } let new_constraint = parser.parse_constraints(&modified)?; diff --git a/crates/shirabe/src/package/version/version_guesser.rs b/crates/shirabe/src/package/version/version_guesser.rs index a97095d..c4e25e0 100644 --- a/crates/shirabe/src/package/version/version_guesser.rs +++ b/crates/shirabe/src/package/version/version_guesser.rs @@ -5,9 +5,9 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::symfony::component::process::process::Process; use shirabe_php_shim::{ - array_keys, array_map, array_merge, empty, function_exists, implode, is_string, json_encode, - preg_quote, str_replace, strlen, strnatcasecmp, strpos, substr, trim, usort, PhpMixed, - RuntimeException, PHP_INT_MAX, + PHP_INT_MAX, PhpMixed, RuntimeException, array_keys, array_map, array_merge, empty, + function_exists, implode, is_string, json_encode, preg_quote, str_replace, strlen, + strnatcasecmp, strpos, substr, trim, usort, }; use shirabe_semver::version_parser::VersionParser as SemverVersionParser; @@ -144,7 +144,12 @@ impl VersionGuesser { .map(|fv| !fv.is_empty()) .unwrap_or(false); if feature_non_empty - && "-dev" == substr(version_data.feature_version.as_deref().unwrap_or(""), -4, None) + && "-dev" + == substr( + version_data.feature_version.as_deref().unwrap_or(""), + -4, + None, + ) && Preg::is_match( r"{\.9{7}}", version_data.feature_version.as_deref().unwrap_or(""), @@ -296,7 +301,11 @@ impl VersionGuesser { &GitUtil::parse_rev_list_output(&command_output, &self.process), None, ); - commit = if parsed.is_empty() { None } else { Some(parsed) }; + commit = if parsed.is_empty() { + None + } else { + Some(parsed) + }; } } @@ -386,10 +395,8 @@ impl VersionGuesser { // TODO(phase-b): clone ProcessExecutor todo!("self.process.clone()"), ); - let branches: Vec<String> = array_map( - |k: &String| k.clone(), - &array_keys(driver.get_branches()), - ); + let branches: Vec<String> = + array_map(|k: &String| k.clone(), &array_keys(driver.get_branches())); // try to find the best (nearest) version branch to assume this feature's version let mut result = self.guess_feature_version( @@ -597,11 +604,7 @@ impl VersionGuesser { // try to fetch current version from fossil tags let mut output = String::new(); if 0 == self.process.execute( - &[ - "fossil".to_string(), - "tag".to_string(), - "list".to_string(), - ], + &["fossil".to_string(), "tag".to_string(), "list".to_string()], &mut output, Some(path.to_string()), ) { @@ -637,11 +640,7 @@ impl VersionGuesser { // try to fetch current version from svn let mut output = String::new(); if 0 == self.process.execute( - &[ - "svn".to_string(), - "info".to_string(), - "--xml".to_string(), - ], + &["svn".to_string(), "info".to_string(), "--xml".to_string()], &mut output, Some(path.to_string()), ) { @@ -670,7 +669,8 @@ impl VersionGuesser { let m1 = matches.get(1).cloned().unwrap_or_default(); let m2 = matches.get(2).cloned(); let m3 = matches.get(3).cloned(); - if m2.is_some() && m3.is_some() + if m2.is_some() + && m3.is_some() && (branches_path == *m2.as_ref().unwrap() || tags_path == *m2.as_ref().unwrap()) { diff --git a/crates/shirabe/src/package/version/version_parser.rs b/crates/shirabe/src/package/version/version_parser.rs index dbaa1c9..6bb1004 100644 --- a/crates/shirabe/src/package/version/version_parser.rs +++ b/crates/shirabe/src/package/version/version_parser.rs @@ -21,7 +21,10 @@ pub struct VersionParser { impl VersionParser { pub const DEFAULT_BRANCH_ALIAS: &'static str = "9999999-dev"; - pub fn parse_constraints(&self, constraints: &str) -> anyhow::Result<Arc<dyn ConstraintInterface + Send + Sync>> { + pub fn parse_constraints( + &self, + constraints: &str, + ) -> anyhow::Result<Arc<dyn ConstraintInterface + Send + Sync>> { let mut cache = CONSTRAINTS.lock().unwrap(); if !cache.contains_key(constraints) { let parsed = self.inner.parse_constraints(constraints)?; @@ -30,13 +33,20 @@ impl VersionParser { Ok(Arc::clone(cache.get(constraints).unwrap())) } - pub fn parse_name_version_pairs(&self, pairs: Vec<String>) -> anyhow::Result<Vec<IndexMap<String, String>>> { + pub fn parse_name_version_pairs( + &self, + pairs: Vec<String>, + ) -> anyhow::Result<Vec<IndexMap<String, String>>> { let pairs: Vec<String> = pairs; let mut result: Vec<IndexMap<String, String>> = Vec::new(); let count = pairs.len(); let mut i = 0_usize; while i < count { - let mut pair = Preg::replace(r"{^([^=: ]+)[=: ](.*)$}", "$1 $2", pairs[i].trim().to_string())?; + let mut pair = Preg::replace( + r"{^([^=: ]+)[=: ](.*)$}", + "$1 $2", + pairs[i].trim().to_string(), + )?; if !pair.contains(' ') && i + 1 < count && !pairs[i + 1].contains('/') diff --git a/crates/shirabe/src/package/version/version_selector.rs b/crates/shirabe/src/package/version/version_selector.rs index 85ce71d..f665320 100644 --- a/crates/shirabe/src/package/version/version_selector.rs +++ b/crates/shirabe/src/package/version/version_selector.rs @@ -5,7 +5,7 @@ use std::any::Any; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - strtolower, version_compare, PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION, + PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION, strtolower, version_compare, }; use shirabe_semver::constraint::constraint::Constraint; use shirabe_semver::constraint::constraint_interface::ConstraintInterface; @@ -20,9 +20,9 @@ use crate::package::base_package::BasePackage; use crate::package::dumper::array_dumper::ArrayDumper; use crate::package::loader::array_loader::ArrayLoader; use crate::package::package_interface::PackageInterface; +use crate::package::version::version_parser::VersionParser; use crate::repository::platform_repository::PlatformRepository; use crate::repository::repository_set::RepositorySet; -use crate::package::version::version_parser::VersionParser; #[derive(Debug)] pub struct VersionSelector { @@ -140,7 +140,8 @@ impl VersionSelector { if link.get_constraint().matches(provided_constraint.as_ref()) { continue 'reqs; } - let list_filter_opt = (platform_requirement_filter.as_ref() as &dyn Any) + let list_filter_opt = (platform_requirement_filter.as_ref() + as &dyn Any) .downcast_ref::<IgnoreListPlatformRequirementFilter>(); if let Some(list_filter) = list_filter_opt { if list_filter.is_upper_bound_ignored(name) { @@ -168,8 +169,7 @@ impl VersionSelector { _ => true, }; if should_warn { - let warn_key = - format!("{}/{}", pkg.get_name(), link.get_target()); + let warn_key = format!("{}/{}", pkg.get_name(), link.get_target()); let is_first_warning = !already_warned_names.contains_key(&warn_key); already_warned_names.insert(warn_key, true); let latest = if is_latest_version { @@ -222,15 +222,16 @@ impl VersionSelector { Some(p) => p, }; - let package = if let Some(alias) = (package.as_ref() as &dyn Any).downcast_ref::<AliasPackage>() { - if alias.get_version() == VersionParser::DEFAULT_BRANCH_ALIAS { - alias.get_alias_of() + let package = + if let Some(alias) = (package.as_ref() as &dyn Any).downcast_ref::<AliasPackage>() { + if alias.get_version() == VersionParser::DEFAULT_BRANCH_ALIAS { + alias.get_alias_of() + } else { + package + } } else { package - } - } else { - package - }; + }; Ok(Some(package)) } @@ -288,7 +289,10 @@ impl VersionSelector { if semantic_version_parts.len() == 4 && Preg::is_match(r"{^\d+\D?}", semantic_version_parts[3]).unwrap_or(false) { - let mut parts: Vec<String> = semantic_version_parts.iter().map(|s| s.to_string()).collect(); + let mut parts: Vec<String> = semantic_version_parts + .iter() + .map(|s| s.to_string()) + .collect(); let version = if parts[0] == "0" { parts.truncate(3); parts.join(".") diff --git a/crates/shirabe/src/phpstan/mod.rs b/crates/shirabe/src/phpstan/mod.rs new file mode 100644 index 0000000..5c48ac7 --- /dev/null +++ b/crates/shirabe/src/phpstan/mod.rs @@ -0,0 +1,2 @@ +pub mod config_return_type_extension; +pub mod rule_reason_data_return_type_extension; diff --git a/crates/shirabe/src/platform/hhvm_detector.rs b/crates/shirabe/src/platform/hhvm_detector.rs index 634b724..c6a53cb 100644 --- a/crates/shirabe/src/platform/hhvm_detector.rs +++ b/crates/shirabe/src/platform/hhvm_detector.rs @@ -1,10 +1,10 @@ //! ref: composer/src/Composer/Platform/HhvmDetector.php -use std::sync::Mutex; -use shirabe_external_packages::symfony::process::executable_finder::ExecutableFinder; -use shirabe_php_shim::{defined, HHVM_VERSION}; use crate::util::platform::Platform; use crate::util::process_executor::ProcessExecutor; +use shirabe_external_packages::symfony::process::executable_finder::ExecutableFinder; +use shirabe_php_shim::{HHVM_VERSION, defined}; +use std::sync::Mutex; // None = null (uninitialized), Some(None) = false (not found), Some(Some(v)) = version static HHVM_VERSION_CACHE: Mutex<Option<Option<String>>> = Mutex::new(None); @@ -15,7 +15,10 @@ pub struct HhvmDetector { } impl HhvmDetector { - pub fn new(executable_finder: Option<ExecutableFinder>, process_executor: Option<ProcessExecutor>) -> Self { + pub fn new( + executable_finder: Option<ExecutableFinder>, + process_executor: Option<ProcessExecutor>, + ) -> Self { Self { executable_finder, process_executor, @@ -41,13 +44,24 @@ impl HhvmDetector { if cache.as_ref().unwrap().is_none() && !Platform::is_windows() { *cache = Some(None); - let finder = self.executable_finder.get_or_insert_with(ExecutableFinder::new); + let finder = self + .executable_finder + .get_or_insert_with(ExecutableFinder::new); let hhvm_path = finder.find("hhvm"); if let Some(hhvm_path) = hhvm_path { - let executor = self.process_executor.get_or_insert_with(ProcessExecutor::new); + let executor = self + .process_executor + .get_or_insert_with(ProcessExecutor::new); let mut version_output = String::new(); let exit_code = executor.execute( - &[&hhvm_path, "--php", "-d", "hhvm.jit=0", "-r", "echo HHVM_VERSION;"], + &[ + &hhvm_path, + "--php", + "-d", + "hhvm.jit=0", + "-r", + "echo HHVM_VERSION;", + ], &mut version_output, ); if exit_code == 0 { diff --git a/crates/shirabe/src/platform/mod.rs b/crates/shirabe/src/platform/mod.rs new file mode 100644 index 0000000..35916e5 --- /dev/null +++ b/crates/shirabe/src/platform/mod.rs @@ -0,0 +1,3 @@ +pub mod hhvm_detector; +pub mod runtime; +pub mod version; diff --git a/crates/shirabe/src/platform/runtime.rs b/crates/shirabe/src/platform/runtime.rs index 4c9c3f5..2e04453 100644 --- a/crates/shirabe/src/platform/runtime.rs +++ b/crates/shirabe/src/platform/runtime.rs @@ -19,7 +19,11 @@ impl Runtime { todo!() } - pub fn invoke(&self, callable: Box<dyn Fn(Vec<PhpMixed>) -> PhpMixed>, arguments: Vec<PhpMixed>) -> PhpMixed { + pub fn invoke( + &self, + callable: Box<dyn Fn(Vec<PhpMixed>) -> PhpMixed>, + arguments: Vec<PhpMixed>, + ) -> PhpMixed { todo!() } diff --git a/crates/shirabe/src/platform/version.rs b/crates/shirabe/src/platform/version.rs index 5c64098..a9566ee 100644 --- a/crates/shirabe/src/platform/version.rs +++ b/crates/shirabe/src/platform/version.rs @@ -15,7 +15,10 @@ impl Version { )?; let patch = if version_compare(&matches["version"], "3.0.0", "<") { - format!(".{}", Self::convert_alpha_version_to_int_version(&matches["patch"])) + format!( + ".{}", + Self::convert_alpha_version_to_int_version(&matches["patch"]) + ) } else { String::new() }; @@ -25,25 +28,33 @@ impl Version { .replace("-fips", "") .replace("-pre", "-alpha"); - Some(format!("{}{}{}", matches["version"], patch, suffix).trim_end_matches('-').to_string()) + Some( + format!("{}{}{}", matches["version"], patch, suffix) + .trim_end_matches('-') + .to_string(), + ) } pub fn parse_libjpeg(libjpeg_version: &str) -> Option<String> { - let matches = Preg::match_strict_groups( - r"^(?P<major>\d+)(?P<minor>[a-z]*)$", - libjpeg_version, - )?; + let matches = + Preg::match_strict_groups(r"^(?P<major>\d+)(?P<minor>[a-z]*)$", libjpeg_version)?; - Some(format!("{}.{}", matches["major"], Self::convert_alpha_version_to_int_version(&matches["minor"]))) + Some(format!( + "{}.{}", + matches["major"], + Self::convert_alpha_version_to_int_version(&matches["minor"]) + )) } pub fn parse_zoneinfo_version(zoneinfo_version: &str) -> Option<String> { - let matches = Preg::match_strict_groups( - r"^(?P<year>\d{4})(?P<revision>[a-z]*)$", - zoneinfo_version, - )?; + let matches = + Preg::match_strict_groups(r"^(?P<year>\d{4})(?P<revision>[a-z]*)$", zoneinfo_version)?; - Some(format!("{}.{}", matches["year"], Self::convert_alpha_version_to_int_version(&matches["revision"]))) + Some(format!( + "{}.{}", + matches["year"], + Self::convert_alpha_version_to_int_version(&matches["revision"]) + )) } fn convert_alpha_version_to_int_version(alpha: &str) -> i64 { diff --git a/crates/shirabe/src/plugin/capability/mod.rs b/crates/shirabe/src/plugin/capability/mod.rs new file mode 100644 index 0000000..c4a6f8b --- /dev/null +++ b/crates/shirabe/src/plugin/capability/mod.rs @@ -0,0 +1,2 @@ +pub mod capability; +pub mod command_provider; diff --git a/crates/shirabe/src/plugin/command_event.rs b/crates/shirabe/src/plugin/command_event.rs index 0eb952a..aa5b366 100644 --- a/crates/shirabe/src/plugin/command_event.rs +++ b/crates/shirabe/src/plugin/command_event.rs @@ -1,8 +1,8 @@ //! ref: composer/src/Composer/Plugin/CommandEvent.php +use crate::event_dispatcher::event::Event; use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; -use crate::event_dispatcher::event::Event; use shirabe_php_shim::PhpMixed; #[derive(Debug)] @@ -23,7 +23,12 @@ impl CommandEvent { flags: Vec<PhpMixed>, ) -> Self { let inner = Event::new(name, args, flags); - Self { inner, command_name, input, output } + Self { + inner, + command_name, + input, + output, + } } pub fn get_input(&self) -> &dyn InputInterface { diff --git a/crates/shirabe/src/plugin/mod.rs b/crates/shirabe/src/plugin/mod.rs new file mode 100644 index 0000000..7ff98f3 --- /dev/null +++ b/crates/shirabe/src/plugin/mod.rs @@ -0,0 +1,11 @@ +pub mod capability; +pub mod capable; +pub mod command_event; +pub mod plugin_blocked_exception; +pub mod plugin_events; +pub mod plugin_interface; +pub mod plugin_manager; +pub mod post_file_download_event; +pub mod pre_command_run_event; +pub mod pre_file_download_event; +pub mod pre_pool_create_event; diff --git a/crates/shirabe/src/plugin/plugin_manager.rs b/crates/shirabe/src/plugin/plugin_manager.rs index ee1aff5..430b0a4 100644 --- a/crates/shirabe/src/plugin/plugin_manager.rs +++ b/crates/shirabe/src/plugin/plugin_manager.rs @@ -8,10 +8,10 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - array_key_exists, array_reverse, array_search, clone, get_class, implode, in_array, is_a, - is_array, is_string, ksort, preg_quote, str_replace, strrpos, strtr, substr, trigger_error, - trim, var_export, version_compare, E_USER_DEPRECATED, PhpMixed, RuntimeException, - UnexpectedValueException, + E_USER_DEPRECATED, PhpMixed, RuntimeException, UnexpectedValueException, array_key_exists, + array_reverse, array_search, clone, get_class, implode, in_array, is_a, is_array, is_string, + ksort, preg_quote, str_replace, strrpos, strtr, substr, trigger_error, trim, var_export, + version_compare, }; use shirabe_semver::constraint::constraint::Constraint; @@ -108,12 +108,20 @@ impl PluginManager { pub fn load_installed_plugins(&mut self) -> anyhow::Result<()> { // TODO(plugin): plugin loading is part of the plugin API if !self.are_plugins_disabled("local") { - let repo = self.composer.get_repository_manager().get_local_repository(); + let repo = self + .composer + .get_repository_manager() + .get_local_repository(); self.load_repository(&*repo, false, Some(self.composer.get_package()))?; } if self.global_composer.is_some() && !self.are_plugins_disabled("global") { - let repo = self.global_composer.as_ref().unwrap().get_repository_manager().get_local_repository(); + let repo = self + .global_composer + .as_ref() + .unwrap() + .get_repository_manager() + .get_local_repository(); self.load_repository(&*repo, true, None)?; } Ok(()) @@ -123,12 +131,20 @@ impl PluginManager { pub fn deactivate_installed_plugins(&mut self) { // TODO(plugin): deactivation is part of the plugin API if !self.are_plugins_disabled("local") { - let repo = self.composer.get_repository_manager().get_local_repository(); + let repo = self + .composer + .get_repository_manager() + .get_local_repository(); self.deactivate_repository(&*repo, false); } if self.global_composer.is_some() && !self.are_plugins_disabled("global") { - let repo = self.global_composer.as_ref().unwrap().get_repository_manager().get_local_repository(); + let repo = self + .global_composer + .as_ref() + .unwrap() + .get_repository_manager() + .get_local_repository(); self.deactivate_repository(&*repo, true); } } @@ -151,16 +167,26 @@ impl PluginManager { } /// Register a plugin package, activate it etc. - pub fn register_package(&mut self, package: &dyn PackageInterface, fail_on_missing_classes: bool, is_global_plugin: bool) -> anyhow::Result<()> { + pub fn register_package( + &mut self, + package: &dyn PackageInterface, + fail_on_missing_classes: bool, + is_global_plugin: bool, + ) -> anyhow::Result<()> { // TODO(plugin): registerPackage drives the actual plugin loading via eval() if self.are_plugins_disabled(if is_global_plugin { "global" } else { "local" }) { - self.io.write_error(&format!("<warning>The \"{}\" plugin was not loaded as plugins are disabled.</warning>", package.get_name())); + self.io.write_error(&format!( + "<warning>The \"{}\" plugin was not loaded as plugins are disabled.</warning>", + package.get_name() + )); return Ok(()); } if package.get_type() == "composer-plugin" { - let mut requires_composer: Option<Box<dyn shirabe_semver::constraint::constraint_interface::ConstraintInterface>> = None; + let mut requires_composer: Option< + Box<dyn shirabe_semver::constraint::constraint_interface::ConstraintInterface>, + > = None; for (_k, link) in &package.get_requires() { if "composer-plugin-api" == link.get_target() { requires_composer = Some(link.get_constraint()); @@ -179,7 +205,11 @@ impl PluginManager { }; let current_plugin_api_version = self.get_plugin_api_version(); - let current_plugin_api_constraint = Constraint::new("==", self.version_parser.normalize(¤t_plugin_api_version, None)?); + let current_plugin_api_constraint = Constraint::new( + "==", + self.version_parser + .normalize(¤t_plugin_api_version, None)?, + ); if requires_composer.get_pretty_string() == self.get_plugin_api_version() { self.io.write_error(&format!("<warning>The \"{}\" plugin requires composer-plugin-api {}, this *WILL* break in the future and it should be fixed ASAP (require ^{} instead for example).</warning>", package.get_name(), self.get_plugin_api_version(), self.get_plugin_api_version())); @@ -205,11 +235,20 @@ impl PluginManager { } } - let plugin_optional = package.get_extra().get("plugin-optional").map(|v| v.as_bool() == Some(true)).unwrap_or(false); + let plugin_optional = package + .get_extra() + .get("plugin-optional") + .map(|v| v.as_bool() == Some(true)) + .unwrap_or(false); if !self.is_plugin_allowed(package.get_name(), is_global_plugin, plugin_optional, true)? { - self.io.write_error(&format!("Skipped loading \"{}\" {}as it is not in config.allow-plugins", + self.io.write_error(&format!( + "Skipped loading \"{}\" {}as it is not in config.allow-plugins", package.get_name(), - if is_global_plugin || self.running_in_global_dir { "(installed globally) " } else { "" } + if is_global_plugin || self.running_in_global_dir { + "(installed globally) " + } else { + "" + } )); return Ok(()); } @@ -232,9 +271,16 @@ impl PluginManager { }.into()); } let _classes: Vec<String> = if let Some(arr) = class_value.and_then(|v| v.as_list()) { - arr.iter().filter_map(|v| v.as_string().map(|s| s.to_string())).collect() + arr.iter() + .filter_map(|v| v.as_string().map(|s| s.to_string())) + .collect() } else { - vec![class_value.and_then(|v| v.as_string()).unwrap_or("").to_string()] + vec![ + class_value + .and_then(|v| v.as_string()) + .unwrap_or("") + .to_string(), + ] }; // TODO(plugin): everything below this point in the original PHP would create runtime instances: @@ -255,11 +301,16 @@ impl PluginManager { return; } - let plugins = self.registered_plugins.shift_remove(package.get_name()).unwrap_or_default(); + let plugins = self + .registered_plugins + .shift_remove(package.get_name()) + .unwrap_or_default(); for plugin in plugins { match plugin { PluginOrInstaller::Installer(inst) => { - self.composer.get_installation_manager().remove_installer(&*inst); + self.composer + .get_installation_manager() + .remove_installer(&*inst); } PluginOrInstaller::Plugin(p) => { self.remove_plugin(&*p); @@ -275,11 +326,16 @@ impl PluginManager { return; } - let plugins = self.registered_plugins.shift_remove(package.get_name()).unwrap_or_default(); + let plugins = self + .registered_plugins + .shift_remove(package.get_name()) + .unwrap_or_default(); for plugin in plugins { match plugin { PluginOrInstaller::Installer(inst) => { - self.composer.get_installation_manager().remove_installer(&*inst); + self.composer + .get_installation_manager() + .remove_installer(&*inst); } PluginOrInstaller::Plugin(p) => { self.remove_plugin(&*p); @@ -295,22 +351,39 @@ impl PluginManager { } /// Adds a plugin, activates it and registers it with the event dispatcher - pub fn add_plugin(&mut self, plugin: Box<dyn PluginInterface>, is_global_plugin: bool, source_package: Option<&dyn PackageInterface>) -> anyhow::Result<()> { + pub fn add_plugin( + &mut self, + plugin: Box<dyn PluginInterface>, + is_global_plugin: bool, + source_package: Option<&dyn PackageInterface>, + ) -> anyhow::Result<()> { // TODO(plugin): plugin activation if self.are_plugins_disabled(if is_global_plugin { "global" } else { "local" }) { return Ok(()); } if source_package.is_none() { - trigger_error("Calling PluginManager::addPlugin without $sourcePackage is deprecated, if you are using this please get in touch with us to explain the use case", E_USER_DEPRECATED); + trigger_error( + "Calling PluginManager::addPlugin without $sourcePackage is deprecated, if you are using this please get in touch with us to explain the use case", + E_USER_DEPRECATED, + ); } else { let sp = source_package.unwrap(); - let plugin_optional = sp.get_extra().get("plugin-optional").map(|v| v.as_bool() == Some(true)).unwrap_or(false); + let plugin_optional = sp + .get_extra() + .get("plugin-optional") + .map(|v| v.as_bool() == Some(true)) + .unwrap_or(false); if !self.is_plugin_allowed(sp.get_name(), is_global_plugin, plugin_optional, true)? { - self.io.write_error(&format!("Skipped loading \"{} from {}\" {} as it is not in config.allow-plugins", + self.io.write_error(&format!( + "Skipped loading \"{} from {}\" {} as it is not in config.allow-plugins", get_class(&*plugin), sp.get_name(), - if is_global_plugin || self.running_in_global_dir { "(installed globally) " } else { "" } + if is_global_plugin || self.running_in_global_dir { + "(installed globally) " + } else { + "" + } )); return Ok(()); } @@ -323,9 +396,14 @@ impl PluginManager { if is_global_plugin || self.running_in_global_dir { details.push("installed globally".to_string()); } - self.io.write_error(&format!("Loading plugin {}{}", + self.io.write_error(&format!( + "Loading plugin {}{}", get_class(&*plugin), - if !details.is_empty() { format!(" ({})", implode(", ", &details)) } else { String::new() } + if !details.is_empty() { + format!(" ({})", implode(", ", &details)) + } else { + String::new() + } )); plugin.activate(&self.composer, &*self.io); @@ -347,7 +425,8 @@ impl PluginManager { None => return, }; - self.io.write_error(&format!("Unloading plugin {}", get_class(plugin))); + self.io + .write_error(&format!("Unloading plugin {}", get_class(plugin))); self.plugins.remove(index as usize); plugin.deactivate(&self.composer, &*self.io); @@ -357,11 +436,17 @@ impl PluginManager { /// Notifies a plugin it is being uninstalled and should clean up pub fn uninstall_plugin(&self, plugin: &dyn PluginInterface) { // TODO(plugin): plugin uninstall hook - self.io.write_error(&format!("Uninstalling plugin {}", get_class(plugin))); + self.io + .write_error(&format!("Uninstalling plugin {}", get_class(plugin))); plugin.uninstall(&self.composer, &*self.io); } - fn load_repository(&mut self, repo: &dyn RepositoryInterface, is_global_repo: bool, root_package: Option<&dyn RootPackageInterface>) -> anyhow::Result<()> { + fn load_repository( + &mut self, + repo: &dyn RepositoryInterface, + is_global_repo: bool, + root_package: Option<&dyn RootPackageInterface>, + ) -> anyhow::Result<()> { // TODO(plugin): repository scan for plugin packages let packages = repo.get_packages(); @@ -370,19 +455,27 @@ impl PluginManager { if package.get_type() == "composer-plugin" { let extra = package.get_extra(); if package.get_name() == "composer/installers" - || extra.get("plugin-modifies-install-path").map(|v| v.as_bool() == Some(true)).unwrap_or(false) + || extra + .get("plugin-modifies-install-path") + .map(|v| v.as_bool() == Some(true)) + .unwrap_or(false) { weights.insert(package.get_name().to_string(), -10000); } } } - let sorted_packages = PackageSorter::sort_packages(packages.iter().map(|p| p.clone_box()).collect(), weights); + let sorted_packages = + PackageSorter::sort_packages(packages.iter().map(|p| p.clone_box()).collect(), weights); let required_packages: Vec<Box<dyn PackageInterface>> = if !is_global_repo { - RepositoryUtils::filter_required_packages(packages.iter().map(|p| p.as_ref()).collect(), root_package.unwrap(), true) - .iter() - .map(|p| p.clone_box()) - .collect() + RepositoryUtils::filter_required_packages( + packages.iter().map(|p| p.as_ref()).collect(), + root_package.unwrap(), + true, + ) + .iter() + .map(|p| p.clone_box()) + .collect() } else { vec![] }; @@ -393,12 +486,23 @@ impl PluginManager { None => continue, }; - if !in_array(package.get_type(), &vec!["composer-plugin".to_string(), "composer-installer".to_string()], true) { + if !in_array( + package.get_type(), + &vec![ + "composer-plugin".to_string(), + "composer-installer".to_string(), + ], + true, + ) { continue; } if !is_global_repo - && !in_array(&**package as &dyn PackageInterface, &required_packages.iter().map(|p| &**p).collect::<Vec<_>>(), true) + && !in_array( + &**package as &dyn PackageInterface, + &required_packages.iter().map(|p| &**p).collect::<Vec<_>>(), + true, + ) && !self.is_plugin_allowed(package.get_name(), false, true, false)? { self.io.write_error(&format!("<warning>The \"{}\" plugin was not loaded as it is not listed in allow-plugins and is not required by the root package anymore.</warning>", package.get_name())); @@ -419,7 +523,10 @@ impl PluginManager { fn deactivate_repository(&mut self, repo: &dyn RepositoryInterface, _is_global_repo: bool) { // TODO(plugin): deactivate plugins from a repository let packages = repo.get_packages(); - let sorted_packages = array_reverse(PackageSorter::sort_packages(packages.iter().map(|p| p.clone_box()).collect(), IndexMap::new())); + let sorted_packages = array_reverse(PackageSorter::sort_packages( + packages.iter().map(|p| p.clone_box()).collect(), + IndexMap::new(), + )); for package in &sorted_packages { if package.as_complete_package().is_none() { @@ -434,13 +541,24 @@ impl PluginManager { } } - fn collect_dependencies(&self, installed_repo: &InstalledRepository, mut collected: IndexMap<String, Box<dyn PackageInterface>>, package: &dyn PackageInterface) -> IndexMap<String, Box<dyn PackageInterface>> { + fn collect_dependencies( + &self, + installed_repo: &InstalledRepository, + mut collected: IndexMap<String, Box<dyn PackageInterface>>, + package: &dyn PackageInterface, + ) -> IndexMap<String, Box<dyn PackageInterface>> { // TODO(plugin): used by registerPackage to assemble plugin dependency autoload map for (_k, require_link) in &package.get_requires() { - for required_package in installed_repo.find_packages_with_replacers_and_providers(require_link.get_target()) { + for required_package in + installed_repo.find_packages_with_replacers_and_providers(require_link.get_target()) + { if !collected.contains_key(required_package.get_name()) { - collected.insert(required_package.get_name().to_string(), required_package.clone_box()); - collected = self.collect_dependencies(installed_repo, collected, &*required_package); + collected.insert( + required_package.get_name().to_string(), + required_package.clone_box(), + ); + collected = + self.collect_dependencies(installed_repo, collected, &*required_package); } } } @@ -451,14 +569,25 @@ impl PluginManager { /// Retrieves the path a package is installed to. fn get_install_path(&self, package: &dyn PackageInterface, global: bool) -> Option<String> { if !global { - return self.composer.get_installation_manager().get_install_path(package); + return self + .composer + .get_installation_manager() + .get_install_path(package); } // PHP: assert(null !== $this->globalComposer); - self.global_composer.as_ref().unwrap().get_installation_manager().get_install_path(package) + self.global_composer + .as_ref() + .unwrap() + .get_installation_manager() + .get_install_path(package) } - pub(crate) fn get_capability_implementation_class_name(&self, plugin: &dyn PluginInterface, capability: &str) -> anyhow::Result<Option<String>> { + pub(crate) fn get_capability_implementation_class_name( + &self, + plugin: &dyn PluginInterface, + capability: &str, + ) -> anyhow::Result<Option<String>> { // TODO(plugin): capability lookup let capable = match plugin.as_capable() { Some(c) => c, @@ -476,37 +605,61 @@ impl PluginManager { } if array_key_exists(capability, &capabilities) - && (capabilities.get(capability).map(|v| v.is_empty()).unwrap_or(true) + && (capabilities + .get(capability) + .map(|v| v.is_empty()) + .unwrap_or(true) || !is_string(capabilities.get(capability).unwrap()) - || trim(capabilities.get(capability).and_then(|v| v.as_string()).unwrap_or(""), " \t\n\r\0\u{0B}").is_empty()) + || trim( + capabilities + .get(capability) + .and_then(|v| v.as_string()) + .unwrap_or(""), + " \t\n\r\0\u{0B}", + ) + .is_empty()) { return Err(UnexpectedValueException { - message: format!("Plugin {} provided invalid capability class name(s), got {}", + message: format!( + "Plugin {} provided invalid capability class name(s), got {}", get_class(plugin), var_export(capabilities.get(capability).unwrap(), true) ), code: 0, - }.into()); + } + .into()); } Ok(None) } - pub fn get_plugin_capability(&self, plugin: &dyn PluginInterface, capability_class_name: &str, _ctor_args: IndexMap<String, PhpMixed>) -> anyhow::Result<Option<Box<dyn Capability>>> { + pub fn get_plugin_capability( + &self, + plugin: &dyn PluginInterface, + capability_class_name: &str, + _ctor_args: IndexMap<String, PhpMixed>, + ) -> anyhow::Result<Option<Box<dyn Capability>>> { // TODO(plugin): instantiate plugin capability via runtime class lookup - let _capability_class = match self.get_capability_implementation_class_name(plugin, capability_class_name)? { - Some(c) => c, - None => return Ok(None), - }; + let _capability_class = + match self.get_capability_implementation_class_name(plugin, capability_class_name)? { + Some(c) => c, + None => return Ok(None), + }; // PHP: requires class_exists / new $capabilityClass($ctorArgs); cannot be performed in Rust without a runtime registry. Ok(None) } - pub fn get_plugin_capabilities(&self, capability_class_name: &str, ctor_args: IndexMap<String, PhpMixed>) -> Vec<Box<dyn Capability>> { + pub fn get_plugin_capabilities( + &self, + capability_class_name: &str, + ctor_args: IndexMap<String, PhpMixed>, + ) -> Vec<Box<dyn Capability>> { // TODO(plugin): aggregate capabilities across all loaded plugins let mut capabilities: Vec<Box<dyn Capability>> = vec![]; for plugin in &self.get_plugins() { - if let Ok(Some(capability)) = self.get_plugin_capability(&**plugin, capability_class_name, ctor_args.clone()) { + if let Ok(Some(capability)) = + self.get_plugin_capability(&**plugin, capability_class_name, ctor_args.clone()) + { capabilities.push(capability); } } @@ -514,7 +667,10 @@ impl PluginManager { capabilities } - fn parse_allowed_plugins(allow_plugins_config: PhpMixed, locker: Option<&Locker>) -> Option<IndexMap<String, bool>> { + fn parse_allowed_plugins( + allow_plugins_config: PhpMixed, + locker: Option<&Locker>, + ) -> Option<IndexMap<String, bool>> { // PHP: [] === $allowPluginsConfig && $locker !== null && $locker->isLocked() && version_compare($locker->getPluginApi(), '2.2.0', '<') let is_empty_array = allow_plugins_config .as_array() @@ -543,7 +699,10 @@ impl PluginManager { let mut rules: IndexMap<String, bool> = IndexMap::new(); if let Some(arr) = allow_plugins_config.as_array() { for (pattern, allow) in arr { - rules.insert(BasePackage::package_name_to_regexp(pattern), allow.as_bool().unwrap_or(false)); + rules.insert( + BasePackage::package_name_to_regexp(pattern), + allow.as_bool().unwrap_or(false), + ); } } @@ -563,7 +722,13 @@ impl PluginManager { self.disable_plugins = DisablePlugins::True; } - pub fn is_plugin_allowed(&mut self, package: &str, is_global_plugin: bool, optional: bool, prompt: bool) -> anyhow::Result<bool> { + pub fn is_plugin_allowed( + &mut self, + package: &str, + is_global_plugin: bool, + optional: bool, + prompt: bool, + ) -> anyhow::Result<bool> { // TODO(plugin): allow-plugins authorization flow with interactive prompt let rules: &mut Option<IndexMap<String, bool>> = if is_global_plugin { &mut self.allow_global_plugin_rules @@ -590,7 +755,12 @@ impl PluginManager { *rules = Some(IndexMap::new()); } - let rules_snapshot: Vec<(String, bool)> = rules.as_ref().unwrap().iter().map(|(k, v)| (k.clone(), *v)).collect(); + let rules_snapshot: Vec<(String, bool)> = rules + .as_ref() + .unwrap() + .iter() + .map(|(k, v)| (k.clone(), *v)) + .collect(); for (pattern, allow) in &rules_snapshot { if Preg::is_match(pattern, package, None).unwrap_or(false) { return Ok(*allow); @@ -634,21 +804,40 @@ impl PluginManager { let allow = answer_str == "y"; // persist answer in current rules to avoid prompting again if the package gets reloaded - rules.as_mut().unwrap().insert(BasePackage::package_name_to_regexp(package), allow); + rules + .as_mut() + .unwrap() + .insert(BasePackage::package_name_to_regexp(package), allow); // persist answer in composer.json if it wasn't simply discarded if answer_str == "y" || answer_str == "n" { - let allow_plugins_value = composer_ref.get_config().get("allow-plugins").clone(); + let allow_plugins_value = + composer_ref.get_config().get("allow-plugins").clone(); if let Some(arr) = allow_plugins_value.as_array() { let mut allow_plugins = arr.clone(); - allow_plugins.insert(package.to_string(), Box::new(PhpMixed::Bool(allow))); - if composer_ref.get_config().get("sort-packages").as_bool().unwrap_or(false) { + allow_plugins + .insert(package.to_string(), Box::new(PhpMixed::Bool(allow))); + if composer_ref + .get_config() + .get("sort-packages") + .as_bool() + .unwrap_or(false) + { ksort(&mut allow_plugins); } - composer_ref.get_config().get_config_source().add_config_setting("allow-plugins", PhpMixed::Array(allow_plugins.clone())); + composer_ref + .get_config() + .get_config_source() + .add_config_setting( + "allow-plugins", + PhpMixed::Array(allow_plugins.clone()), + ); let mut wrap: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); let mut inner: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); - inner.insert("allow-plugins".to_string(), Box::new(PhpMixed::Array(allow_plugins))); + inner.insert( + "allow-plugins".to_string(), + Box::new(PhpMixed::Array(allow_plugins)), + ); wrap.insert("config".to_string(), Box::new(PhpMixed::Array(inner))); composer_ref.get_config().merge(PhpMixed::Array(wrap), ""); } diff --git a/crates/shirabe/src/plugin/pre_command_run_event.rs b/crates/shirabe/src/plugin/pre_command_run_event.rs index 9b8de26..40672c0 100644 --- a/crates/shirabe/src/plugin/pre_command_run_event.rs +++ b/crates/shirabe/src/plugin/pre_command_run_event.rs @@ -1,8 +1,8 @@ //! ref: composer/src/Composer/Plugin/PreCommandRunEvent.php // TODO(plugin): this event is part of the plugin API and is dispatched before a command runs -use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; use crate::event_dispatcher::event::Event; +use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; pub struct PreCommandRunEvent { inner: Event, diff --git a/crates/shirabe/src/plugin/pre_pool_create_event.rs b/crates/shirabe/src/plugin/pre_pool_create_event.rs index 3126119..7363b08 100644 --- a/crates/shirabe/src/plugin/pre_pool_create_event.rs +++ b/crates/shirabe/src/plugin/pre_pool_create_event.rs @@ -62,7 +62,9 @@ impl PrePoolCreateEvent { &self.stability_flags } - pub fn get_root_aliases(&self) -> &IndexMap<String, IndexMap<String, IndexMap<String, String>>> { + pub fn get_root_aliases( + &self, + ) -> &IndexMap<String, IndexMap<String, IndexMap<String, String>>> { &self.root_aliases } diff --git a/crates/shirabe/src/question/mod.rs b/crates/shirabe/src/question/mod.rs new file mode 100644 index 0000000..fe17dc8 --- /dev/null +++ b/crates/shirabe/src/question/mod.rs @@ -0,0 +1 @@ +pub mod strict_confirmation_question; diff --git a/crates/shirabe/src/question/strict_confirmation_question.rs b/crates/shirabe/src/question/strict_confirmation_question.rs index cfa9c36..eea6629 100644 --- a/crates/shirabe/src/question/strict_confirmation_question.rs +++ b/crates/shirabe/src/question/strict_confirmation_question.rs @@ -4,7 +4,7 @@ use anyhow::Result; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::symfony::console::exception::invalid_argument_exception::InvalidArgumentException; use shirabe_external_packages::symfony::console::question::question::Question; -use shirabe_php_shim::{empty, is_bool, PhpMixed}; +use shirabe_php_shim::{PhpMixed, empty, is_bool}; pub struct StrictConfirmationQuestion { inner: Question, @@ -62,7 +62,8 @@ impl StrictConfirmationQuestion { return Err(InvalidArgumentException { message: "Please answer yes, y, no, or n.".to_string(), code: 0, - }.into()); + } + .into()); } Ok(answer.clone()) }) diff --git a/crates/shirabe/src/repository/advisory_provider_interface.rs b/crates/shirabe/src/repository/advisory_provider_interface.rs index d0aaceb..f9ddeb2 100644 --- a/crates/shirabe/src/repository/advisory_provider_interface.rs +++ b/crates/shirabe/src/repository/advisory_provider_interface.rs @@ -1,9 +1,9 @@ //! ref: composer/src/Composer/Repository/AdvisoryProviderInterface.php -use indexmap::IndexMap; -use shirabe_semver::constraint::constraint_interface::ConstraintInterface; use crate::advisory::partial_security_advisory::PartialSecurityAdvisory; use crate::advisory::security_advisory::SecurityAdvisory; +use indexmap::IndexMap; +use shirabe_semver::constraint::constraint_interface::ConstraintInterface; #[derive(Debug)] pub enum PartialOrSecurityAdvisory { @@ -20,5 +20,9 @@ pub struct SecurityAdvisoryResult { pub trait AdvisoryProviderInterface { fn has_security_advisories(&self) -> bool; - fn get_security_advisories(&self, package_constraint_map: IndexMap<String, Box<dyn ConstraintInterface>>, allow_partial_advisories: bool) -> anyhow::Result<SecurityAdvisoryResult>; + fn get_security_advisories( + &self, + package_constraint_map: IndexMap<String, Box<dyn ConstraintInterface>>, + allow_partial_advisories: bool, + ) -> anyhow::Result<SecurityAdvisoryResult>; } diff --git a/crates/shirabe/src/repository/array_repository.rs b/crates/shirabe/src/repository/array_repository.rs index 5be2a8b..7bb7e81 100644 --- a/crates/shirabe/src/repository/array_repository.rs +++ b/crates/shirabe/src/repository/array_repository.rs @@ -7,8 +7,8 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - implode, preg_quote, spl_object_hash, strtolower, Countable, InvalidArgumentException, - LogicException, + Countable, InvalidArgumentException, LogicException, implode, preg_quote, spl_object_hash, + strtolower, }; use shirabe_semver::constraint::constraint::Constraint; use shirabe_semver::constraint::constraint_interface::ConstraintInterface; @@ -53,7 +53,10 @@ impl ArrayRepository { /// Adds a new package to the repository pub fn add_package(&self, package: Box<dyn PackageInterface>) -> Result<()> { // PHP: if (!$package instanceof BasePackage) throw new \InvalidArgumentException(...) - 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(InvalidArgumentException { message: "Only subclasses of BasePackage are supported".to_string(), code: 0, @@ -61,7 +64,8 @@ impl ArrayRepository { .into()); } // TODO(phase-b): convert Box<dyn PackageInterface> to Box<BasePackage> - let mut package: Box<BasePackage> = todo!("downcast Box<dyn PackageInterface> to Box<BasePackage>"); + let mut package: Box<BasePackage> = + todo!("downcast Box<dyn PackageInterface> to Box<BasePackage>"); if self.packages.borrow().is_none() { self.initialize(); @@ -101,7 +105,10 @@ impl ArrayRepository { package = alias_pkg.get_alias_of().clone_box(); } - if (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>().is_some() { + if (package.as_any() as &dyn Any) + .downcast_ref::<CompletePackage>() + .is_some() + { // TODO(phase-b): construct CompleteAliasPackage/AliasPackage and return as Box<BasePackage> return todo!("new CompleteAliasPackage(package, alias, pretty_alias)"); } @@ -291,10 +298,7 @@ impl RepositoryInterface for ArrayRepository { ) } else { // vendor/name searches expect the caller to have preg_quoted the query - format!( - "{{(?:{})}}i", - implode("|", &Preg::split("{\\s+}", &query)) - ) + format!("{{(?:{})}}i", implode("|", &Preg::split("{\\s+}", &query))) }; let mut matches: IndexMap<String, SearchResult> = IndexMap::new(); @@ -314,8 +318,7 @@ impl RepositoryInterface for ArrayRepository { } } - let complete = - (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>(); + let complete = (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>(); let fulltext_match = mode == Self::SEARCH_FULLTEXT && complete.is_some() @@ -397,8 +400,8 @@ impl RepositoryInterface for ArrayRepository { } for link in candidate.get_provides().values() { if package_name == link.get_target() { - let complete = (candidate.as_any() as &dyn Any) - .downcast_ref::<CompletePackage>(); + let complete = + (candidate.as_any() as &dyn Any).downcast_ref::<CompletePackage>(); let description = complete.and_then(|c| c.get_description().map(String::from)); result.insert( candidate.get_name().to_string(), diff --git a/crates/shirabe/src/repository/artifact_repository.rs b/crates/shirabe/src/repository/artifact_repository.rs index 988e456..a9832a1 100644 --- a/crates/shirabe/src/repository/artifact_repository.rs +++ b/crates/shirabe/src/repository/artifact_repository.rs @@ -3,7 +3,9 @@ use std::path::Path; use indexmap::IndexMap; -use shirabe_php_shim::{extension_loaded, hash_file, PhpMixed, RuntimeException, UnexpectedValueException}; +use shirabe_php_shim::{ + PhpMixed, RuntimeException, UnexpectedValueException, extension_loaded, hash_file, +}; use crate::io::io_interface::IOInterface; use crate::json::json_file::JsonFile; @@ -34,7 +36,10 @@ impl std::fmt::Debug for ArtifactRepository { } impl ArtifactRepository { - pub fn new(repo_config: IndexMap<String, PhpMixed>, io: Box<dyn IOInterface>) -> anyhow::Result<Self> { + pub fn new( + repo_config: IndexMap<String, PhpMixed>, + io: Box<dyn IOInterface>, + ) -> anyhow::Result<Self> { if !extension_loaded("zip") { return Err(RuntimeException { message: "The artifact repository requires PHP's zip extension".to_string(), @@ -101,7 +106,10 @@ impl ArtifactRepository { match package { None => { self.io.write_error( - &format!("File <comment>{}</comment> doesn't seem to hold a package", basename), + &format!( + "File <comment>{}</comment> doesn't seem to hold a package", + basename + ), true, IOInterface::VERBOSE, ); @@ -169,7 +177,8 @@ impl ArtifactRepository { return Ok(None); } - let mut package = JsonFile::parse_json(&json.unwrap(), &format!("{}#composer.json", pathname))?; + let mut package = + JsonFile::parse_json(&json.unwrap(), &format!("{}#composer.json", pathname))?; let url_normalized = pathname.replace('\\', '/'); let real_path = file .canonicalize() @@ -179,8 +188,14 @@ impl ArtifactRepository { let shasum = hash_file("sha1", &real_path).unwrap_or_default(); let mut dist = IndexMap::new(); - dist.insert("type".to_string(), Box::new(PhpMixed::String(file_type.to_string()))); - dist.insert("url".to_string(), Box::new(PhpMixed::String(url_normalized))); + dist.insert( + "type".to_string(), + Box::new(PhpMixed::String(file_type.to_string())), + ); + dist.insert( + "url".to_string(), + Box::new(PhpMixed::String(url_normalized)), + ); dist.insert("shasum".to_string(), Box::new(PhpMixed::String(shasum))); package.insert("dist".to_string(), Box::new(PhpMixed::Array(dist))); diff --git a/crates/shirabe/src/repository/canonical_packages_trait.rs b/crates/shirabe/src/repository/canonical_packages_trait.rs index 3fac07e..8fae936 100644 --- a/crates/shirabe/src/repository/canonical_packages_trait.rs +++ b/crates/shirabe/src/repository/canonical_packages_trait.rs @@ -1,7 +1,7 @@ //! ref: composer/src/Composer/Repository/CanonicalPackagesTrait.php -use indexmap::IndexMap; use crate::package::package_interface::PackageInterface; +use indexmap::IndexMap; /// Provides get_canonical_packages() to various repository implementations. pub trait CanonicalPackagesTrait { diff --git a/crates/shirabe/src/repository/composer_repository.rs b/crates/shirabe/src/repository/composer_repository.rs index 9d74e5b..ea73090 100644 --- a/crates/shirabe/src/repository/composer_repository.rs +++ b/crates/shirabe/src/repository/composer_repository.rs @@ -5,11 +5,10 @@ use shirabe_external_packages::composer::metadata_minifier::metadata_minifier::M use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use shirabe_php_shim::{ - PhpMixed, InvalidArgumentException, LogicException, RuntimeException, - UnexpectedValueException, PHP_EOL, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, - extension_loaded, hash, http_build_query, in_array, - json_decode, parse_url_all, realpath, strtolower, strtr, - spl_object_hash, urlencode, var_export, + InvalidArgumentException, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, LogicException, + PHP_EOL, PhpMixed, RuntimeException, UnexpectedValueException, extension_loaded, hash, + http_build_query, in_array, json_decode, parse_url_all, realpath, spl_object_hash, strtolower, + strtr, urlencode, var_export, }; use shirabe_semver::compiling_matcher::CompilingMatcher; @@ -190,7 +189,11 @@ impl ComposerRepository { } if url_after.starts_with("https?") { - let scheme = if extension_loaded("openssl") { "https" } else { "http" }; + let scheme = if extension_loaded("openssl") { + "https" + } else { + "http" + }; let rest = &url_after[6..]; repo_config.insert( "url".to_string(), @@ -211,10 +214,7 @@ impl ComposerRepository { .map_or(false, |s| !s.is_empty()); if url_bits_arr.is_none() || !scheme_present { return Err(UnexpectedValueException { - message: format!( - "Invalid url given for Composer repository: {}", - current_url - ), + message: format!("Invalid url given for Composer repository: {}", current_url), code: 0, } .into()); @@ -255,8 +255,7 @@ impl ComposerRepository { url = format!("{}://repo.packagist.org", proto); } - let base_url_trimmed = - Preg::replace(r"{(?:/[^/\\]+\.json)?(?:[?#].*)?$}", "", &url)?; + let base_url_trimmed = Preg::replace(r"{(?:/[^/\\]+\.json)?(?:[?#].*)?$}", "", &url)?; let base_url = base_url_trimmed.trim_end_matches('/').to_string(); assert!(!base_url.is_empty()); @@ -503,15 +502,14 @@ impl ComposerRepository { if self.lazy_providers_url.is_some() { if let Some(ref available_packages) = self.available_packages.clone() { if self.available_package_patterns.is_none() { - let mut package_map: IndexMap< - String, - Option<Box<dyn ConstraintInterface>>, - > = IndexMap::new(); + let mut package_map: IndexMap<String, Option<Box<dyn ConstraintInterface>>> = + IndexMap::new(); for name in available_packages.values() { package_map.insert( name.clone(), - Some(Box::new(MatchAllConstraint::new()) - as Box<dyn ConstraintInterface>), + Some( + Box::new(MatchAllConstraint::new()) as Box<dyn ConstraintInterface> + ), ); } @@ -536,10 +534,8 @@ impl ComposerRepository { let partial = self.partial_packages_by_name.clone().unwrap(); let flat: Vec<IndexMap<String, PhpMixed>> = partial.into_values().flatten().collect(); - return self.create_packages_flat( - flat, - Some("packages.json inline packages".to_string()), - ); + return self + .create_packages_flat(flat, Some("packages.json inline packages".to_string())); } return Err(LogicException { @@ -643,10 +639,7 @@ impl ComposerRepository { Ok(vendors) } - fn load_package_list( - &mut self, - package_filter: Option<&str>, - ) -> anyhow::Result<Vec<String>> { + fn load_package_list(&mut self, package_filter: Option<&str>) -> anyhow::Result<Vec<String>> { if self.list_url.is_none() { return Err(LogicException { message: "Make sure to call loadRootServerFile before loadPackageList".to_string(), @@ -768,8 +761,7 @@ impl ComposerRepository { let matches_constraint = match &constraint { None => true, Some(c) => { - let pkg_c = - Constraint::new("==", candidate.get_version().to_string()); + let pkg_c = Constraint::new("==", candidate.get_version().to_string()); c.matches(&pkg_c) } }; @@ -909,10 +901,7 @@ impl ComposerRepository { for name in Preg::grep(®ex, &vendor_names)? { let mut entry = IndexMap::new(); entry.insert("name".to_string(), PhpMixed::String(name)); - entry.insert( - "description".to_string(), - PhpMixed::String(String::new()), - ); + entry.insert("description".to_string(), PhpMixed::String(String::new())); results.push(entry); } @@ -926,8 +915,7 @@ impl ComposerRepository { r"{^\^(?P<query>(?P<vendor>[a-z0-9_.-]+)/[a-z0-9_.-]*)\*?$}i", &query, &mut match_groups, - )? - && self.list_url.is_some() + )? && self.list_url.is_some() { let q = match_groups.get(1).cloned().unwrap_or_default(); let vendor = match_groups.get(2).cloned().unwrap_or_default(); @@ -951,14 +939,9 @@ impl ComposerRepository { for name_mixed in list.iter() { if let Some(name) = name_mixed.as_string() { let mut entry = IndexMap::new(); - entry.insert( - "name".to_string(), - PhpMixed::String(name.to_string()), - ); - entry.insert( - "description".to_string(), - PhpMixed::String(String::new()), - ); + entry.insert("name".to_string(), PhpMixed::String(name.to_string())); + entry + .insert("description".to_string(), PhpMixed::String(String::new())); results.push(entry); } } @@ -975,10 +958,7 @@ impl ComposerRepository { for name in Preg::grep(®ex, &package_names)? { let mut entry = IndexMap::new(); entry.insert("name".to_string(), PhpMixed::String(name)); - entry.insert( - "description".to_string(), - PhpMixed::String(String::new()), - ); + entry.insert("description".to_string(), PhpMixed::String(String::new())); results.push(entry); } @@ -991,9 +971,10 @@ impl ComposerRepository { pub fn has_security_advisories(&mut self) -> anyhow::Result<bool> { self.load_root_server_file(Some(600))?; - Ok(self.security_advisory_config.as_ref().map_or(false, |c| { - c.metadata || c.api_url.is_some() - })) + Ok(self + .security_advisory_config + .as_ref() + .map_or(false, |c| c.metadata || c.api_url.is_some())) } /// @inheritDoc @@ -1032,10 +1013,7 @@ impl ComposerRepository { let repo_name = self.get_repo_name(); let create = |data: &IndexMap<String, PhpMixed>, name: &str, - package_constraint_map: &IndexMap< - String, - Box<dyn ConstraintInterface>, - >| + package_constraint_map: &IndexMap<String, Box<dyn ConstraintInterface>>| -> anyhow::Result<Option<PartialOrSecurityAdvisory>> { let advisory = PartialSecurityAdvisory::create(name.to_string(), data.clone(), &parser)?; @@ -1095,8 +1073,7 @@ impl ComposerRepository { .then_boxed(Box::new({ let advisories_ptr = &mut advisories as *mut _; let names_found_ptr = &mut names_found as *mut _; - let package_constraint_map_ptr = - &mut package_constraint_map as *mut _; + let package_constraint_map_ptr = &mut package_constraint_map as *mut _; let name = name.clone(); let create = &create; move |spec: PhpMixed| -> anyhow::Result<()> { @@ -1129,10 +1106,8 @@ impl ComposerRepository { .iter() .map(|(k, v)| (k.clone(), (**v).clone())) .collect(); - let pcm: &IndexMap< - String, - Box<dyn ConstraintInterface>, - > = unsafe { &*package_constraint_map_ptr }; + let pcm: &IndexMap<String, Box<dyn ConstraintInterface>> = + unsafe { &*package_constraint_map_ptr }; if let Some(adv) = create(&data_map, &name, pcm)? { entries.push(adv); } @@ -1183,10 +1158,7 @@ impl ComposerRepository { headers.push(Box::new(PhpMixed::String( "Content-type: application/x-www-form-urlencoded".to_string(), ))); - http_map.insert( - "header".to_string(), - Box::new(PhpMixed::List(headers)), - ); + http_map.insert("header".to_string(), Box::new(PhpMixed::List(headers))); http_map.insert("timeout".to_string(), Box::new(PhpMixed::Int(10))); let packages_list: Vec<(String, String)> = package_constraint_map .keys() @@ -1200,10 +1172,7 @@ impl ComposerRepository { "&", "=", ); - http_map.insert( - "content".to_string(), - Box::new(PhpMixed::String(body)), - ); + http_map.insert("content".to_string(), Box::new(PhpMixed::String(body))); } let response = self.http_downloader.get(&api_url, &options)?; @@ -1240,8 +1209,7 @@ impl ComposerRepository { .iter() .map(|(k, v)| (k.clone(), (**v).clone())) .collect(); - if let Some(adv) = - create(&data_map, name, &package_constraint_map)? + if let Some(adv) = create(&data_map, name, &package_constraint_map)? { entries.push(adv); } @@ -1312,9 +1280,8 @@ impl ComposerRepository { if self.has_partial_packages()? { if self.partial_packages_by_name.is_none() { return Err(LogicException { - message: - "hasPartialPackages failed to initialize $this->partialPackagesByName" - .to_string(), + message: "hasPartialPackages failed to initialize $this->partialPackagesByName" + .to_string(), code: 0, } .into()); @@ -1335,10 +1302,7 @@ impl ComposerRepository { continue; } let mut entry: IndexMap<String, PhpMixed> = IndexMap::new(); - entry.insert( - "name".to_string(), - PhpMixed::String(candidate_name.clone()), - ); + entry.insert("name".to_string(), PhpMixed::String(candidate_name.clone())); entry.insert( "description".to_string(), candidate @@ -1523,11 +1487,8 @@ impl ComposerRepository { if let Some(last_modified) = arr.get("last-modified").and_then(|v| v.as_string()) { - let response = self.fetch_file_if_last_modified( - &url, - &cache_key, - last_modified, - )?; + let response = + self.fetch_file_if_last_modified(&url, &cache_key, last_modified)?; match response { FetchFileIfLastModifiedResult::NotModified => { let map: IndexMap<String, PhpMixed> = arr @@ -1563,10 +1524,8 @@ impl ComposerRepository { ) { Ok(p) => { packages_opt = Some(p); - packages_source = Some(format!( - "downloaded file ({})", - Url::sanitize(url.clone()) - )); + packages_source = + Some(format!("downloaded file ({})", Url::sanitize(url.clone()))); } Err(e) => { // 404s are acceptable for lazy provider repos @@ -1583,20 +1542,15 @@ impl ComposerRepository { ) { let mut p: IndexMap<String, PhpMixed> = IndexMap::new(); - p.insert( - "packages".to_string(), - PhpMixed::Array(IndexMap::new()), - ); + p.insert("packages".to_string(), PhpMixed::Array(IndexMap::new())); packages_opt = Some(p); packages_source = Some(format!( "not-found file ({})", Url::sanitize(url.clone()) )); if status_code == 499 { - self.io.error(&format!( - "<warning>{}</warning>", - te.get_message() - )); + self.io + .error(&format!("<warning>{}</warning>", te.get_message())); } } else { return Err(e); @@ -1757,8 +1711,7 @@ impl ComposerRepository { // load acceptable packages in the providers let versions_to_load_vec: Vec<IndexMap<String, PhpMixed>> = versions_to_load.values().cloned().collect(); - let loaded_packages = - self.create_packages_flat(versions_to_load_vec, packages_source)?; + let loaded_packages = self.create_packages_flat(versions_to_load_vec, packages_source)?; let uids: Vec<String> = versions_to_load.keys().cloned().collect(); for (index, mut package) in loaded_packages.into_iter().enumerate() { @@ -1785,7 +1738,10 @@ impl ComposerRepository { let repo_data = self.load_data_from_server()?; - let source = format!("root file ({})", Url::sanitize(self.get_packages_json_url())); + let source = format!( + "root file ({})", + Url::sanitize(self.get_packages_json_url()) + ); for package in self.create_packages_flat(repo_data, Some(source))? { self.add_package(package); } @@ -1815,9 +1771,12 @@ impl ComposerRepository { if self.lazy_providers_url.is_none() { return Err(LogicException { - message: "loadAsyncPackages only supports v2 protocol composer repos with a metadata-url".to_string(), + message: + "loadAsyncPackages only supports v2 protocol composer repos with a metadata-url" + .to_string(), code: 0, - }.into()); + } + .into()); } // load ~dev versions of the packages as well if needed @@ -1836,8 +1795,7 @@ impl ComposerRepository { package_names.insert(format!("{}~dev", name), constraint); } // if only dev stability is requested, we skip loading the non dev file - if acceptable_stabilities - .map_or(false, |m| m.contains_key("dev") && m.len() == 1) + if acceptable_stabilities.map_or(false, |m| m.contains_key("dev") && m.len() == 1) && stability_flags.map_or(false, |m| m.is_empty()) { package_names.shift_remove(&name); @@ -2043,7 +2001,9 @@ impl ComposerRepository { } let name = strtolower(file_name); - let package_name = package_name.map(|s| s.to_string()).unwrap_or_else(|| name.clone()); + let package_name = package_name + .map(|s| s.to_string()) + .unwrap_or_else(|| name.clone()); let url = self .lazy_providers_url @@ -2091,9 +2051,7 @@ impl ComposerRepository { contents .clone() .map(|m| { - 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()) }) .unwrap_or(PhpMixed::Null) } else { @@ -2105,8 +2063,8 @@ impl ComposerRepository { .and_then(|a| a.get("packages")) .and_then(|v| v.as_array()) .map_or(false, |a| a.contains_key(&package_name)); - let has_advisories = response_arr - .map_or(false, |a| a.contains_key("security-advisories")); + let has_advisories = + response_arr.map_or(false, |a| a.contains_key("security-advisories")); if !has_pkg && !has_advisories { return Ok(PhpMixed::List(vec![ Box::new(PhpMixed::Null), @@ -2166,11 +2124,13 @@ impl ComposerRepository { acceptable_stabilities: Option<&IndexMap<String, i64>>, stability_flags: Option<&IndexMap<String, i64>>, ) -> anyhow::Result<bool> { - let mut versions: Vec<String> = vec![version_data - .get("version_normalized") - .and_then(|v| v.as_string()) - .unwrap_or("") - .to_string()]; + let mut versions: Vec<String> = vec![ + version_data + .get("version_normalized") + .and_then(|v| v.as_string()) + .unwrap_or("") + .to_string(), + ]; if let Some(alias) = loader.get_branch_alias(version_data) { versions.push(alias); @@ -2216,10 +2176,7 @@ impl ComposerRepository { format!("{}/packages.json", self.url) } - fn load_root_server_file( - &mut self, - root_max_age: Option<i64>, - ) -> anyhow::Result<RootData> { + fn load_root_server_file(&mut self, root_max_age: Option<i64>) -> anyhow::Result<RootData> { if let Some(rd) = &self.root_data { return Ok(clone_root_data(rd)); } @@ -2243,9 +2200,7 @@ impl ComposerRepository { .map(|(k, v)| (k.clone(), (**v).clone())) .collect(); let age = self.cache.get_age("packages.json"); - if root_max_age.is_some() - && age.is_some() - && age.unwrap() <= root_max_age.unwrap() + if root_max_age.is_some() && age.is_some() && age.unwrap() <= root_max_age.unwrap() { data = Some(cached_data); } else if let Some(last_modified) = cached_data @@ -2550,16 +2505,13 @@ impl ComposerRepository { return Err(InvalidArgumentException { message: "Expected a string with a value and not an empty string".to_string(), code: 0, - }.into()); + } + .into()); } if url.starts_with('/') { let mut matches: Vec<String> = Vec::new(); - if Preg::is_match_with_matches( - r"{^[^:]++://[^/]*+}", - &self.url, - &mut matches, - )? { + if Preg::is_match_with_matches(r"{^[^:]++://[^/]*+}", &self.url, &mut matches)? { return Ok(format!( "{}{}", matches.get(0).cloned().unwrap_or_default(), @@ -2581,7 +2533,8 @@ impl ComposerRepository { message: "loadRootServerFile should not return true during initialization" .to_string(), code: 0, - }.into()); + } + .into()); } RootData::Data(d) => d, }; @@ -2597,10 +2550,7 @@ impl ComposerRepository { Ok(self.has_partial_packages) } - fn load_provider_listings( - &mut self, - data: &IndexMap<String, PhpMixed>, - ) -> anyhow::Result<()> { + fn load_provider_listings(&mut self, data: &IndexMap<String, PhpMixed>) -> anyhow::Result<()> { if let Some(providers) = data.get("providers").and_then(|v| v.as_array()) { if self.provider_listing.is_none() { self.provider_listing = Some(IndexMap::new()); @@ -2636,11 +2586,7 @@ impl ComposerRepository { .and_then(|v| v.as_string()) .unwrap_or("") .to_string(); - let url = format!( - "{}/{}", - self.base_url, - include.replace("%hash%", &sha256) - ); + let url = format!("{}/{}", self.base_url, include.replace("%hash%", &sha256)); let cache_key = include.replace("%hash%", "").replace("$", ""); let included_data: IndexMap<String, PhpMixed> = if self.cache.sha256(&cache_key).as_deref() == Some(sha256.as_str()) { @@ -2649,9 +2595,7 @@ impl ComposerRepository { decoded .as_array() .map(|a| { - a.iter() - .map(|(k, v)| (k.clone(), (**v).clone())) - .collect() + a.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect() }) .unwrap_or_default() } else { @@ -2681,11 +2625,8 @@ impl ComposerRepository { if let Some(versions) = pkg.get("versions").and_then(|v| v.as_array()) { for (_, metadata) in versions.iter() { if let Some(m) = metadata.as_array() { - packages.push( - m.iter() - .map(|(k, v)| (k.clone(), (**v).clone())) - .collect(), - ); + packages + .push(m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect()); } } } @@ -2745,11 +2686,7 @@ impl ComposerRepository { let decoded = json_decode(&raw, true)?; decoded .as_array() - .map(|a| { - a.iter() - .map(|(k, v)| (k.clone(), (**v).clone())) - .collect() - }) + .map(|a| a.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect()) .unwrap_or_default() } else { self.fetch_file(include, None, None, false)? @@ -2795,10 +2732,8 @@ impl ComposerRepository { let mut results: Vec<Box<BasePackage>> = Vec::new(); for mut package in package_instances.into_iter() { if let Some(src_type) = package.get_source_type() { - if let Some(mirrors) = self - .source_mirrors - .as_ref() - .and_then(|m| m.get(src_type)) + if let Some(mirrors) = + self.source_mirrors.as_ref().and_then(|m| m.get(src_type)) { package.set_source_mirrors(mirrors); } @@ -2954,11 +2889,7 @@ impl ComposerRepository { let decoded = response.decode_json()?; let mut data_local: IndexMap<String, PhpMixed> = decoded .as_array() - .map(|a| { - a.iter() - .map(|(k, v)| (k.clone(), (**v).clone())) - .collect() - }) + .map(|a| a.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect()) .unwrap_or_default(); HttpDownloader::output_warnings(&*self.io, &self.url, &data_local); @@ -3025,9 +2956,7 @@ impl ComposerRepository { let map: IndexMap<String, PhpMixed> = parsed .as_array() .map(|a| { - a.iter() - .map(|(k, v)| (k.clone(), (**v).clone())) - .collect() + a.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect() }) .unwrap_or_default(); data = Some(map); @@ -3061,7 +2990,8 @@ impl ComposerRepository { return Err(InvalidArgumentException { message: "$filename should not be an empty string".to_string(), code: 0, - }.into()); + } + .into()); } let mut filename = filename.to_string(); @@ -3143,11 +3073,7 @@ impl ComposerRepository { let decoded = response.decode_json()?; let mut data: IndexMap<String, PhpMixed> = decoded .as_array() - .map(|a| { - a.iter() - .map(|(k, v)| (k.clone(), (**v).clone())) - .collect() - }) + .map(|a| a.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect()) .unwrap_or_default(); HttpDownloader::output_warnings(&*self.io, &self.url, &data); @@ -3205,7 +3131,8 @@ impl ComposerRepository { return Err(InvalidArgumentException { message: "$filename should not be an empty string".to_string(), code: 0, - }.into()); + } + .into()); } if self.packagesNotFoundCache.contains_key(filename) { @@ -3318,11 +3245,7 @@ impl ComposerRepository { let decoded = response.decode_json()?; let mut data: IndexMap<String, PhpMixed> = decoded .as_array() - .map(|a| { - a.iter() - .map(|(k, v)| (k.clone(), (**v).clone())) - .collect() - }) + .map(|a| a.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect()) .unwrap_or_default(); let io_ref = unsafe { &*io_ptr }; HttpDownloader::output_warnings(io_ref, &url_owned, &data); @@ -3420,7 +3343,11 @@ impl ComposerRepository { }; self.partial_packages_by_name = Some(IndexMap::new()); - if let Some(packages) = root_data.get("packages").and_then(|v| v.as_array()).cloned() { + if let Some(packages) = root_data + .get("packages") + .and_then(|v| v.as_array()) + .cloned() + { for (package, versions_mixed) in packages.iter() { let versions = match versions_mixed.as_array() { Some(a) => a.clone(), diff --git a/crates/shirabe/src/repository/composite_repository.rs b/crates/shirabe/src/repository/composite_repository.rs index 9787b6a..5413bbe 100644 --- a/crates/shirabe/src/repository/composite_repository.rs +++ b/crates/shirabe/src/repository/composite_repository.rs @@ -18,7 +18,9 @@ pub struct CompositeRepository { impl CompositeRepository { pub fn new(repositories: Vec<Box<dyn RepositoryInterface>>) -> Self { - let mut this = Self { repositories: vec![] }; + let mut this = Self { + repositories: vec![], + }; for repo in repositories { this.add_repository(repo); } @@ -37,7 +39,9 @@ impl CompositeRepository { } pub fn add_repository(&mut self, repository: Box<dyn RepositoryInterface>) { - if let Some(composite) = (repository.as_any() as &dyn Any).downcast_ref::<CompositeRepository>() { + if let Some(composite) = + (repository.as_any() as &dyn Any).downcast_ref::<CompositeRepository>() + { for repo in composite.get_repositories() { self.repositories.push(repo.clone_box()); } @@ -55,7 +59,11 @@ impl shirabe_php_shim::Countable for CompositeRepository { impl RepositoryInterface for CompositeRepository { fn get_repo_name(&self) -> String { - let names: Vec<String> = self.repositories.iter().map(|r| r.get_repo_name()).collect(); + let names: Vec<String> = self + .repositories + .iter() + .map(|r| r.get_repo_name()) + .collect(); format!("composite repo ({})", names.join(", ")) } @@ -68,7 +76,11 @@ impl RepositoryInterface for CompositeRepository { false } - fn find_package(&self, name: String, constraint: FindPackageConstraint) -> Option<Box<BasePackage>> { + fn find_package( + &self, + name: String, + constraint: FindPackageConstraint, + ) -> Option<Box<BasePackage>> { for repository in &self.repositories { let package = repository.find_package(name.clone(), constraint.clone()); if package.is_some() { @@ -78,7 +90,11 @@ impl RepositoryInterface for CompositeRepository { None } - fn find_packages(&self, name: String, constraint: Option<FindPackageConstraint>) -> Vec<Box<BasePackage>> { + fn find_packages( + &self, + name: String, + constraint: Option<FindPackageConstraint>, + ) -> Vec<Box<BasePackage>> { let mut packages = vec![]; for repository in &self.repositories { packages.extend(repository.find_packages(name.clone(), constraint.clone())); diff --git a/crates/shirabe/src/repository/filesystem_repository.rs b/crates/shirabe/src/repository/filesystem_repository.rs index 5cbff22..4ca5ab1 100644 --- a/crates/shirabe/src/repository/filesystem_repository.rs +++ b/crates/shirabe/src/repository/filesystem_repository.rs @@ -6,10 +6,10 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - array_flip, dirname, file_get_contents, get_class, get_debug_type, in_array, is_array, - is_int, is_null, is_string, ksort, php_dir, r#eval, realpath, sort, sort_with_flags, - str_repeat, strtr, trim, usort, var_export, InvalidArgumentException, LogicException, - PhpMixed, Silencer, UnexpectedValueException, SORT_NATURAL, + InvalidArgumentException, LogicException, PhpMixed, SORT_NATURAL, Silencer, + UnexpectedValueException, array_flip, dirname, r#eval, file_get_contents, get_class, + get_debug_type, in_array, is_array, is_int, is_null, is_string, ksort, php_dir, realpath, sort, + sort_with_flags, str_repeat, strtr, trim, usort, var_export, }; use crate::installed_versions::InstalledVersions; @@ -141,12 +141,18 @@ impl FilesystemRepository { let mut loader = ArrayLoader::new(None, true); if let Some(packages_list) = packages.as_list() { for package_data in packages_list.iter() { - let package = loader.load((**package_data).clone(), "Composer\\Package\\CompletePackage")?; + let package = loader.load( + (**package_data).clone(), + "Composer\\Package\\CompletePackage", + )?; self.inner.add_package(package)?; } } else if let Some(packages_array) = packages.as_array() { for (_, package_data) in packages_array.iter() { - let package = loader.load((**package_data).clone(), "Composer\\Package\\CompletePackage")?; + let package = loader.load( + (**package_data).clone(), + "Composer\\Package\\CompletePackage", + )?; self.inner.add_package(package)?; } } @@ -179,9 +185,9 @@ impl FilesystemRepository { let repo_dir = dirname(self.file.get_path()); self.filesystem.ensure_directory_exists(&repo_dir); - let repo_dir = self.filesystem.normalize_path( - &realpath(&repo_dir).unwrap_or_default(), - ); + let repo_dir = self + .filesystem + .normalize_path(&realpath(&repo_dir).unwrap_or_default()); let mut install_paths: IndexMap<String, Option<String>> = IndexMap::new(); for package in self.inner.get_canonical_packages() { @@ -190,13 +196,18 @@ impl FilesystemRepository { let mut install_path: Option<String> = None; if let Some(path_str) = &path { if !path_str.is_empty() { - let normalized_path = self.filesystem.normalize_path( - &if self.filesystem.is_absolute_path(path_str) { - path_str.clone() - } else { - format!("{}/{}", Platform::get_cwd(false).unwrap_or_default(), path_str) - }, - ); + let normalized_path = self.filesystem.normalize_path(&if self + .filesystem + .is_absolute_path(path_str) + { + path_str.clone() + } else { + format!( + "{}/{}", + Platform::get_cwd(false).unwrap_or_default(), + path_str + ) + }); install_path = Some(self.filesystem.find_shortest_path( &repo_dir, &normalized_path, @@ -215,7 +226,10 @@ impl FilesystemRepository { ); if let Some(PhpMixed::List(list)) = data.get_mut("packages") { list.push(Box::new(PhpMixed::Array( - pkg_array.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), + pkg_array + .into_iter() + .map(|(k, v)| (k, Box::new(v))) + .collect(), ))); } @@ -273,20 +287,19 @@ impl FilesystemRepository { )?; if self.dump_versions { - let versions = - self.generate_installed_versions(installation_manager, &install_paths, dev_mode, &repo_dir)?; + let versions = self.generate_installed_versions( + installation_manager, + &install_paths, + dev_mode, + &repo_dir, + )?; self.filesystem.file_put_contents_if_modified( &format!("{}/installed.php", repo_dir), - &format!( - "<?php return {};\n", - self.dump_to_php_code(&versions, 0), - ), + &format!("<?php return {};\n", self.dump_to_php_code(&versions, 0),), ); - let installed_versions_class = file_get_contents(&format!( - "{}/../InstalledVersions.php", - php_dir(), - )); + let installed_versions_class = + file_get_contents(&format!("{}/../InstalledVersions.php", php_dir(),)); // this normally should not happen but during upgrades of Composer when it is installed in the project it is a possibility if let Some(class_content) = installed_versions_class { @@ -328,7 +341,7 @@ impl FilesystemRepository { let mixed = PhpMixed::String(data.clone()); if is_string(&mixed) && Preg::is_match(pattern, &trim(&data, None)) { let replaced = Preg::replace( - r"{=>\s*+__DIR__\s*+\.\s*+(['\"])}", + r#"{=>\s*+__DIR__\s*+\.\s*+(['\"])}"#, &format!( "=> {} . $1", var_export(&PhpMixed::String(dirname(path)), true), @@ -391,10 +404,7 @@ impl FilesystemRepository { } else if key == "install_path" && is_string(value) { let s = value.as_string().unwrap_or("").to_string(); if self.filesystem.is_absolute_path(&s) { - lines.push_str(&format!( - "{},\n", - var_export(&PhpMixed::String(s), true), - )); + lines.push_str(&format!("{},\n", var_export(&PhpMixed::String(s), true),)); } else { lines.push_str(&format!( "__DIR__ . {},\n", @@ -447,7 +457,9 @@ impl FilesystemRepository { let mut root_package = match &self.root_package { None => { return Err(LogicException { - message: "It should not be possible to dump packages if no root package is given".to_string(), + message: + "It should not be possible to dump packages if no root package is given" + .to_string(), code: 0, } .into()); @@ -460,8 +472,11 @@ impl FilesystemRepository { let mut current_root: Box<dyn RootPackageInterface> = root_package; // packages.push(current_root.clone_box()); - while let Some(_alias) = (current_root.as_any() as &dyn Any).downcast_ref::<RootAliasPackage>() { - current_root = todo!("RootAliasPackage::get_alias_of() returning Box<dyn RootPackageInterface>"); + while let Some(_alias) = + (current_root.as_any() as &dyn Any).downcast_ref::<RootAliasPackage>() + { + current_root = + todo!("RootAliasPackage::get_alias_of() returning Box<dyn RootPackageInterface>"); // packages.push(current_root.clone_box()); } let mut versions: IndexMap<String, PhpMixed> = IndexMap::new(); @@ -480,18 +495,19 @@ impl FilesystemRepository { .collect(), ), ); - versions.insert( - "versions".to_string(), - PhpMixed::Array(IndexMap::new()), - ); + versions.insert("versions".to_string(), PhpMixed::Array(IndexMap::new())); // add real installed packages for package in &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; } - let dumped = self.dump_installed_package(&**package, install_paths, repo_dir, &dev_packages); + let dumped = + self.dump_installed_package(&**package, install_paths, repo_dir, &dev_packages); if let Some(PhpMixed::Array(versions_map)) = versions.get_mut("versions") { versions_map.insert( package.get_name().to_string(), @@ -552,7 +568,10 @@ impl FilesystemRepository { }; // TODO(phase-b): mutate nested versions['versions'][name]['aliases'] todo!("append alias->getPrettyVersion() to versions['versions'][name]['aliases']"); - if (package.as_any() as &dyn Any).downcast_ref::<dyn RootPackageInterface>().is_some() { + if (package.as_any() as &dyn Any) + .downcast_ref::<dyn RootPackageInterface>() + .is_some() + { // TODO(phase-b): same mutation on versions['root']['aliases'] todo!("append alias->getPrettyVersion() to versions['root']['aliases']"); } @@ -614,17 +633,16 @@ impl FilesystemRepository { }; } - let install_path = if (package.as_any() as &dyn Any).downcast_ref::<dyn RootPackageInterface>().is_some() { + let install_path = if (package.as_any() as &dyn Any) + .downcast_ref::<dyn RootPackageInterface>() + .is_some() + { let to = self.filesystem.normalize_path( - &realpath(&Platform::get_cwd(false).unwrap_or_default()) - .unwrap_or_default(), + &realpath(&Platform::get_cwd(false).unwrap_or_default()).unwrap_or_default(), ); Some(self.filesystem.find_shortest_path(repo_dir, &to, true)) } else { - install_paths - .get(package.get_name()) - .cloned() - .flatten() + install_paths.get(package.get_name()).cloned().flatten() }; let mut data: IndexMap<String, PhpMixed> = IndexMap::new(); @@ -690,7 +708,9 @@ impl FilesystemRepository { ); result.insert( "pretty_version".to_string(), - data.get("pretty_version").cloned().unwrap_or(PhpMixed::Null), + data.get("pretty_version") + .cloned() + .unwrap_or(PhpMixed::Null), ); result.insert( "version".to_string(), @@ -710,7 +730,9 @@ impl FilesystemRepository { ); result.insert( "aliases".to_string(), - data.get("aliases").cloned().unwrap_or(PhpMixed::List(vec![])), + data.get("aliases") + .cloned() + .unwrap_or(PhpMixed::List(vec![])), ); result.insert("dev".to_string(), PhpMixed::Bool(dev_mode)); diff --git a/crates/shirabe/src/repository/filter_repository.rs b/crates/shirabe/src/repository/filter_repository.rs index 3a65ef2..eeb1306 100644 --- a/crates/shirabe/src/repository/filter_repository.rs +++ b/crates/shirabe/src/repository/filter_repository.rs @@ -1,16 +1,18 @@ //! ref: composer/src/Composer/Repository/FilterRepository.php -use anyhow::Result; -use indexmap::IndexMap; -use shirabe_php_shim::{InvalidArgumentException, PhpMixed}; -use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_semver::constraint::constraint_interface::ConstraintInterface; use crate::package::base_package::BasePackage; use crate::package::package_interface::PackageInterface; -use crate::repository::advisory_provider_interface::{AdvisoryProviderInterface, SecurityAdvisoryResult}; +use crate::repository::advisory_provider_interface::{ + AdvisoryProviderInterface, SecurityAdvisoryResult, +}; use crate::repository::repository_interface::{ FindPackageConstraint, LoadPackagesResult, ProviderInfo, RepositoryInterface, SearchResult, }; +use anyhow::Result; +use indexmap::IndexMap; +use shirabe_external_packages::composer::pcre::preg::Preg; +use shirabe_php_shim::{InvalidArgumentException, PhpMixed}; +use shirabe_semver::constraint::constraint_interface::ConstraintInterface; #[derive(Debug)] pub struct FilterRepository { @@ -21,7 +23,10 @@ pub struct FilterRepository { } impl FilterRepository { - pub fn new(repo: Box<dyn RepositoryInterface>, options: IndexMap<String, PhpMixed>) -> Result<Self> { + pub fn new( + repo: Box<dyn RepositoryInterface>, + options: IndexMap<String, PhpMixed>, + ) -> Result<Self> { let mut only: Option<String> = None; let mut exclude: Option<String> = None; let mut canonical = true; @@ -29,40 +34,66 @@ impl FilterRepository { if let Some(only_val) = options.get("only") { match only_val { PhpMixed::List(list) => { - let names: Vec<String> = list.iter().filter_map(|v| { - if let PhpMixed::String(s) = v.as_ref() { Some(s.clone()) } else { None } - }).collect(); + let names: Vec<String> = list + .iter() + .filter_map(|v| { + if let PhpMixed::String(s) = v.as_ref() { + Some(s.clone()) + } else { + None + } + }) + .collect(); only = Some(BasePackage::package_names_to_regexp(&names)); } _ => { return Err(InvalidArgumentException { - message: format!(r#""only" key for repository {} should be an array"#, repo.get_repo_name()), + message: format!( + r#""only" key for repository {} should be an array"#, + repo.get_repo_name() + ), code: 0, - }.into()); + } + .into()); } } } if let Some(exclude_val) = options.get("exclude") { match exclude_val { PhpMixed::List(list) => { - let names: Vec<String> = list.iter().filter_map(|v| { - if let PhpMixed::String(s) = v.as_ref() { Some(s.clone()) } else { None } - }).collect(); + let names: Vec<String> = list + .iter() + .filter_map(|v| { + if let PhpMixed::String(s) = v.as_ref() { + Some(s.clone()) + } else { + None + } + }) + .collect(); exclude = Some(BasePackage::package_names_to_regexp(&names)); } _ => { return Err(InvalidArgumentException { - message: format!(r#""exclude" key for repository {} should be an array"#, repo.get_repo_name()), + message: format!( + r#""exclude" key for repository {} should be an array"#, + repo.get_repo_name() + ), code: 0, - }.into()); + } + .into()); } } } if exclude.is_some() && only.is_some() { return Err(InvalidArgumentException { - message: format!(r#"Only one of "only" and "exclude" can be specified for repository {}"#, repo.get_repo_name()), + message: format!( + r#"Only one of "only" and "exclude" can be specified for repository {}"#, + repo.get_repo_name() + ), code: 0, - }.into()); + } + .into()); } if let Some(canonical_val) = options.get("canonical") { match canonical_val { @@ -71,14 +102,23 @@ impl FilterRepository { } _ => { return Err(InvalidArgumentException { - message: format!(r#""canonical" key for repository {} should be a boolean"#, repo.get_repo_name()), + message: format!( + r#""canonical" key for repository {} should be a boolean"#, + repo.get_repo_name() + ), code: 0, - }.into()); + } + .into()); } } } - Ok(Self { only, exclude, canonical, repo }) + Ok(Self { + only, + exclude, + canonical, + repo, + }) } pub fn get_repository(&self) -> &dyn RepositoryInterface { @@ -117,7 +157,11 @@ impl RepositoryInterface for FilterRepository { self.repo.has_package(package) } - fn find_package(&self, name: String, constraint: FindPackageConstraint) -> Option<Box<BasePackage>> { + fn find_package( + &self, + name: String, + constraint: FindPackageConstraint, + ) -> Option<Box<BasePackage>> { if !self.is_allowed(&name) { return None; } @@ -125,7 +169,11 @@ impl RepositoryInterface for FilterRepository { self.repo.find_package(name, constraint) } - fn find_packages(&self, name: String, constraint: Option<FindPackageConstraint>) -> Vec<Box<BasePackage>> { + fn find_packages( + &self, + name: String, + constraint: Option<FindPackageConstraint>, + ) -> Vec<Box<BasePackage>> { if !self.is_allowed(&name) { return Vec::new(); } @@ -143,10 +191,18 @@ impl RepositoryInterface for FilterRepository { package_name_map.retain(|name, _| self.is_allowed(name)); if package_name_map.is_empty() { - return LoadPackagesResult { names_found: Vec::new(), packages: Vec::new() }; + return LoadPackagesResult { + names_found: Vec::new(), + packages: Vec::new(), + }; } - let mut result = self.repo.load_packages(package_name_map, acceptable_stabilities, stability_flags, already_loaded); + let mut result = self.repo.load_packages( + package_name_map, + acceptable_stabilities, + stability_flags, + already_loaded, + ); if !self.canonical { result.names_found = Vec::new(); } @@ -219,7 +275,10 @@ impl AdvisoryProviderInterface for FilterRepository { package_constraint_map.retain(|name, _| self.is_allowed(name)); advisory_repo.get_security_advisories(package_constraint_map, allow_partial_advisories) } else { - Ok(SecurityAdvisoryResult { names_found: Vec::new(), advisories: IndexMap::new() }) + Ok(SecurityAdvisoryResult { + names_found: Vec::new(), + advisories: IndexMap::new(), + }) } } } diff --git a/crates/shirabe/src/repository/installed_repository.rs b/crates/shirabe/src/repository/installed_repository.rs index f3b53ae..bb56c94 100644 --- a/crates/shirabe/src/repository/installed_repository.rs +++ b/crates/shirabe/src/repository/installed_repository.rs @@ -223,11 +223,7 @@ impl InstalledRepository { for pkg in self.find_packages(link.get_target().to_string(), None) { let version = Constraint::new("=", pkg.get_version()); if link.get_constraint().matches(&version) == invert { - results.push(DependentsEntry( - package.clone_box(), - link.clone(), - None, - )); + results.push(DependentsEntry(package.clone_box(), link.clone(), None)); } } } @@ -238,11 +234,7 @@ impl InstalledRepository { for pkg in self.find_packages(link.get_target().to_string(), None) { let version = Constraint::new("=", pkg.get_version()); if link.get_constraint().matches(&version) == invert { - results.push(DependentsEntry( - package.clone_box(), - link.clone(), - None, - )); + results.push(DependentsEntry(package.clone_box(), link.clone(), None)); } } } @@ -327,9 +319,7 @@ impl InstalledRepository { } for root_req in root_reqs.values() { if pkg.get_names().contains(&root_req.get_target().to_string()) - && !root_req - .get_constraint() - .matches(link.get_constraint()) + && !root_req.get_constraint().matches(link.get_constraint()) { results.push(DependentsEntry( package.clone_box(), diff --git a/crates/shirabe/src/repository/mod.rs b/crates/shirabe/src/repository/mod.rs new file mode 100644 index 0000000..e570051 --- /dev/null +++ b/crates/shirabe/src/repository/mod.rs @@ -0,0 +1,31 @@ +pub mod advisory_provider_interface; +pub mod array_repository; +pub mod artifact_repository; +pub mod canonical_packages_trait; +pub mod composer_repository; +pub mod composite_repository; +pub mod configurable_repository_interface; +pub mod filesystem_repository; +pub mod filter_repository; +pub mod installed_array_repository; +pub mod installed_filesystem_repository; +pub mod installed_repository; +pub mod installed_repository_interface; +pub mod invalid_repository_exception; +pub mod lock_array_repository; +pub mod package_repository; +pub mod path_repository; +pub mod pear_repository; +pub mod platform_repository; +pub mod repository_factory; +pub mod repository_interface; +pub mod repository_manager; +pub mod repository_security_exception; +pub mod repository_set; +pub mod repository_utils; +pub mod root_package_repository; +pub mod vcs; +pub mod vcs_repository; +pub mod version_cache_interface; +pub mod writable_array_repository; +pub mod writable_repository_interface; diff --git a/crates/shirabe/src/repository/path_repository.rs b/crates/shirabe/src/repository/path_repository.rs index f2b470d..c148880 100644 --- a/crates/shirabe/src/repository/path_repository.rs +++ b/crates/shirabe/src/repository/path_repository.rs @@ -3,8 +3,8 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - defined, file_exists, file_get_contents, glob_with_flags, hash, realpath, serialize, - PhpMixed, RuntimeException, DIRECTORY_SEPARATOR, GLOB_BRACE, GLOB_MARK, GLOB_ONLYDIR, + DIRECTORY_SEPARATOR, GLOB_BRACE, GLOB_MARK, GLOB_ONLYDIR, PhpMixed, RuntimeException, defined, + file_exists, file_get_contents, glob_with_flags, hash, realpath, serialize, }; use crate::config::Config; @@ -65,8 +65,7 @@ impl PathRepository { .to_string(); let url = Platform::expand_path(&url_str); let process = process.unwrap_or_else(|| ProcessExecutor::new(&*io)); - let version_guesser = - VersionGuesser::new(&config, &process, VersionParser::new(), &*io); + let version_guesser = VersionGuesser::new(&config, &process, VersionParser::new(), &*io); let mut options = repo_config .get("options") .and_then(|v| v.as_array()) @@ -133,10 +132,7 @@ impl PathRepository { } for url in url_matches { - let path = format!( - "{}/", - realpath(&url).unwrap_or_default() - ); + let path = format!("{}/", realpath(&url).unwrap_or_default()); let composer_file_path = format!("{}composer.json", path); if !file_exists(&composer_file_path) { @@ -144,11 +140,14 @@ impl PathRepository { } let json = file_get_contents(&composer_file_path).unwrap_or_default(); - let mut package = JsonFile::parse_json(&json, Some(&composer_file_path))? - .unwrap_or_default(); + let mut package = + JsonFile::parse_json(&json, Some(&composer_file_path))?.unwrap_or_default(); let dist = { let mut dist = IndexMap::new(); - dist.insert("type".to_string(), Box::new(PhpMixed::String("path".to_string()))); + dist.insert( + "type".to_string(), + Box::new(PhpMixed::String("path".to_string())), + ); dist.insert("url".to_string(), Box::new(PhpMixed::String(url.clone()))); dist }; @@ -193,7 +192,11 @@ impl PathRepository { ); // use the version provided as option if available - if let Some(name) = package.get("name").and_then(|v| v.as_string()).map(|s| s.to_string()) { + if let Some(name) = package + .get("name") + .and_then(|v| v.as_string()) + .map(|s| s.to_string()) + { if let Some(version) = self .options .get("versions") @@ -226,9 +229,7 @@ impl PathRepository { { package.insert( "version".to_string(), - PhpMixed::String( - self.version_guesser.get_root_version_from_env(), - ), + PhpMixed::String(self.version_guesser.get_root_version_from_env()), ); } } @@ -236,20 +237,25 @@ impl PathRepository { } let mut output = String::new(); - let command = GitUtil::build_rev_list_command( - &self.process, - { - let mut args = vec!["-n1".to_string(), "--format=%H".to_string(), "HEAD".to_string()]; - args.extend(GitUtil::get_no_show_signature_flags(&self.process)); - args - }, - ); + let command = GitUtil::build_rev_list_command(&self.process, { + let mut args = vec![ + "-n1".to_string(), + "--format=%H".to_string(), + "HEAD".to_string(), + ]; + args.extend(GitUtil::get_no_show_signature_flags(&self.process)); + args + }); if reference == "auto" && shirabe_php_shim::is_dir(&format!("{}/.git", path.trim_end_matches('/'))) - && self.process.execute(&command, &mut output, Some(path.clone())) == 0 + && self + .process + .execute(&command, &mut output, Some(path.clone())) + == 0 { - let ref_val = - GitUtil::parse_rev_list_output(&output, &self.process).trim().to_string(); + let ref_val = GitUtil::parse_rev_list_output(&output, &self.process) + .trim() + .to_string(); if let Some(PhpMixed::Array(ref mut dist)) = package.get_mut("dist") { dist.insert("reference".to_string(), Box::new(PhpMixed::String(ref_val))); } @@ -294,12 +300,17 @@ impl PathRepository { } self.inner - .add_package(self.loader.load(package.clone()).map_err(|e| { - RuntimeException { - message: format!("Failed loading the package in {}", composer_file_path), - code: 0, - } - })?); + .add_package( + self.loader + .load(package.clone()) + .map_err(|e| RuntimeException { + message: format!( + "Failed loading the package in {}", + composer_file_path + ), + code: 0, + })?, + ); } Ok(()) @@ -324,7 +335,11 @@ impl PathRepository { // Ensure environment-specific path separators are normalized to URL separators Ok(glob_with_flags(&self.url, flags) .into_iter() - .map(|val| val.replace(DIRECTORY_SEPARATOR, "/").trim_end_matches('/').to_string()) + .map(|val| { + val.replace(DIRECTORY_SEPARATOR, "/") + .trim_end_matches('/') + .to_string() + }) .collect()) } } diff --git a/crates/shirabe/src/repository/platform_repository.rs b/crates/shirabe/src/repository/platform_repository.rs index 6f3f774..5c6bec7 100644 --- a/crates/shirabe/src/repository/platform_repository.rs +++ b/crates/shirabe/src/repository/platform_repository.rs @@ -7,9 +7,9 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::composer::xdebug_handler::xdebug_handler::XdebugHandler; use shirabe_php_shim::{ - array_map_str_fn, array_slice, array_slice_strs, explode, get_class, implode, in_array, - is_string, sprintf, str_replace, str_starts_with, strpos, strtolower, var_export, - InvalidArgumentException, PhpMixed, UnexpectedValueException, + InvalidArgumentException, PhpMixed, UnexpectedValueException, array_map_str_fn, array_slice, + array_slice_strs, explode, get_class, implode, in_array, is_string, sprintf, str_replace, + str_starts_with, strpos, strtolower, var_export, }; use shirabe_semver::constraint::constraint::Constraint; @@ -27,8 +27,7 @@ use crate::repository::array_repository::ArrayRepository; use crate::repository::repository_interface::RepositoryInterface; use crate::util::silencer::Silencer; -static LAST_SEEN_PLATFORM_PHP: LazyLock<Mutex<Option<String>>> = - LazyLock::new(|| Mutex::new(None)); +static LAST_SEEN_PLATFORM_PHP: LazyLock<Mutex<Option<String>>> = LazyLock::new(|| Mutex::new(None)); static IS_PLATFORM_PACKAGE_CACHE: LazyLock<Mutex<IndexMap<String, bool>>> = LazyLock::new(|| Mutex::new(IndexMap::new())); @@ -153,7 +152,11 @@ impl PlatformRepository { .as_ref() .unwrap() .normalize(&pretty_version, None)?; - let mut composer = CompletePackage::new("composer".to_string(), version.clone(), pretty_version.clone()); + let mut composer = CompletePackage::new( + "composer".to_string(), + version.clone(), + pretty_version.clone(), + ); composer.set_description("Composer package".to_string()); self.add_package(Box::new(composer))?; @@ -163,8 +166,11 @@ impl PlatformRepository { .as_ref() .unwrap() .normalize(&pretty_version, None)?; - let mut composer_plugin_api = - CompletePackage::new("composer-plugin-api".to_string(), version.clone(), pretty_version.clone()); + let mut composer_plugin_api = CompletePackage::new( + "composer-plugin-api".to_string(), + version.clone(), + pretty_version.clone(), + ); composer_plugin_api.set_description("The Composer Plugin API".to_string()); self.add_package(Box::new(composer_plugin_api))?; @@ -174,8 +180,11 @@ impl PlatformRepository { .as_ref() .unwrap() .normalize(&pretty_version, None)?; - let mut composer_runtime_api = - CompletePackage::new("composer-runtime-api".to_string(), version.clone(), pretty_version.clone()); + let mut composer_runtime_api = CompletePackage::new( + "composer-runtime-api".to_string(), + version.clone(), + pretty_version.clone(), + ); composer_runtime_api.set_description("The Composer Runtime API".to_string()); self.add_package(Box::new(composer_runtime_api))?; @@ -195,8 +204,8 @@ impl PlatformRepository { version = v; } Err(_) => { - pretty_version = - Preg::replace("#^([^~+-]+).*$#", "$1", &php_version_str).unwrap_or(php_version_str); + pretty_version = Preg::replace("#^([^~+-]+).*$#", "$1", &php_version_str) + .unwrap_or(php_version_str); version = self .version_parser .as_ref() @@ -205,22 +214,38 @@ impl PlatformRepository { } } - let mut php = CompletePackage::new("php".to_string(), version.clone(), pretty_version.clone()); + let mut php = + CompletePackage::new("php".to_string(), version.clone(), pretty_version.clone()); php.set_description("The PHP interpreter".to_string()); self.add_package(Box::new(php))?; - if self.runtime.get_constant("PHP_DEBUG", None).as_bool().unwrap_or(false) { - let mut phpdebug = - CompletePackage::new("php-debug".to_string(), version.clone(), pretty_version.clone()); + if self + .runtime + .get_constant("PHP_DEBUG", None) + .as_bool() + .unwrap_or(false) + { + let mut phpdebug = CompletePackage::new( + "php-debug".to_string(), + version.clone(), + pretty_version.clone(), + ); phpdebug.set_description("The PHP interpreter, with debugging symbols".to_string()); self.add_package(Box::new(phpdebug))?; } if self.runtime.has_constant("PHP_ZTS", None) - && self.runtime.get_constant("PHP_ZTS", None).as_bool().unwrap_or(false) + && self + .runtime + .get_constant("PHP_ZTS", None) + .as_bool() + .unwrap_or(false) { - let mut phpzts = - CompletePackage::new("php-zts".to_string(), version.clone(), pretty_version.clone()); + let mut phpzts = CompletePackage::new( + "php-zts".to_string(), + version.clone(), + pretty_version.clone(), + ); phpzts.set_description("The PHP interpreter, with Zend Thread Safety".to_string()); self.add_package(Box::new(phpzts))?; } @@ -232,8 +257,11 @@ impl PlatformRepository { .map(|v| v == 8) .unwrap_or(false) { - let mut php64 = - CompletePackage::new("php-64bit".to_string(), version.clone(), pretty_version.clone()); + let mut php64 = CompletePackage::new( + "php-64bit".to_string(), + version.clone(), + pretty_version.clone(), + ); php64.set_description("The PHP interpreter, 64bit".to_string()); self.add_package(Box::new(php64))?; } @@ -254,8 +282,11 @@ impl PlatformRepository { }) .unwrap_or(PhpMixed::Bool(false)); if has_inet6 || !matches!(inet_pton_check, PhpMixed::Bool(false)) { - let mut php_ipv6 = - CompletePackage::new("php-ipv6".to_string(), version.clone(), pretty_version.clone()); + let mut php_ipv6 = CompletePackage::new( + "php-ipv6".to_string(), + version.clone(), + pretty_version.clone(), + ); php_ipv6.set_description("The PHP interpreter, with IPv6 support".to_string()); self.add_package(Box::new(php_ipv6))?; } @@ -376,16 +407,9 @@ impl PlatformRepository { .unwrap_or_default(); self.add_library( &mut libraries, - &format!( - "{}-openssl{}", - name, - if is_fips { "-fips" } else { "" } - ), + &format!("{}-openssl{}", name, if is_fips { "-fips" } else { "" }), Some(&parsed_version), - Some(&format!( - "curl OpenSSL version ({})", - parsed_version - )), + Some(&format!("curl OpenSSL version ({})", parsed_version)), &[], if is_fips { &["curl-openssl".to_string()] @@ -442,10 +466,9 @@ impl PlatformRepository { } // ZLib Version => 1.2.8 - if let Ok(Some(zlib_matches)) = Preg::is_match_strict_groups( - "{^ZLib Version => (?<version>.+)$}im", - &info, - ) { + if let Ok(Some(zlib_matches)) = + Preg::is_match_strict_groups("{^ZLib Version => (?<version>.+)$}im", &info) + { self.add_library( &mut libraries, &format!("{}-zlib", name), @@ -486,9 +509,7 @@ impl PlatformRepository { &info, ) { // If the timezonedb is provided by ext/timezonedb, register that version as a replacement - if external - && loaded_extensions.iter().any(|n| n == "timezonedb") - { + if external && loaded_extensions.iter().any(|n| n == "timezonedb") { self.add_library( &mut libraries, "timezonedb-zoneinfo", @@ -552,8 +573,8 @@ impl PlatformRepository { "/^libJPEG Version => (?<version>.+?)(?: compatible)?$/im", &info, ) { - let parsed = Version::parse_libjpeg(&libjpeg_matches["version"]) - .unwrap_or_default(); + let parsed = + Version::parse_libjpeg(&libjpeg_matches["version"]).unwrap_or_default(); self.add_library( &mut libraries, &format!("{}-libjpeg", name), @@ -596,8 +617,7 @@ impl PlatformRepository { "/^libXpm Version => (?<versionId>\\d+)$/im", &info, ) { - let version_id: i64 = - libxpm_matches["versionId"].parse().unwrap_or(0); + let version_id: i64 = libxpm_matches["versionId"].parse().unwrap_or(0); let converted = Version::convert_libxpm_version_id(version_id).unwrap_or_default(); self.add_library( @@ -649,8 +669,7 @@ impl PlatformRepository { let description = "The ICU unicode and globalization support library"; // Truthy check is for testing only so we can make the condition fail if self.runtime.has_constant("INTL_ICU_VERSION", None) { - let intl_icu_version = - self.runtime.get_constant("INTL_ICU_VERSION", None); + let intl_icu_version = self.runtime.get_constant("INTL_ICU_VERSION", None); let intl_icu_str = match &intl_icu_version { PhpMixed::String(s) => Some(s.clone()), _ => None, @@ -663,10 +682,9 @@ impl PlatformRepository { &[], &[], )?; - } else if let Ok(Some(matches)) = Preg::is_match_strict_groups( - "/^ICU version => (?<version>.+)$/im", - &info, - ) { + } else if let Ok(Some(matches)) = + Preg::is_match_strict_groups("/^ICU version => (?<version>.+)$/im", &info) + { self.add_library( &mut libraries, "icu", @@ -740,8 +758,7 @@ impl PlatformRepository { ])], ); let sliced = array_slice(&intl_char_versions, 0, Some(3)); - let joined = - implode(".", &Self::php_array_to_string_vec(&sliced)); + let joined = implode(".", &Self::php_array_to_string_vec(&sliced)); self.add_library( &mut libraries, "icu-unicode", @@ -754,10 +771,7 @@ impl PlatformRepository { } "imagick" => { - let image_magick_version = self.runtime.construct( - "Imagick", - Vec::new(), - )?; + let image_magick_version = self.runtime.construct("Imagick", Vec::new())?; // TODO(plugin): `->getVersion()` is a dynamic method call on Imagick let image_magick_version_str = Self::imagick_get_version_string(&image_magick_version); @@ -791,10 +805,7 @@ impl PlatformRepository { "/^Vendor Version => (?<versionId>\\d+)$/im", &info, ), - Preg::is_match_strict_groups( - "/^Vendor Name => (?<vendor>.+)$/im", - &info, - ), + Preg::is_match_strict_groups("/^Vendor Name => (?<vendor>.+)$/im", &info), ) { let version_id: i64 = matches["versionId"].parse().unwrap_or(0); let converted = @@ -1102,15 +1113,9 @@ impl PlatformRepository { let version_built = sprintf( "%d.%d.%d", &[ - PhpMixed::Int( - (lib_rd_kafka_version_int & 0x7F000000) >> 24, - ), - PhpMixed::Int( - (lib_rd_kafka_version_int & 0x00FF0000) >> 16, - ), - PhpMixed::Int( - (lib_rd_kafka_version_int & 0x0000FF00) >> 8, - ), + PhpMixed::Int((lib_rd_kafka_version_int & 0x7F000000) >> 24), + PhpMixed::Int((lib_rd_kafka_version_int & 0x00FF0000) >> 16), + PhpMixed::Int((lib_rd_kafka_version_int & 0x0000FF00) >> 8), ], ); self.add_library( @@ -1236,7 +1241,10 @@ impl PlatformRepository { } "zip" => { - if self.runtime.has_constant("LIBZIP_VERSION", Some("ZipArchive")) { + if self + .runtime + .has_constant("LIBZIP_VERSION", Some("ZipArchive")) + { let libzip = self .runtime .get_constant("LIBZIP_VERSION", Some("ZipArchive")); @@ -1308,8 +1316,8 @@ impl PlatformRepository { version = v; } Err(_) => { - pretty_version = - Preg::replace("#^([^~+-]+).*$#", "$1", &hhvm_version).unwrap_or(hhvm_version); + pretty_version = Preg::replace("#^([^~+-]+).*$#", "$1", &hhvm_version) + .unwrap_or(hhvm_version); version = self .version_parser .as_ref() @@ -1373,8 +1381,10 @@ impl PlatformRepository { name: self.overrides["php"].name.clone(), version: self.overrides["php"].version.clone(), }; - let mut overrider = - self.add_overridden_package(&php_override, Some(package.get_pretty_name().to_string()))?; + let mut overrider = self.add_overridden_package( + &php_override, + Some(package.get_pretty_name().to_string()), + )?; let actual_text = if package.get_version() == overrider.get_version() { "same as actual".to_string() } else { @@ -1457,8 +1467,7 @@ impl PlatformRepository { { Ok(v) => v, Err(_) => { - extra_description = - Some(format!(" (actual version: {})", pretty_version)); + extra_description = Some(format!(" (actual version: {})", pretty_version)); if let Ok(Some(m)) = Preg::is_match_strict_groups( "{^(\\d+\\.\\d+\\.\\d+(?:\\.\\d+)?)}", &pretty_version, @@ -1540,7 +1549,11 @@ impl PlatformRepository { .map(|s| s.to_string()) .unwrap_or_else(|| format!("The {} library", name)); - let mut lib = CompletePackage::new(format!("lib-{}", name), version.clone(), pretty_version.to_string()); + let mut lib = CompletePackage::new( + format!("lib-{}", name), + version.clone(), + pretty_version.to_string(), + ); lib.set_description(description); let mut replace_links: IndexMap<String, Link> = IndexMap::new(); diff --git a/crates/shirabe/src/repository/repository_factory.rs b/crates/shirabe/src/repository/repository_factory.rs index feb2f3d..794cd3f 100644 --- a/crates/shirabe/src/repository/repository_factory.rs +++ b/crates/shirabe/src/repository/repository_factory.rs @@ -2,7 +2,9 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_php_shim::{get_debug_type, json_encode, InvalidArgumentException, PhpMixed, UnexpectedValueException}; +use shirabe_php_shim::{ + InvalidArgumentException, PhpMixed, UnexpectedValueException, get_debug_type, json_encode, +}; use crate::config::Config; use crate::event_dispatcher::event_dispatcher::EventDispatcher; @@ -18,7 +20,12 @@ use crate::util::process_executor::ProcessExecutor; pub struct RepositoryFactory; impl RepositoryFactory { - pub fn config_from_string(io: &dyn IOInterface, config: &Config, repository: &str, allow_filesystem: bool) -> anyhow::Result<IndexMap<String, PhpMixed>> { + pub fn config_from_string( + io: &dyn IOInterface, + config: &Config, + repository: &str, + allow_filesystem: bool, + ) -> anyhow::Result<IndexMap<String, PhpMixed>> { if repository.starts_with("http") { let mut repo_config = IndexMap::new(); repo_config.insert("type".to_string(), PhpMixed::String("composer".to_string())); @@ -32,23 +39,35 @@ impl RepositoryFactory { .unwrap_or(""); if extension == "json" { - let json = JsonFile::new(repository.to_string(), Some(Factory::create_http_downloader(io, config)?)); + let json = JsonFile::new( + repository.to_string(), + Some(Factory::create_http_downloader(io, config)?), + ); let data = json.read()?; let has_packages = data.get("packages").map_or(false, |v| !v.is_null()); let has_includes = data.get("includes").map_or(false, |v| !v.is_null()); - let has_provider_includes = data.get("provider-includes").map_or(false, |v| !v.is_null()); + let has_provider_includes = data + .get("provider-includes") + .map_or(false, |v| !v.is_null()); if has_packages || has_includes || has_provider_includes { - let real_path = std::fs::canonicalize(repository).ok() + let real_path = std::fs::canonicalize(repository) + .ok() .and_then(|p| p.to_str().map(|s| s.to_string())) .unwrap_or_else(|| repository.to_string()) .replace('\\', "/"); let mut repo_config = IndexMap::new(); repo_config.insert("type".to_string(), PhpMixed::String("composer".to_string())); - repo_config.insert("url".to_string(), PhpMixed::String(format!("file://{}", real_path))); + repo_config.insert( + "url".to_string(), + PhpMixed::String(format!("file://{}", real_path)), + ); return Ok(repo_config); } else if allow_filesystem { let mut repo_config = IndexMap::new(); - repo_config.insert("type".to_string(), PhpMixed::String("filesystem".to_string())); + repo_config.insert( + "type".to_string(), + PhpMixed::String("filesystem".to_string()), + ); repo_config.insert("json".to_string(), PhpMixed::String(repository.to_string())); return Ok(repo_config); } else { @@ -70,12 +89,23 @@ impl RepositoryFactory { }.into()) } - pub fn from_string(io: &dyn IOInterface, config: &Config, repository: &str, allow_filesystem: bool, rm: Option<&mut RepositoryManager>) -> anyhow::Result<Box<dyn RepositoryInterface>> { + pub fn from_string( + io: &dyn IOInterface, + config: &Config, + repository: &str, + allow_filesystem: bool, + rm: Option<&mut RepositoryManager>, + ) -> anyhow::Result<Box<dyn RepositoryInterface>> { let repo_config = Self::config_from_string(io, config, repository, allow_filesystem)?; Self::create_repo(io, config, repo_config, rm) } - pub fn create_repo(io: &dyn IOInterface, config: &Config, repo_config: IndexMap<String, PhpMixed>, rm: Option<&mut RepositoryManager>) -> anyhow::Result<Box<dyn RepositoryInterface>> { + pub fn create_repo( + io: &dyn IOInterface, + config: &Config, + repo_config: IndexMap<String, PhpMixed>, + rm: Option<&mut RepositoryManager>, + ) -> anyhow::Result<Box<dyn RepositoryInterface>> { let mut owned_rm; let rm = if let Some(rm) = rm { rm @@ -83,13 +113,23 @@ impl RepositoryFactory { owned_rm = Self::manager(io, config, None, None, None)?; &mut owned_rm }; - let mut repos = Self::create_repos(rm, vec![PhpMixed::Array( - repo_config.into_iter().map(|(k, v)| (k, Box::new(v))).collect() - )])?; + let mut repos = Self::create_repos( + rm, + vec![PhpMixed::Array( + repo_config + .into_iter() + .map(|(k, v)| (k, Box::new(v))) + .collect(), + )], + )?; Ok(repos.remove(0)) } - pub fn default_repos(io: Option<&dyn IOInterface>, config: Option<Config>, rm: Option<&mut RepositoryManager>) -> anyhow::Result<Vec<Box<dyn RepositoryInterface>>> { + pub fn default_repos( + io: Option<&dyn IOInterface>, + config: Option<Config>, + rm: Option<&mut RepositoryManager>, + ) -> anyhow::Result<Vec<Box<dyn RepositoryInterface>>> { let config = match config { Some(c) => c, None => Factory::create_config(None, None)?, @@ -103,10 +143,17 @@ impl RepositoryFactory { rm } else { let io = io.ok_or_else(|| InvalidArgumentException { - message: "This function requires either an IOInterface or a RepositoryManager".to_string(), + message: "This function requires either an IOInterface or a RepositoryManager" + .to_string(), code: 0, })?; - owned_rm = Self::manager(io, &config, Some(Factory::create_http_downloader(io, &config)?), None, None)?; + owned_rm = Self::manager( + io, + &config, + Some(Factory::create_http_downloader(io, &config)?), + None, + None, + )?; &mut owned_rm }; @@ -114,7 +161,13 @@ impl RepositoryFactory { Self::create_repos(rm, repo_configs) } - pub fn manager(io: &dyn IOInterface, config: &Config, http_downloader: Option<HttpDownloader>, event_dispatcher: Option<EventDispatcher>, process: Option<ProcessExecutor>) -> anyhow::Result<RepositoryManager> { + pub fn manager( + io: &dyn IOInterface, + config: &Config, + http_downloader: Option<HttpDownloader>, + event_dispatcher: Option<EventDispatcher>, + process: Option<ProcessExecutor>, + ) -> anyhow::Result<RepositoryManager> { let http_downloader = match http_downloader { Some(h) => h, None => Factory::create_http_downloader(io, config)?, @@ -148,14 +201,19 @@ impl RepositoryFactory { Ok(rm) } - pub fn default_repos_with_default_manager(io: &dyn IOInterface) -> anyhow::Result<Vec<Box<dyn RepositoryInterface>>> { + pub fn default_repos_with_default_manager( + io: &dyn IOInterface, + ) -> anyhow::Result<Vec<Box<dyn RepositoryInterface>>> { let config = Factory::create_config(Some(io), None)?; let mut manager = Self::manager(io, &config, None, None, None)?; io.load_configuration(&config); Self::default_repos(Some(io), Some(config), Some(&mut manager)) } - fn create_repos(rm: &mut RepositoryManager, repo_configs: Vec<PhpMixed>) -> anyhow::Result<Vec<Box<dyn RepositoryInterface>>> { + fn create_repos( + rm: &mut RepositoryManager, + repo_configs: Vec<PhpMixed>, + ) -> anyhow::Result<Vec<Box<dyn RepositoryInterface>>> { let mut repo_map: IndexMap<String, Box<dyn RepositoryInterface>> = IndexMap::new(); for (index, repo) in repo_configs.into_iter().enumerate() { @@ -169,27 +227,51 @@ impl RepositoryFactory { PhpMixed::Array(repo_arr) => { if !repo_arr.contains_key("type") { return Err(UnexpectedValueException { - message: format!("Repository \"{}\" ({}) must have a type defined", index, json_encode(&repo).unwrap_or_default()), + message: format!( + "Repository \"{}\" ({}) must have a type defined", + index, + json_encode(&repo).unwrap_or_default() + ), code: 0, - }.into()); + } + .into()); } - let repo_type = repo_arr.get("type").and_then(|v| v.as_string()).unwrap_or("").to_string(); - let repo_config_map: IndexMap<String, PhpMixed> = repo_arr.iter().map(|(k, v)| (k.clone(), *v.clone())).collect(); - let name = Self::generate_repository_name_indexed(index, &repo_config_map, &repo_map); + let repo_type = repo_arr + .get("type") + .and_then(|v| v.as_string()) + .unwrap_or("") + .to_string(); + let repo_config_map: IndexMap<String, PhpMixed> = repo_arr + .iter() + .map(|(k, v)| (k.clone(), *v.clone())) + .collect(); + let name = + Self::generate_repository_name_indexed(index, &repo_config_map, &repo_map); if repo_type == "filesystem" { - let json_path = repo_arr.get("json").and_then(|v| v.as_string()).unwrap_or("").to_string(); + let json_path = repo_arr + .get("json") + .and_then(|v| v.as_string()) + .unwrap_or("") + .to_string(); repo_map.insert(name, Box::new(FilesystemRepository::new(json_path)?)); } else { - let created = rm.create_repository(&repo_type, repo_config_map, &index.to_string())?; + let created = + rm.create_repository(&repo_type, repo_config_map, &index.to_string())?; repo_map.insert(name, created); } } _ => { return Err(UnexpectedValueException { - message: format!("Repository \"{}\" ({}) should be an array, {} given", index, json_encode(&repo).unwrap_or_default(), get_debug_type(&repo)), + message: format!( + "Repository \"{}\" ({}) should be an array, {} given", + index, + json_encode(&repo).unwrap_or_default(), + get_debug_type(&repo) + ), code: 0, - }.into()); + } + .into()); } } } @@ -197,7 +279,11 @@ impl RepositoryFactory { Ok(repo_map.into_values().collect()) } - pub fn generate_repository_name(index: &PhpMixed, repo: &IndexMap<String, PhpMixed>, existing_repos: &IndexMap<String, Box<dyn RepositoryInterface>>) -> String { + pub fn generate_repository_name( + index: &PhpMixed, + repo: &IndexMap<String, PhpMixed>, + existing_repos: &IndexMap<String, Box<dyn RepositoryInterface>>, + ) -> String { let mut name = match index { PhpMixed::Int(_) => { if let Some(url) = repo.get("url").and_then(|v| v.as_string()) { @@ -214,7 +300,11 @@ impl RepositoryFactory { name } - fn generate_repository_name_indexed(index: usize, repo: &IndexMap<String, PhpMixed>, existing_repos: &IndexMap<String, Box<dyn RepositoryInterface>>) -> String { + fn generate_repository_name_indexed( + index: usize, + repo: &IndexMap<String, PhpMixed>, + existing_repos: &IndexMap<String, Box<dyn RepositoryInterface>>, + ) -> String { let mut name = if let Some(url) = repo.get("url").and_then(|v| v.as_string()) { Preg::replace("{^https?://}i", "", url, -1).unwrap_or_else(|_| url.to_string()) } else { diff --git a/crates/shirabe/src/repository/repository_interface.rs b/crates/shirabe/src/repository/repository_interface.rs index f284afd..15c85f0 100644 --- a/crates/shirabe/src/repository/repository_interface.rs +++ b/crates/shirabe/src/repository/repository_interface.rs @@ -1,11 +1,11 @@ //! ref: composer/src/Composer/Repository/RepositoryInterface.php -use indexmap::IndexMap; -use shirabe_php_shim::Countable; -use shirabe_semver::constraint::constraint_interface::ConstraintInterface; use crate::package::base_package::BasePackage; use crate::package::package_interface::PackageInterface; use crate::repository::advisory_provider_interface::AdvisoryProviderInterface; +use indexmap::IndexMap; +use shirabe_php_shim::Countable; +use shirabe_semver::constraint::constraint_interface::ConstraintInterface; pub enum FindPackageConstraint { String(String), @@ -42,9 +42,17 @@ pub trait RepositoryInterface: Countable { fn has_package(&self, package: &dyn PackageInterface) -> bool; - fn find_package(&self, name: String, constraint: FindPackageConstraint) -> Option<Box<BasePackage>>; + fn find_package( + &self, + name: String, + constraint: FindPackageConstraint, + ) -> Option<Box<BasePackage>>; - fn find_packages(&self, name: String, constraint: Option<FindPackageConstraint>) -> Vec<Box<BasePackage>>; + fn find_packages( + &self, + name: String, + constraint: Option<FindPackageConstraint>, + ) -> Vec<Box<BasePackage>>; fn get_packages(&self) -> Vec<Box<BasePackage>>; diff --git a/crates/shirabe/src/repository/repository_manager.rs b/crates/shirabe/src/repository/repository_manager.rs index fd60e7b..cc43aed 100644 --- a/crates/shirabe/src/repository/repository_manager.rs +++ b/crates/shirabe/src/repository/repository_manager.rs @@ -1,7 +1,7 @@ //! ref: composer/src/Composer/Repository/RepositoryManager.php use indexmap::IndexMap; -use shirabe_php_shim::{json_encode, InvalidArgumentException, PhpMixed}; +use shirabe_php_shim::{InvalidArgumentException, PhpMixed, json_encode}; use shirabe_semver::constraint::constraint_interface::ConstraintInterface; use crate::config::Config; @@ -26,7 +26,13 @@ pub struct RepositoryManager { } impl RepositoryManager { - pub fn new(io: &dyn IOInterface, config: &Config, http_downloader: HttpDownloader, event_dispatcher: Option<EventDispatcher>, process: Option<ProcessExecutor>) -> Self { + pub fn new( + io: &dyn IOInterface, + config: &Config, + http_downloader: HttpDownloader, + event_dispatcher: Option<EventDispatcher>, + process: Option<ProcessExecutor>, + ) -> Self { let process = process.unwrap_or_else(|| ProcessExecutor::new(io)); Self { local_repository: None, @@ -40,7 +46,11 @@ impl RepositoryManager { } } - pub fn find_package(&self, name: &str, constraint: &dyn ConstraintInterface) -> Option<Box<dyn PackageInterface>> { + pub fn find_package( + &self, + name: &str, + constraint: &dyn ConstraintInterface, + ) -> Option<Box<dyn PackageInterface>> { for repository in &self.repositories { if let Some(package) = repository.find_package(name, constraint) { return Some(package); @@ -49,7 +59,11 @@ impl RepositoryManager { None } - pub fn find_packages(&self, name: &str, constraint: &dyn ConstraintInterface) -> Vec<Box<dyn PackageInterface>> { + pub fn find_packages( + &self, + name: &str, + constraint: &dyn ConstraintInterface, + ) -> Vec<Box<dyn PackageInterface>> { let mut packages: Vec<Box<dyn PackageInterface>> = vec![]; for repository in self.get_repositories() { packages.extend(repository.find_packages(name, constraint)); @@ -65,23 +79,41 @@ impl RepositoryManager { self.repositories.insert(0, repository); } - pub fn create_repository(&self, r#type: &str, config: IndexMap<String, PhpMixed>, name: Option<&str>) -> anyhow::Result<Box<dyn RepositoryInterface>> { + pub fn create_repository( + &self, + r#type: &str, + config: IndexMap<String, PhpMixed>, + name: Option<&str>, + ) -> anyhow::Result<Box<dyn RepositoryInterface>> { if !self.repository_classes.contains_key(r#type) { return Err(InvalidArgumentException { message: format!("Repository type is not registered: {}", r#type), code: 0, - }.into()); + } + .into()); } if config.get("packagist").and_then(|v| v.as_bool()) == Some(false) { - let config_json = json_encode(&PhpMixed::Array(config.iter().map(|(k, v)| (k.clone(), Box::new(v.clone()))).collect())).unwrap_or_default(); + let config_json = json_encode(&PhpMixed::Array( + config + .iter() + .map(|(k, v)| (k.clone(), Box::new(v.clone()))) + .collect(), + )) + .unwrap_or_default(); self.io.write_error(&format!("<warning>Repository \"{}\" ({}) has a packagist key which should be in its own repository definition</warning>", name.unwrap_or(""), config_json)); } let class = self.repository_classes[r#type].clone(); - let has_filter = config.contains_key("only") || config.contains_key("exclude") || config.contains_key("canonical"); - let filter_config = if has_filter { Some(config.clone()) } else { None }; + let has_filter = config.contains_key("only") + || config.contains_key("exclude") + || config.contains_key("canonical"); + let filter_config = if has_filter { + Some(config.clone()) + } else { + None + }; let mut cleaned_config = config; cleaned_config.remove("only"); @@ -98,12 +130,17 @@ impl RepositoryManager { Ok(repository) } - fn create_repository_by_class(&self, _class: &str, _config: IndexMap<String, PhpMixed>) -> anyhow::Result<Box<dyn RepositoryInterface>> { + fn create_repository_by_class( + &self, + _class: &str, + _config: IndexMap<String, PhpMixed>, + ) -> anyhow::Result<Box<dyn RepositoryInterface>> { todo!("Phase B: dynamic class instantiation by class name") } pub fn set_repository_class(&mut self, r#type: &str, class: &str) { - self.repository_classes.insert(r#type.to_string(), class.to_string()); + self.repository_classes + .insert(r#type.to_string(), class.to_string()); } pub fn get_repositories(&self) -> &Vec<Box<dyn RepositoryInterface>> { diff --git a/crates/shirabe/src/repository/repository_set.rs b/crates/shirabe/src/repository/repository_set.rs index 7134ed7..e25bd29 100644 --- a/crates/shirabe/src/repository/repository_set.rs +++ b/crates/shirabe/src/repository/repository_set.rs @@ -5,8 +5,8 @@ use std::any::Any; use anyhow::Result; use indexmap::IndexMap; use shirabe_php_shim::{ - array_merge, array_merge_recursive, ksort, strtolower, LogicException, PhpMixed, - RuntimeException, + LogicException, PhpMixed, RuntimeException, array_merge, array_merge_recursive, ksort, + strtolower, }; use shirabe_semver::constraint::constraint::Constraint; use shirabe_semver::constraint::constraint_interface::ConstraintInterface; @@ -184,7 +184,11 @@ impl RepositorySet { (repo.as_any() as &dyn Any).downcast_ref::<CompositeRepository>() { // TODO(phase-b): clone composite.get_repositories() — Box<dyn RepositoryInterface> cloning - composite.get_repositories().iter().map(|r| r.clone_box()).collect() + composite + .get_repositories() + .iter() + .map(|r| r.clone_box()) + .collect() } else { vec![repo] }; @@ -479,7 +483,8 @@ impl RepositorySet { .is_some(); if is_installed && !self.allow_installed_repositories { return Err(LogicException { - message: "The pool can not accept packages from an installed repository".to_string(), + message: "The pool can not accept packages from an installed repository" + .to_string(), code: 0, } .into()); @@ -503,7 +508,8 @@ impl RepositorySet { .is_some(); if is_installed && !self.allow_installed_repositories { return Err(LogicException { - message: "The pool can not accept packages from an installed repository".to_string(), + message: "The pool can not accept packages from an installed repository" + .to_string(), code: 0, } .into()); @@ -548,7 +554,14 @@ impl RepositorySet { } // TODO(phase-b): Pool::new signature - Ok(Pool::new(packages, vec![], IndexMap::new(), IndexMap::new(), IndexMap::new(), IndexMap::new())) + Ok(Pool::new( + packages, + vec![], + IndexMap::new(), + IndexMap::new(), + IndexMap::new(), + IndexMap::new(), + )) } pub fn create_pool_for_package( @@ -587,7 +600,15 @@ impl RepositorySet { request.restrict_packages(allowed_packages); } - self.create_pool(request, Box::new(NullIO::new()), None, None, vec![], None, None) + self.create_pool( + request, + Box::new(NullIO::new()), + None, + None, + vec![], + None, + None, + ) } /// @param array[] $aliases diff --git a/crates/shirabe/src/repository/repository_utils.rs b/crates/shirabe/src/repository/repository_utils.rs index 1ca98ee..526fae7 100644 --- a/crates/shirabe/src/repository/repository_utils.rs +++ b/crates/shirabe/src/repository/repository_utils.rs @@ -1,12 +1,12 @@ //! ref: composer/src/Composer/Repository/RepositoryUtils.php -use std::any::Any; -use indexmap::IndexMap; use crate::package::link::Link; use crate::package::package_interface::PackageInterface; use crate::repository::composite_repository::CompositeRepository; use crate::repository::filter_repository::FilterRepository; use crate::repository::repository_interface::RepositoryInterface; +use indexmap::IndexMap; +use std::any::Any; pub struct RepositoryUtils; @@ -33,7 +33,12 @@ impl RepositoryUtils { }); if !already_in_bucket { bucket.push(candidate.clone_box()); - bucket = Self::filter_required_packages(packages, candidate.as_ref(), false, bucket); + bucket = Self::filter_required_packages( + packages, + candidate.as_ref(), + false, + bucket, + ); } break; } @@ -48,7 +53,9 @@ impl RepositoryUtils { unwrap_filter_repos: bool, ) -> Vec<Box<dyn RepositoryInterface>> { let repo: Box<dyn RepositoryInterface> = if unwrap_filter_repos { - if let Some(filter_repo) = (repo.as_any() as &dyn Any).downcast_ref::<FilterRepository>() { + if let Some(filter_repo) = + (repo.as_any() as &dyn Any).downcast_ref::<FilterRepository>() + { filter_repo.get_repository() } else { repo @@ -57,7 +64,9 @@ impl RepositoryUtils { repo }; - if let Some(composite_repo) = (repo.as_any() as &dyn Any).downcast_ref::<CompositeRepository>() { + if let Some(composite_repo) = + (repo.as_any() as &dyn Any).downcast_ref::<CompositeRepository>() + { let mut repos = Vec::new(); for r in composite_repo.get_repositories() { for r2 in Self::flatten_repositories(r, unwrap_filter_repos) { diff --git a/crates/shirabe/src/repository/vcs/forgejo_driver.rs b/crates/shirabe/src/repository/vcs/forgejo_driver.rs index 5d482b2..74f1e11 100644 --- a/crates/shirabe/src/repository/vcs/forgejo_driver.rs +++ b/crates/shirabe/src/repository/vcs/forgejo_driver.rs @@ -4,7 +4,7 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - base64_decode, extension_loaded, explode, urlencode, PhpMixed, RuntimeException, + PhpMixed, RuntimeException, base64_decode, explode, extension_loaded, urlencode, }; use crate::cache::Cache; @@ -215,8 +215,7 @@ impl ForgejoDriver { if self.branches.is_none() { let mut branches = IndexMap::new(); let api_url = self.forgejo_url.as_ref().unwrap().api_url.clone(); - let mut resource: Option<String> = - Some(format!("{}/branches?per_page=100", api_url)); + let mut resource: Option<String> = Some(format!("{}/branches?per_page=100", api_url)); while let Some(url) = resource { let response = self @@ -259,8 +258,7 @@ impl ForgejoDriver { if self.tags.is_none() { let mut tags = IndexMap::new(); let api_url = self.forgejo_url.as_ref().unwrap().api_url.clone(); - let mut resource: Option<String> = - Some(format!("{}/tags?per_page=100", api_url)); + let mut resource: Option<String> = Some(format!("{}/tags?per_page=100", api_url)); while let Some(url) = resource { let response = self @@ -330,7 +328,10 @@ impl ForgejoDriver { shirabe_php_shim::JSON_UNESCAPED_UNICODE | shirabe_php_shim::JSON_UNESCAPED_SLASHES, ); - self.inner.cache.as_ref().map(|c| c.write(identifier, &encoded)); + self.inner + .cache + .as_ref() + .map(|c| c.write(identifier, &encoded)); } } c @@ -347,8 +348,7 @@ impl ForgejoDriver { .get("support") .map_or(false, |v| v.as_array().is_none()); if support_not_array { - composer_map - .insert("support".to_string(), PhpMixed::Array(IndexMap::new())); + composer_map.insert("support".to_string(), PhpMixed::Array(IndexMap::new())); } let has_source = composer_map @@ -366,28 +366,26 @@ impl ForgejoDriver { let tags = self.get_tags()?; let branches = self.get_branches()?; - let source_url = if let Some(label) = - tags.into_iter().find(|(_, v)| v == identifier).map(|(k, _)| k) + let source_url = if let Some(label) = tags + .into_iter() + .find(|(_, v)| v == identifier) + .map(|(k, _)| k) { format!("{}/tag/{}", html_url, label) - } else if let Some(label) = - branches - .into_iter() - .find(|(_, v)| v == identifier) - .map(|(k, _)| k) + } else if let Some(label) = branches + .into_iter() + .find(|(_, v)| v == identifier) + .map(|(k, _)| k) { format!("{}/branch/{}", html_url, label) } else { format!("{}/commit/{}", html_url, identifier) }; - if let Some(PhpMixed::Array(ref mut support)) = - composer_map.get_mut("support") + if let Some(PhpMixed::Array(ref mut support)) = composer_map.get_mut("support") { - support.insert( - "source".to_string(), - Box::new(PhpMixed::String(source_url)), - ); + support + .insert("source".to_string(), Box::new(PhpMixed::String(source_url))); } } @@ -409,13 +407,10 @@ impl ForgejoDriver { .map(|r| r.html_url.clone()) .unwrap_or_default() ); - if let Some(PhpMixed::Array(ref mut support)) = - composer_map.get_mut("support") + if let Some(PhpMixed::Array(ref mut support)) = composer_map.get_mut("support") { - support.insert( - "issues".to_string(), - Box::new(PhpMixed::String(issues_url)), - ); + support + .insert("issues".to_string(), Box::new(PhpMixed::String(issues_url))); } } @@ -429,10 +424,16 @@ impl ForgejoDriver { } } - self.inner.info_cache.insert(identifier.to_string(), composer); + self.inner + .info_cache + .insert(identifier.to_string(), composer); } - Ok(self.inner.info_cache.get(identifier).and_then(|v| v.clone())) + Ok(self + .inner + .info_cache + .get(identifier) + .and_then(|v| v.clone())) } pub fn get_source(&mut self, identifier: &str) -> IndexMap<String, String> { @@ -470,8 +471,9 @@ impl ForgejoDriver { let forgejo_domains = config.get("forgejo-domains"); let in_domains = if let Some(list) = forgejo_domains.as_list() { list.iter().any(|d| { - d.as_string() - .map_or(false, |s| s.to_lowercase() == forgejo_url.origin_url.to_lowercase()) + d.as_string().map_or(false, |s| { + s.to_lowercase() == forgejo_url.origin_url.to_lowercase() + }) }) } else { false @@ -570,82 +572,73 @@ impl ForgejoDriver { ) -> anyhow::Result<Response, TransportException> { match self.inner.get_contents(url) { Ok(response) => Ok(response), - Err(e) => { - match e.get_code() { - 401 | 403 | 404 | 429 => { - if !fetching_repo_data { - return Err(e); - } + Err(e) => match e.get_code() { + 401 | 403 | 404 | 429 => { + if !fetching_repo_data { + return Err(e); + } - if !self.inner.io.is_interactive() { - self.attempt_clone_fallback() - .map_err(|inner_e| TransportException { - message: inner_e.to_string(), - code: 0, - headers: None, - response: None, - status_code: None, - response_info: vec![], - })?; + if !self.inner.io.is_interactive() { + self.attempt_clone_fallback() + .map_err(|inner_e| TransportException { + message: inner_e.to_string(), + code: 0, + headers: None, + response: None, + status_code: None, + response_info: vec![], + })?; - return Ok(Response::new( - { - let mut m = IndexMap::new(); - m.insert( - "url".to_string(), - PhpMixed::String("dummy".to_string()), - ); - m - }, - Some(200), - vec![], - Some("null".to_string()), - ) - .unwrap() - .unwrap()); - } + return Ok(Response::new( + { + let mut m = IndexMap::new(); + m.insert("url".to_string(), PhpMixed::String("dummy".to_string())); + m + }, + Some(200), + vec![], + Some("null".to_string()), + ) + .unwrap() + .unwrap()); + } - if !self.inner.io.has_authentication(&self.inner.origin_url) { - let origin_url = - self.forgejo_url.as_ref().unwrap().origin_url.clone(); - let message = if e.get_code() == 429 { - Some(format!( - "API limit exhausted. Enter your Forgejo credentials to get a larger API limit (<info>{}</info>)", - self.inner.url - )) - } else { - None - }; + if !self.inner.io.has_authentication(&self.inner.origin_url) { + let origin_url = self.forgejo_url.as_ref().unwrap().origin_url.clone(); + let message = if e.get_code() == 429 { + Some(format!( + "API limit exhausted. Enter your Forgejo credentials to get a larger API limit (<info>{}</info>)", + self.inner.url + )) + } else { + None + }; - let mut forgejo = Forgejo::new( - todo!("clone io for Forgejo OAuth"), - self.inner.config.clone(), - self.inner.http_downloader.clone(), - ); - let auth_result = forgejo - .authorize_o_auth_interactively( - &origin_url, - message.as_deref(), - ) - .map_err(|inner_e| TransportException { - message: inner_e.to_string(), - code: 0, - headers: None, - response: None, - status_code: None, - response_info: vec![], - })?; + let mut forgejo = Forgejo::new( + todo!("clone io for Forgejo OAuth"), + self.inner.config.clone(), + self.inner.http_downloader.clone(), + ); + let auth_result = forgejo + .authorize_o_auth_interactively(&origin_url, message.as_deref()) + .map_err(|inner_e| TransportException { + message: inner_e.to_string(), + code: 0, + headers: None, + response: None, + status_code: None, + response_info: vec![], + })?; - if let Ok(true) = auth_result { - return self.inner.get_contents(url); - } + if let Ok(true) = auth_result { + return self.inner.get_contents(url); } - - Err(e) } - _ => Err(e), + + Err(e) } - } + _ => Err(e), + }, } } diff --git a/crates/shirabe/src/repository/vcs/fossil_driver.rs b/crates/shirabe/src/repository/vcs/fossil_driver.rs index da81548..6e99f5f 100644 --- a/crates/shirabe/src/repository/vcs/fossil_driver.rs +++ b/crates/shirabe/src/repository/vcs/fossil_driver.rs @@ -3,7 +3,7 @@ use chrono::{DateTime, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_php_shim::{dirname, is_dir, is_file, is_writable, PhpMixed, RuntimeException}; +use shirabe_php_shim::{PhpMixed, RuntimeException, dirname, is_dir, is_file, is_writable}; use crate::cache::Cache; use crate::config::Config; @@ -28,15 +28,29 @@ impl FossilDriver { self.check_fossil()?; // Ensure we are allowed to use this URL by config. - self.inner.config.prohibit_url_by_config(&self.inner.url, &*self.inner.io)?; + self.inner + .config + .prohibit_url_by_config(&self.inner.url, &*self.inner.io)?; // Only if url points to a locally accessible directory, assume it's the checkout directory. // Otherwise, it should be something fossil can clone from. if Filesystem::is_local_path(&self.inner.url) && is_dir(&self.inner.url) { self.checkout_dir = self.inner.url.clone(); } else { - let cache_repo_dir = self.inner.config.get("cache-repo-dir").as_string().unwrap_or("").to_string(); - let cache_vcs_dir = self.inner.config.get("cache-vcs-dir").as_string().unwrap_or("").to_string(); + let cache_repo_dir = self + .inner + .config + .get("cache-repo-dir") + .as_string() + .unwrap_or("") + .to_string(); + let cache_vcs_dir = self + .inner + .config + .get("cache-vcs-dir") + .as_string() + .unwrap_or("") + .to_string(); if !Cache::is_usable(&cache_repo_dir) || !Cache::is_usable(&cache_vcs_dir) { return Err(RuntimeException { message: "FossilDriver requires a usable cache directory, and it looks like you set it to be disabled".to_string(), diff --git a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs index 495b427..dc3b5f3 100644 --- a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs +++ b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs @@ -5,9 +5,9 @@ use chrono::{DateTime, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - array_key_exists, array_search_mixed, extension_loaded, http_build_query_mixed, implode, - in_array, is_array, sprintf, strpos, InvalidArgumentException, LogicException, PhpMixed, - RuntimeException, + InvalidArgumentException, LogicException, PhpMixed, RuntimeException, array_key_exists, + array_search_mixed, extension_loaded, http_build_query_mixed, implode, in_array, is_array, + sprintf, strpos, }; use crate::cache::Cache; @@ -94,11 +94,13 @@ impl GitBitbucketDriver { ), None, )); - self.inner - .cache - .as_mut() - .unwrap() - .set_read_only(self.inner.config.get("cache-read-only").as_bool().unwrap_or(false)); + self.inner.cache.as_mut().unwrap().set_read_only( + self.inner + .config + .get("cache-read-only") + .as_bool() + .unwrap_or(false), + ); Ok(()) } @@ -153,7 +155,11 @@ impl GitBitbucketDriver { self.parse_clone_urls(clone_links); self.has_issues = !shirabe_php_shim::empty( - repo_data.get("has_issues").cloned().as_ref().unwrap_or(&PhpMixed::Null), + repo_data + .get("has_issues") + .cloned() + .as_ref() + .unwrap_or(&PhpMixed::Null), ); self.branches_url = repo_data .get("links") @@ -214,21 +220,19 @@ impl GitBitbucketDriver { if !self.inner.info_cache.contains_key(identifier) { let mut composer: Option<IndexMap<String, PhpMixed>> = None; - if self.inner.should_cache(identifier) - && { - let res = self - .inner - .cache - .as_ref() - .and_then(|c| c.read(identifier).ok().flatten()); - if let Some(res) = res { - composer = Some(JsonFile::parse_json(&res, None)?); - true - } else { - false - } + if self.inner.should_cache(identifier) && { + let res = self + .inner + .cache + .as_ref() + .and_then(|c| c.read(identifier).ok().flatten()); + if let Some(res) = res { + composer = Some(JsonFile::parse_json(&res, None)?); + true + } else { + false } - { + } { // composer already set above } else { composer = self.inner.get_base_composer_information(identifier)?; @@ -258,10 +262,7 @@ impl GitBitbucketDriver { if composer_map.contains_key("support") && !is_array(composer_map.get("support").unwrap()) { - composer_map.insert( - "support".to_string(), - PhpMixed::Array(IndexMap::new()), - ); + composer_map.insert("support".to_string(), PhpMixed::Array(IndexMap::new())); } let support_has_source = composer_map .get("support") @@ -379,18 +380,16 @@ impl GitBitbucketDriver { composer = Some(composer_map); } - self.inner.info_cache.insert(identifier.to_string(), composer); + self.inner + .info_cache + .insert(identifier.to_string(), composer); } Ok(self.inner.info_cache.get(identifier).cloned().flatten()) } /// @inheritDoc - pub fn get_file_content( - &mut self, - file: &str, - identifier: &str, - ) -> Result<Option<String>> { + pub fn get_file_content(&mut self, file: &str, identifier: &str) -> Result<Option<String>> { if let Some(fallback) = self.fallback_driver.as_mut() { return fallback.get_file_content(file, identifier); } @@ -446,10 +445,7 @@ impl GitBitbucketDriver { .decode_json()?; // TODO(phase-b): port PHP `new \DateTimeImmutable($commit['date'])` - let date_str = commit - .get("date") - .and_then(|v| v.as_string()) - .unwrap_or(""); + let date_str = commit.get("date").and_then(|v| v.as_string()).unwrap_or(""); let date: DateTime<Utc> = chrono::DateTime::parse_from_rfc3339(date_str) .map_err(|e| anyhow::anyhow!(e))? .with_timezone(&Utc); @@ -513,9 +509,7 @@ impl GitBitbucketDriver { m.insert("pagelen".to_string(), PhpMixed::Int(100)); m.insert( "fields".to_string(), - PhpMixed::String( - "values.name,values.target.hash,next".to_string(), - ), + PhpMixed::String("values.name,values.target.hash,next".to_string()), ); m.insert( "sort".to_string(), @@ -556,7 +550,11 @@ impl GitBitbucketDriver { } } if shirabe_php_shim::empty( - tags_data.get("next").cloned().as_ref().unwrap_or(&PhpMixed::Null), + tags_data + .get("next") + .cloned() + .as_ref() + .unwrap_or(&PhpMixed::Null), ) { has_next = false; } else { @@ -593,8 +591,7 @@ impl GitBitbucketDriver { m.insert( "fields".to_string(), PhpMixed::String( - "values.name,values.target.hash,values.heads,next" - .to_string(), + "values.name,values.target.hash,values.heads,next".to_string(), ), ); m.insert( @@ -636,7 +633,11 @@ impl GitBitbucketDriver { } } if shirabe_php_shim::empty( - branch_data.get("next").cloned().as_ref().unwrap_or(&PhpMixed::Null), + branch_data + .get("next") + .cloned() + .as_ref() + .unwrap_or(&PhpMixed::Null), ) { has_next = false; } else { @@ -684,7 +685,9 @@ impl GitBitbucketDriver { true, ); if in_set - || (401 == code && strpos(te.get_message(), "Could not authenticate against") == Some(0)) + || (401 == code + && strpos(te.get_message(), "Could not authenticate against") + == Some(0)) { if !self.inner.io.has_authentication(&self.inner.origin_url) && bitbucket_util.authorize_oauth(&self.inner.origin_url) @@ -696,11 +699,14 @@ impl GitBitbucketDriver { self.attempt_clone_fallback()?; let mut headers: IndexMap<String, PhpMixed> = IndexMap::new(); - headers.insert( - "url".to_string(), - PhpMixed::String("dummy".to_string()), - ); - return Ok(Response::new(headers, 200, IndexMap::new(), "null".to_string())); + headers + .insert("url".to_string(), PhpMixed::String("dummy".to_string())); + return Ok(Response::new( + headers, + 200, + IndexMap::new(), + "null".to_string(), + )); } } } @@ -785,9 +791,8 @@ impl GitBitbucketDriver { if !self.get_repo_data()? { if self.fallback_driver.is_none() { return Err(LogicException { - message: - "A fallback driver should be setup if getRepoData returns false" - .to_string(), + message: "A fallback driver should be setup if getRepoData returns false" + .to_string(), code: 0, } .into()); @@ -823,12 +828,7 @@ impl GitBitbucketDriver { } /// @inheritDoc - pub fn supports( - io: &dyn IOInterface, - _config: &Config, - url: &str, - _deep: bool, - ) -> bool { + pub fn supports(io: &dyn IOInterface, _config: &Config, url: &str, _deep: bool) -> bool { if !Preg::is_match( r"#^https?://bitbucket\.org/([^/]+)/([^/]+?)(\.git|/?)?$#i", url, diff --git a/crates/shirabe/src/repository/vcs/git_driver.rs b/crates/shirabe/src/repository/vcs/git_driver.rs index 5e2e547..0f8f841 100644 --- a/crates/shirabe/src/repository/vcs/git_driver.rs +++ b/crates/shirabe/src/repository/vcs/git_driver.rs @@ -5,8 +5,8 @@ use chrono::{DateTime, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - dirname, is_dir, is_writable, realpath, sys_get_temp_dir, InvalidArgumentException, - RuntimeException, + InvalidArgumentException, RuntimeException, dirname, is_dir, is_writable, realpath, + sys_get_temp_dir, }; use crate::cache::Cache; @@ -31,8 +31,7 @@ impl GitDriver { pub fn initialize(&mut self) -> anyhow::Result<()> { let cache_url; if Filesystem::is_local_path(&self.inner.url) { - self.inner.url = - Preg::replace(r"{[\\/]\.git/?$}", "", self.inner.url.clone())?; + self.inner.url = Preg::replace(r"{[\\/]\.git/?$}", "", self.inner.url.clone())?; if !is_dir(&self.inner.url) { return Err(RuntimeException { message: format!( @@ -64,11 +63,7 @@ impl GitDriver { self.repo_dir = format!( "{}/{}/", cache_vcs_dir, - Preg::replace( - r"{[^a-z0-9.]}i", - "-", - Url::sanitize(self.inner.url.clone()) - )? + Preg::replace(r"{[^a-z0-9.]}i", "-", Url::sanitize(self.inner.url.clone()))? ); GitUtil::clean_env(&self.inner.process); @@ -88,9 +83,7 @@ impl GitDriver { .into()); } - if Preg::is_match(r"{^ssh://[^@]+@[^:]+:[^0-9]+}", &self.inner.url) - .unwrap_or(false) - { + if Preg::is_match(r"{^ssh://[^@]+@[^:]+:[^0-9]+}", &self.inner.url).unwrap_or(false) { return Err(InvalidArgumentException { message: format!( "The source URL {} is invalid, ssh URLs should have a port number after \":\".\nUse ssh://git@example.com:22/path or just git@example.com:path if you do not want to provide a password or custom port.", @@ -173,11 +166,8 @@ impl GitDriver { &Filesystem::new(), ); if !Filesystem::is_local_path(&self.inner.url) { - let default_branch = git_util.get_mirror_default_branch( - &self.inner.url, - &self.repo_dir, - false, - )?; + let default_branch = + git_util.get_mirror_default_branch(&self.inner.url, &self.repo_dir, false)?; if let Some(branch) = default_branch { self.root_identifier = Some(branch.clone()); return Ok(branch); @@ -198,9 +188,7 @@ impl GitDriver { if !branches.contains(&"* master".to_string()) { for branch in &branches { if !branch.is_empty() { - if let Some(caps) = - Preg::match_strict_groups(r"{^\* +(\S+)}", branch) - { + if let Some(caps) = Preg::match_strict_groups(r"{^\* +(\S+)}", branch) { if let Some(name) = caps.get("1") { self.root_identifier = Some(name.clone()); break; @@ -264,10 +252,7 @@ impl GitDriver { Ok(Some(content)) } - pub fn get_change_date( - &mut self, - identifier: &str, - ) -> anyhow::Result<Option<DateTime<Utc>>> { + pub fn get_change_date(&mut self, identifier: &str) -> anyhow::Result<Option<DateTime<Utc>>> { if identifier.starts_with('-') { return Err(RuntimeException { message: format!( @@ -292,8 +277,7 @@ impl GitDriver { .process .execute(&command, &mut output, Some(self.repo_dir.clone())); - let timestamp_str = - GitUtil::parse_rev_list_output(&output, &self.inner.process); + let timestamp_str = GitUtil::parse_rev_list_output(&output, &self.inner.process); let timestamp: i64 = timestamp_str.trim().parse().unwrap_or(0); Ok(Some(Utc.timestamp_opt(timestamp, 0).unwrap())) } @@ -319,9 +303,7 @@ impl GitDriver { r"{^([a-f0-9]{40}) refs/tags/(\S+?)(\^\{\})?$}", &tag, ) { - if let (Some(hash), Some(name)) = - (caps.get("1"), caps.get("2")) - { + if let (Some(hash), Some(name)) = (caps.get("1"), caps.get("2")) { self.tags .as_mut() .unwrap() @@ -359,8 +341,7 @@ impl GitDriver { r"{^(?:\* )? *(\S+) *([a-f0-9]+)(?: .*)?$}", &branch, ) { - if let (Some(name), Some(hash)) = (caps.get("1"), caps.get("2")) - { + if let (Some(name), Some(hash)) = (caps.get("1"), caps.get("2")) { if !name.starts_with('-') { branches.insert(name.clone(), hash.clone()); } diff --git a/crates/shirabe/src/repository/vcs/github_driver.rs b/crates/shirabe/src/repository/vcs/github_driver.rs index a75e2a0..bd2e2ad 100644 --- a/crates/shirabe/src/repository/vcs/github_driver.rs +++ b/crates/shirabe/src/repository/vcs/github_driver.rs @@ -5,9 +5,9 @@ use chrono::{DateTime, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - array_diff, array_key_exists, array_map, array_search_mixed, base64_decode, basename, count, - empty, explode, extension_loaded, in_array, parse_url_all, sprintf, strpos, strtolower, substr, - trim, urlencode, InvalidArgumentException, PhpMixed, RuntimeException, + InvalidArgumentException, PhpMixed, RuntimeException, array_diff, array_key_exists, array_map, + array_search_mixed, base64_decode, basename, count, empty, explode, extension_loaded, in_array, + parse_url_all, sprintf, strpos, strtolower, substr, trim, urlencode, }; use crate::cache::Cache; @@ -255,10 +255,7 @@ impl GitHubDriver { if composer.contains_key("support") && !matches!(composer.get("support"), Some(PhpMixed::Array(_))) { - composer.insert( - "support".to_string(), - PhpMixed::Array(IndexMap::new()), - ); + composer.insert("support".to_string(), PhpMixed::Array(IndexMap::new())); } let support_source_missing = !composer .get("support") @@ -294,12 +291,10 @@ impl GitHubDriver { .filter(|v| !matches!(v, PhpMixed::Bool(false) | PhpMixed::Null)) .unwrap_or_else(|| PhpMixed::String(identifier.to_string())); let label_str = label.as_string().unwrap_or(identifier).to_string(); - if let Some(support) = - composer.get_mut("support").and_then(|v| match v { - PhpMixed::Array(m) => Some(m), - _ => None, - }) - { + if let Some(support) = composer.get_mut("support").and_then(|v| match v { + PhpMixed::Array(m) => Some(m), + _ => None, + }) { support.insert( "source".to_string(), Box::new(PhpMixed::String(sprintf( @@ -320,12 +315,10 @@ impl GitHubDriver { .map(|m| m.contains_key("issues")) .unwrap_or(false); if issues_missing && self.has_issues { - if let Some(support) = - composer.get_mut("support").and_then(|v| match v { - PhpMixed::Array(m) => Some(m), - _ => None, - }) - { + if let Some(support) = composer.get_mut("support").and_then(|v| match v { + PhpMixed::Array(m) => Some(m), + _ => None, + }) { support.insert( "issues".to_string(), Box::new(PhpMixed::String(sprintf( @@ -390,15 +383,10 @@ impl GitHubDriver { ] { let mut options: IndexMap<String, PhpMixed> = IndexMap::new(); options.insert("retry-auth-failure".to_string(), PhpMixed::Bool(false)); - let response = self - .inner - .http_downloader - .get(file_url, &PhpMixed::Array( - options - .into_iter() - .map(|(k, v)| (k, Box::new(v))) - .collect(), - )); + let response = self.inner.http_downloader.get( + file_url, + &PhpMixed::Array(options.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), + ); let response = match response { Ok(r) => r, Err(_) => continue, @@ -417,10 +405,8 @@ impl GitHubDriver { .and_then(|v| v.as_string()) .map(|s| s.is_empty()) .unwrap_or(true); - let encoding_not_base64 = response_map - .get("encoding") - .and_then(|v| v.as_string()) - != Some("base64"); + let encoding_not_base64 = + response_map.get("encoding").and_then(|v| v.as_string()) != Some("base64"); if content_empty || encoding_not_base64 { continue; } @@ -451,23 +437,18 @@ impl GitHubDriver { let mut key: Option<String> = None; for line in Preg::split(r"{\r?\n}", &funding) { let line = trim(&line, None); - if let Some(m) = - Preg::is_match_strict_groups(r"{^(\w+)\s*:\s*(.+)$}", &line) - { + if let Some(m) = Preg::is_match_strict_groups(r"{^(\w+)\s*:\s*(.+)$}", &line) { let g1 = m.get(1).cloned().unwrap_or_default(); let g2 = m.get(2).cloned().unwrap_or_default(); if g2 == "[" { key = Some(g1); continue; } - if let Some(m2) = Preg::is_match_strict_groups( - r"{^\[(.*?)\](?:\s*#.*)?$}", - &g2, - ) { + if let Some(m2) = Preg::is_match_strict_groups(r"{^\[(.*?)\](?:\s*#.*)?$}", &g2) { let inner = m2.get(1).cloned().unwrap_or_default(); for item in array_map( |s: &String| trim(s, None), - &Preg::split(r"{[\'\"]?\s*,\s*[\'\"]?}", &inner), + &Preg::split(r#"{[\'\"]?\s*,\s*[\'\"]?}"#, &inner), ) { let mut entry = IndexMap::new(); entry.insert("type".to_string(), PhpMixed::String(g1.clone())); @@ -477,10 +458,9 @@ impl GitHubDriver { ); result.push(entry); } - } else if let Some(m2) = Preg::is_match_strict_groups( - r"{^([^#].*?)(?:\s+#.*)?$}", - &g2, - ) { + } else if let Some(m2) = + Preg::is_match_strict_groups(r"{^([^#].*?)(?:\s+#.*)?$}", &g2) + { let mut entry = IndexMap::new(); entry.insert("type".to_string(), PhpMixed::String(g1.clone())); entry.insert( @@ -493,15 +473,11 @@ impl GitHubDriver { result.push(entry); } key = None; - } else if let Some(m) = - Preg::is_match_strict_groups(r"{^(\w+)\s*:\s*#\s*$}", &line) - { + } else if let Some(m) = Preg::is_match_strict_groups(r"{^(\w+)\s*:\s*#\s*$}", &line) { key = Some(m.get(1).cloned().unwrap_or_default()); } else if key.is_some() - && (Preg::is_match_strict_groups(r"{^-\s*(.+)(?:\s+#.*)?$}", &line) - .is_some() - || Preg::is_match_strict_groups(r"{^(.+),(?:\s*#.*)?$}", &line) - .is_some()) + && (Preg::is_match_strict_groups(r"{^-\s*(.+)(?:\s+#.*)?$}", &line).is_some() + || Preg::is_match_strict_groups(r"{^(.+),(?:\s*#.*)?$}", &line).is_some()) { let m = Preg::is_match_strict_groups(r"{^-\s*(.+)(?:\s+#.*)?$}", &line) .or_else(|| Preg::is_match_strict_groups(r"{^(.+),(?:\s*#.*)?$}", &line)) @@ -513,10 +489,7 @@ impl GitHubDriver { ); entry.insert( "url".to_string(), - PhpMixed::String(trim( - &m.get(1).cloned().unwrap_or_default(), - Some("\"' "), - )), + PhpMixed::String(trim(&m.get(1).cloned().unwrap_or_default(), Some("\"' "))), ); result.push(entry); } else if key.is_some() && line == "]" { @@ -568,10 +541,7 @@ impl GitHubDriver { "liberapay" => { result[key_idx].insert( "url".to_string(), - PhpMixed::String(format!( - "https://liberapay.com/{}", - basename(&item_url) - )), + PhpMixed::String(format!("https://liberapay.com/{}", basename(&item_url))), ); } "open_collective" => { @@ -625,10 +595,7 @@ impl GitHubDriver { "otechie" => { result[key_idx].insert( "url".to_string(), - PhpMixed::String(format!( - "https://otechie.com/{}", - basename(&item_url) - )), + PhpMixed::String(format!("https://otechie.com/{}", basename(&item_url))), ); } "custom" => { @@ -712,16 +679,16 @@ impl GitHubDriver { PhpMixed::Array(ref m) => m.clone(), _ => IndexMap::new(), }; - let needs_git_url = (resource_map.get("content").and_then(|v| v.as_string()).is_none() + let needs_git_url = (resource_map + .get("content") + .and_then(|v| v.as_string()) + .is_none() || resource_map .get("content") .and_then(|v| v.as_string()) .map(|s| s.is_empty()) .unwrap_or(false)) - && resource_map - .get("encoding") - .and_then(|v| v.as_string()) - == Some("none") + && resource_map.get("encoding").and_then(|v| v.as_string()) == Some("none") && resource_map.contains_key("git_url"); if needs_git_url { let git_url = resource_map @@ -740,10 +707,8 @@ impl GitHubDriver { _ => IndexMap::new(), }; let has_content = resource_map.contains_key("content"); - let encoding_base64 = resource_map - .get("encoding") - .and_then(|v| v.as_string()) - == Some("base64"); + let encoding_base64 = + resource_map.get("encoding").and_then(|v| v.as_string()) == Some("base64"); let content = if has_content && encoding_base64 { base64_decode( resource_map @@ -922,11 +887,7 @@ impl GitHubDriver { .filter(|s| !s.is_empty()) .unwrap_or_else(|| matches.get(3).cloned().unwrap_or_default()); if !in_array( - PhpMixed::String(strtolower(&Preg::replace( - r"{^www\.}i", - "", - origin_url, - ))), + PhpMixed::String(strtolower(&Preg::replace(r"{^www\.}i", "", origin_url))), &config.get("github-domains"), false, ) { @@ -1001,20 +962,23 @@ impl GitHubDriver { } if !self.inner.io.is_interactive() { - self.attempt_clone_fallback(Some(&e)) - .map_err(|err| TransportException { + self.attempt_clone_fallback(Some(&e)).map_err(|err| { + TransportException { message: err.to_string(), code: 0, - })?; + } + })?; let mut req = IndexMap::new(); - req.insert( - "url".to_string(), - PhpMixed::String("dummy".to_string()), - ); - return Ok(Response::new(req, Some(200), vec![], Some("null".to_string())) - .unwrap() - .unwrap()); + req.insert("url".to_string(), PhpMixed::String("dummy".to_string())); + return Ok(Response::new( + req, + Some(200), + vec![], + Some("null".to_string()), + ) + .unwrap() + .unwrap()); } let mut scopes_issued: Vec<String> = vec![]; @@ -1068,20 +1032,23 @@ impl GitHubDriver { } if !self.inner.io.is_interactive() && fetching_repo_data { - self.attempt_clone_fallback(Some(&e)) - .map_err(|err| TransportException { + self.attempt_clone_fallback(Some(&e)).map_err(|err| { + TransportException { message: err.to_string(), code: 0, - })?; + } + })?; let mut req = IndexMap::new(); - req.insert( - "url".to_string(), - PhpMixed::String("dummy".to_string()), - ); - return Ok(Response::new(req, Some(200), vec![], Some("null".to_string())) - .unwrap() - .unwrap()); + req.insert("url".to_string(), PhpMixed::String("dummy".to_string())); + return Ok(Response::new( + req, + Some(200), + vec![], + Some("null".to_string()), + ) + .unwrap() + .unwrap()); } let rate_limited = git_hub_util @@ -1186,11 +1153,8 @@ impl GitHubDriver { .unwrap_or("") .to_string(); - self.is_private = !empty( - &repo_data.get("private").cloned().unwrap_or(PhpMixed::Null), - ); - if let Some(default_branch) = repo_data.get("default_branch").and_then(|v| v.as_string()) - { + self.is_private = !empty(&repo_data.get("private").cloned().unwrap_or(PhpMixed::Null)); + if let Some(default_branch) = repo_data.get("default_branch").and_then(|v| v.as_string()) { self.root_identifier = default_branch.to_string(); } else if let Some(master_branch) = repo_data.get("master_branch").and_then(|v| v.as_string()) @@ -1205,12 +1169,7 @@ impl GitHubDriver { .cloned() .unwrap_or(PhpMixed::Null), ); - self.is_archived = !empty( - &repo_data - .get("archived") - .cloned() - .unwrap_or(PhpMixed::Null), - ); + self.is_archived = !empty(&repo_data.get("archived").cloned().unwrap_or(PhpMixed::Null)); Ok(()) } diff --git a/crates/shirabe/src/repository/vcs/gitlab_driver.rs b/crates/shirabe/src/repository/vcs/gitlab_driver.rs index 54147e9..cb0d0a2 100644 --- a/crates/shirabe/src/repository/vcs/gitlab_driver.rs +++ b/crates/shirabe/src/repository/vcs/gitlab_driver.rs @@ -5,9 +5,9 @@ use chrono::{DateTime, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - array_search_mixed, array_shift, ctype_alnum, empty, explode, extension_loaded, implode, - in_array, is_array, is_string, ord, sprintf, strpos, strtolower, InvalidArgumentException, - LogicException, PhpMixed, RuntimeException, + InvalidArgumentException, LogicException, PhpMixed, RuntimeException, array_search_mixed, + array_shift, ctype_alnum, empty, explode, extension_loaded, implode, in_array, is_array, + is_string, ord, sprintf, strpos, strtolower, }; use crate::cache::Cache; @@ -76,7 +76,8 @@ impl GitLabDriver { .filter(|s| !s.is_empty()) .unwrap_or_else(|| match_.get("domain2").cloned().unwrap_or_default()); let configured_domains = self.inner.config.get("gitlab-domains"); - let mut url_parts: Vec<String> = explode("/", &match_.get("parts").cloned().unwrap_or_default()); + let mut url_parts: Vec<String> = + explode("/", &match_.get("parts").cloned().unwrap_or_default()); let scheme_match = match_.get("scheme").cloned().unwrap_or_default(); self.scheme = if in_array( @@ -122,7 +123,10 @@ impl GitLabDriver { self.inner.origin_url = origin; let protocol_value = self.inner.config.get("gitlab-protocol"); - if let Some(protocol) = protocol_value.as_string().filter(|_| is_string(&protocol_value)) { + if let Some(protocol) = protocol_value + .as_string() + .filter(|_| is_string(&protocol_value)) + { // https treated as a synonym for http. if !in_array( PhpMixed::String(protocol.to_string()), @@ -250,10 +254,7 @@ impl GitLabDriver { if composer.contains_key("support") && !is_array(composer.get("support").cloned().unwrap_or(PhpMixed::Null)) { - composer.insert( - "support".to_string(), - PhpMixed::Array(IndexMap::new()), - ); + composer.insert("support".to_string(), PhpMixed::Array(IndexMap::new())); } let project = self.project.clone().unwrap_or_default(); let has_web_url = project.contains_key("web_url"); @@ -278,7 +279,8 @@ impl GitLabDriver { array_search_mixed( &PhpMixed::String(identifier.to_string()), &PhpMixed::Array( - self.get_branches().unwrap_or_default() + self.get_branches() + .unwrap_or_default() .into_iter() .map(|(k, v)| (k, Box::new(PhpMixed::String(v)))) .collect(), @@ -294,20 +296,15 @@ impl GitLabDriver { .and_then(|v| v.as_string()) .unwrap_or("") .to_string(); - if let Some(support) = composer.get_mut("support").and_then(|v| { - match v { - PhpMixed::Array(m) => Some(m), - _ => None, - } + if let Some(support) = composer.get_mut("support").and_then(|v| match v { + PhpMixed::Array(m) => Some(m), + _ => None, }) { support.insert( "source".to_string(), Box::new(PhpMixed::String(sprintf( "%s/-/tree/%s", - &[ - PhpMixed::String(web_url), - PhpMixed::String(label_str), - ], + &[PhpMixed::String(web_url), PhpMixed::String(label_str)], ))), ); } @@ -343,12 +340,7 @@ impl GitLabDriver { } } if !composer.contains_key("abandoned") - && !empty( - &project - .get("archived") - .cloned() - .unwrap_or(PhpMixed::Null), - ) + && !empty(&project.get("archived").cloned().unwrap_or(PhpMixed::Null)) { composer.insert("abandoned".to_string(), PhpMixed::Bool(true)); } @@ -559,12 +551,8 @@ impl GitLabDriver { Box::new(PhpMixed::String("_".to_string())), ]), true, - ) - { - format!( - "%{}", - sprintf("%02X", &[PhpMixed::Int(ord(&character))]) - ) + ) { + format!("%{}", sprintf("%02X", &[PhpMixed::Int(ord(&character))])) } else { character }; @@ -774,15 +762,13 @@ impl GitLabDriver { // Check both access levels (e.g. project, group) // - value will be null if no access is set // - value will be array with key access_level if set - if let Some(permissions) = json_map - .get("permissions") - .and_then(|v| v.as_array()) + if let Some(permissions) = + json_map.get("permissions").and_then(|v| v.as_array()) { for (_, permission) in permissions { if let Some(perm_map) = permission.as_array() { - if let Some(level) = perm_map - .get("access_level") - .and_then(|v| v.as_int()) + if let Some(level) = + perm_map.get("access_level").and_then(|v| v.as_int()) { if level >= 20 { more_than_guest_access = true; @@ -809,13 +795,15 @@ impl GitLabDriver { })?; let mut req = IndexMap::new(); - req.insert( - "url".to_string(), - PhpMixed::String("dummy".to_string()), - ); - return Ok(Response::new(req, Some(200), vec![], Some("null".to_string())) - .unwrap() - .unwrap()); + req.insert("url".to_string(), PhpMixed::String("dummy".to_string())); + return Ok(Response::new( + req, + Some(200), + vec![], + Some("null".to_string()), + ) + .unwrap() + .unwrap()); } } @@ -834,9 +822,7 @@ impl GitLabDriver { }); } - if !empty( - &json_map.get("id").cloned().unwrap_or(PhpMixed::Null), - ) { + if !empty(&json_map.get("id").cloned().unwrap_or(PhpMixed::Null)) { self.is_private = false; } @@ -885,13 +871,15 @@ impl GitLabDriver { })?; let mut req = IndexMap::new(); - req.insert( - "url".to_string(), - PhpMixed::String("dummy".to_string()), - ); - return Ok(Response::new(req, Some(200), vec![], Some("null".to_string())) - .unwrap() - .unwrap()); + req.insert("url".to_string(), PhpMixed::String("dummy".to_string())); + return Ok(Response::new( + req, + Some(200), + vec![], + Some("null".to_string()), + ) + .unwrap() + .unwrap()); } self.inner.io.write_error( PhpMixed::String(format!( @@ -927,13 +915,15 @@ impl GitLabDriver { })?; let mut req = IndexMap::new(); - req.insert( - "url".to_string(), - PhpMixed::String("dummy".to_string()), - ); - return Ok(Response::new(req, Some(200), vec![], Some("null".to_string())) - .unwrap() - .unwrap()); + req.insert("url".to_string(), PhpMixed::String("dummy".to_string())); + return Ok(Response::new( + req, + Some(200), + vec![], + Some("null".to_string()), + ) + .unwrap() + .unwrap()); } Err(e) @@ -1002,9 +992,7 @@ impl GitLabDriver { let links = explode(",", &header); for link in &links { - if let Some(match_) = - Preg::is_match_strict_groups(r#"{<(.+?)>; *rel="next"}"#, link) - { + if let Some(match_) = Preg::is_match_strict_groups(r#"{<(.+?)>; *rel="next"}"#, link) { return Some(match_.get(1).cloned().unwrap_or_default()); } } @@ -1059,11 +1047,7 @@ impl GitLabDriver { false, ) || (port_number.is_some() && in_array( - PhpMixed::String(Preg::replace( - r"{:\d+}", - "", - guessed_domain.clone(), - )), + PhpMixed::String(Preg::replace(r"{:\d+}", "", guessed_domain.clone())), configured_domains, false, )) @@ -1075,4 +1059,3 @@ impl GitLabDriver { None } } - diff --git a/crates/shirabe/src/repository/vcs/hg_driver.rs b/crates/shirabe/src/repository/vcs/hg_driver.rs index 7dc85e2..6b32a0a 100644 --- a/crates/shirabe/src/repository/vcs/hg_driver.rs +++ b/crates/shirabe/src/repository/vcs/hg_driver.rs @@ -1,9 +1,5 @@ //! ref: composer/src/Composer/Repository/Vcs/HgDriver.php -use chrono::{DateTime, Utc}; -use indexmap::IndexMap; -use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_php_shim::{dirname, is_dir, is_writable, RuntimeException}; use crate::cache::Cache; use crate::config::Config; use crate::io::io_interface::IOInterface; @@ -11,6 +7,10 @@ use crate::repository::vcs::vcs_driver::VcsDriver; use crate::util::filesystem::Filesystem; use crate::util::hg::Hg as HgUtils; use crate::util::url::Url; +use chrono::{DateTime, Utc}; +use indexmap::IndexMap; +use shirabe_external_packages::composer::pcre::preg::Preg; +use shirabe_php_shim::{RuntimeException, dirname, is_dir, is_writable}; #[derive(Debug)] pub struct HgDriver { @@ -26,7 +26,13 @@ impl HgDriver { if Filesystem::is_local_path(&self.inner.url) { self.repo_dir = self.inner.url.clone(); } else { - let cache_vcs_dir = self.inner.config.get("cache-vcs-dir").as_string().unwrap_or("").to_string(); + let cache_vcs_dir = self + .inner + .config + .get("cache-vcs-dir") + .as_string() + .unwrap_or("") + .to_string(); if !Cache::is_usable(&cache_vcs_dir) { return Err(RuntimeException { message: "HgDriver requires a usable cache directory, and it looks like you set it to be disabled".to_string(), @@ -34,7 +40,8 @@ impl HgDriver { }.into()); } - let sanitized = Preg::replace(r"{[^a-z0-9]}i", "-", Url::sanitize(self.inner.url.clone())); + let sanitized = + Preg::replace(r"{[^a-z0-9]}i", "-", Url::sanitize(self.inner.url.clone())); self.repo_dir = format!("{}/{}/", cache_vcs_dir, sanitized); let fs = Filesystem::new(); @@ -50,12 +57,25 @@ impl HgDriver { }.into()); } - self.inner.config.prohibit_url_by_config(&self.inner.url, &*self.inner.io)?; + self.inner + .config + .prohibit_url_by_config(&self.inner.url, &*self.inner.io)?; let hg_utils = HgUtils::new(&*self.inner.io, &self.inner.config, &self.inner.process); - if is_dir(&self.repo_dir) && self.inner.process.execute(&["hg", "summary"].map(|s| s.to_string()).to_vec(), &mut String::new(), Some(self.repo_dir.clone())) == 0 { - if self.inner.process.execute(&["hg", "pull"].map(|s| s.to_string()).to_vec(), &mut String::new(), Some(self.repo_dir.clone())) != 0 { + if is_dir(&self.repo_dir) + && self.inner.process.execute( + &["hg", "summary"].map(|s| s.to_string()).to_vec(), + &mut String::new(), + Some(self.repo_dir.clone()), + ) == 0 + { + if self.inner.process.execute( + &["hg", "pull"].map(|s| s.to_string()).to_vec(), + &mut String::new(), + Some(self.repo_dir.clone()), + ) != 0 + { self.inner.io.write_error( format!("<error>Failed to update {}, package information from this repository may be outdated ({})</error>", self.inner.url, self.inner.process.get_error_output()).into(), true, @@ -68,7 +88,14 @@ impl HgDriver { let repo_dir = self.repo_dir.clone(); let command = move |url: String| -> Vec<String> { - vec!["hg".to_string(), "clone".to_string(), "--noupdate".to_string(), "--".to_string(), url, repo_dir.clone()] + vec![ + "hg".to_string(), + "clone".to_string(), + "--noupdate".to_string(), + "--".to_string(), + url, + repo_dir.clone(), + ] }; hg_utils.run_command(command, self.inner.url.clone(), None)?; @@ -85,7 +112,9 @@ impl HgDriver { if self.root_identifier.is_none() { let mut output = String::new(); self.inner.process.execute( - &["hg", "tip", "--template", "{node}"].map(|s| s.to_string()).to_vec(), + &["hg", "tip", "--template", "{node}"] + .map(|s| s.to_string()) + .to_vec(), &mut output, Some(self.repo_dir.clone()), ); @@ -115,17 +144,27 @@ impl HgDriver { pub fn get_file_content(&self, file: &str, identifier: &str) -> anyhow::Result<Option<String>> { if identifier.starts_with('-') { return Err(RuntimeException { - message: format!("Invalid hg identifier detected. Identifier must not start with a -, given: {}", identifier), + message: format!( + "Invalid hg identifier detected. Identifier must not start with a -, given: {}", + identifier + ), code: 0, - }.into()); + } + .into()); } let resource = vec![ - "hg".to_string(), "cat".to_string(), "-r".to_string(), identifier.to_string(), - "--".to_string(), file.to_string(), + "hg".to_string(), + "cat".to_string(), + "-r".to_string(), + identifier.to_string(), + "--".to_string(), + file.to_string(), ]; let mut content = String::new(); - self.inner.process.execute(&resource, &mut content, Some(self.repo_dir.clone())); + self.inner + .process + .execute(&resource, &mut content, Some(self.repo_dir.clone())); if content.trim().is_empty() { return Ok(None); @@ -137,22 +176,32 @@ impl HgDriver { pub fn get_change_date(&self, identifier: &str) -> anyhow::Result<Option<DateTime<Utc>>> { if identifier.starts_with('-') { return Err(RuntimeException { - message: format!("Invalid hg identifier detected. Identifier must not start with a -, given: {}", identifier), + message: format!( + "Invalid hg identifier detected. Identifier must not start with a -, given: {}", + identifier + ), code: 0, - }.into()); + } + .into()); } let mut output = String::new(); self.inner.process.execute( - &["hg", "log", "--template", "{date|rfc3339date}", "-r", identifier] - .map(|s| s.to_string()) - .to_vec(), + &[ + "hg", + "log", + "--template", + "{date|rfc3339date}", + "-r", + identifier, + ] + .map(|s| s.to_string()) + .to_vec(), &mut output, Some(self.repo_dir.clone()), ); - let date = DateTime::parse_from_rfc3339(output.trim()) - .map(|d| d.with_timezone(&Utc))?; + let date = DateTime::parse_from_rfc3339(output.trim()).map(|d| d.with_timezone(&Utc))?; Ok(Some(date)) } @@ -231,7 +280,12 @@ impl HgDriver { } pub fn supports(io: &dyn IOInterface, config: &Config, url: &str, deep: bool) -> bool { - if Preg::is_match(r"#(^(?:https?|ssh)://(?:[^@]+@)?bitbucket.org|https://(?:.*?)\.kilnhg.com)#i", url).unwrap_or(false) { + if Preg::is_match( + r"#(^(?:https?|ssh)://(?:[^@]+@)?bitbucket.org|https://(?:.*?)\.kilnhg.com)#i", + url, + ) + .unwrap_or(false) + { return true; } @@ -243,7 +297,12 @@ impl HgDriver { let process = crate::util::process_executor::ProcessExecutor::new(io); let mut output = String::new(); - if process.execute(&["hg", "summary"].map(|s| s.to_string()).to_vec(), &mut output, Some(url)) == 0 { + if process.execute( + &["hg", "summary"].map(|s| s.to_string()).to_vec(), + &mut output, + Some(url), + ) == 0 + { return true; } } @@ -254,7 +313,13 @@ impl HgDriver { let process = crate::util::process_executor::ProcessExecutor::new(io); let mut ignored = String::new(); - let exit = process.execute(&["hg", "identify", "--", url].map(|s| s.to_string()).to_vec(), &mut ignored, None); + let exit = process.execute( + &["hg", "identify", "--", url] + .map(|s| s.to_string()) + .to_vec(), + &mut ignored, + None, + ); exit == 0 } diff --git a/crates/shirabe/src/repository/vcs/mod.rs b/crates/shirabe/src/repository/vcs/mod.rs new file mode 100644 index 0000000..4bbf4a6 --- /dev/null +++ b/crates/shirabe/src/repository/vcs/mod.rs @@ -0,0 +1,11 @@ +pub mod forgejo_driver; +pub mod fossil_driver; +pub mod git_bitbucket_driver; +pub mod git_driver; +pub mod github_driver; +pub mod gitlab_driver; +pub mod hg_driver; +pub mod perforce_driver; +pub mod svn_driver; +pub mod vcs_driver; +pub mod vcs_driver_interface; diff --git a/crates/shirabe/src/repository/vcs/perforce_driver.rs b/crates/shirabe/src/repository/vcs/perforce_driver.rs index f32f895..cd3b32b 100644 --- a/crates/shirabe/src/repository/vcs/perforce_driver.rs +++ b/crates/shirabe/src/repository/vcs/perforce_driver.rs @@ -22,12 +22,20 @@ pub struct PerforceDriver { impl PerforceDriver { pub fn initialize(&mut self) -> anyhow::Result<()> { - self.depot = self.inner.repo_config.get("depot") + self.depot = self + .inner + .repo_config + .get("depot") .and_then(|v| v.as_string()) .unwrap_or("") .to_string(); self.branch = String::new(); - if let Some(branch) = self.inner.repo_config.get("branch").and_then(|v| v.as_string()) { + if let Some(branch) = self + .inner + .repo_config + .get("branch") + .and_then(|v| v.as_string()) + { if !branch.is_empty() { self.branch = branch.to_string(); } @@ -48,7 +56,13 @@ impl PerforceDriver { return Ok(()); } - let cache_vcs_dir = self.inner.config.get("cache-vcs-dir").as_string().unwrap_or("").to_string(); + let cache_vcs_dir = self + .inner + .config + .get("cache-vcs-dir") + .as_string() + .unwrap_or("") + .to_string(); if !Cache::is_usable(&cache_vcs_dir) { return Err(RuntimeException { message: "PerforceDriver requires a usable cache directory, and it looks like you set it to be disabled".to_string(), @@ -57,16 +71,28 @@ impl PerforceDriver { } let repo_dir = format!("{}/{}", cache_vcs_dir, self.depot); - self.perforce = Some(Perforce::create(repo_config, &self.inner.url, &repo_dir, &self.inner.process, self.inner.io.as_ref())?); + self.perforce = Some(Perforce::create( + repo_config, + &self.inner.url, + &repo_dir, + &self.inner.process, + self.inner.io.as_ref(), + )?); Ok(()) } pub fn get_file_content(&self, file: &str, identifier: &str) -> anyhow::Result<Option<String>> { - self.perforce.as_ref().unwrap().get_file_content(file, identifier) + self.perforce + .as_ref() + .unwrap() + .get_file_content(file, identifier) } - pub fn get_change_date(&self, _identifier: &str) -> anyhow::Result<Option<chrono::DateTime<chrono::Utc>>> { + pub fn get_change_date( + &self, + _identifier: &str, + ) -> anyhow::Result<Option<chrono::DateTime<chrono::Utc>>> { Ok(None) } @@ -89,9 +115,22 @@ impl PerforceDriver { pub fn get_source(&self, identifier: &str) -> IndexMap<String, PhpMixed> { let mut source = IndexMap::new(); source.insert("type".to_string(), PhpMixed::String("perforce".to_string())); - source.insert("url".to_string(), self.inner.repo_config.get("url").cloned().unwrap_or(PhpMixed::Null)); - source.insert("reference".to_string(), PhpMixed::String(identifier.to_string())); - source.insert("p4user".to_string(), PhpMixed::String(self.perforce.as_ref().unwrap().get_user().to_string())); + source.insert( + "url".to_string(), + self.inner + .repo_config + .get("url") + .cloned() + .unwrap_or(PhpMixed::Null), + ); + source.insert( + "reference".to_string(), + PhpMixed::String(identifier.to_string()), + ); + source.insert( + "p4user".to_string(), + PhpMixed::String(self.perforce.as_ref().unwrap().get_user().to_string()), + ); source } @@ -101,14 +140,19 @@ impl PerforceDriver { pub fn has_composer_file(&self, identifier: &str) -> bool { let path = format!("//{}/{}", self.depot, identifier); - self.perforce.as_ref().unwrap().get_composer_information(&path).map_or(false, |info| !info.is_empty()) + self.perforce + .as_ref() + .unwrap() + .get_composer_information(&path) + .map_or(false, |info| !info.is_empty()) } pub fn get_contents(&self, _url: &str) -> anyhow::Result<Response> { Err(BadMethodCallException { message: "Not implemented/used in PerforceDriver".to_string(), code: 0, - }.into()) + } + .into()) } pub fn supports(io: &dyn IOInterface, config: &Config, url: &str, deep: bool) -> bool { diff --git a/crates/shirabe/src/repository/vcs/svn_driver.rs b/crates/shirabe/src/repository/vcs/svn_driver.rs index 4bfcf91..7c32e4a 100644 --- a/crates/shirabe/src/repository/vcs/svn_driver.rs +++ b/crates/shirabe/src/repository/vcs/svn_driver.rs @@ -5,8 +5,8 @@ use chrono::{DateTime, TimeZone, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - array_key_exists, is_array, max, sprintf, stripos, strrpos, strtr, substr, trim, PhpMixed, - RuntimeException, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, + JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, PhpMixed, RuntimeException, array_key_exists, + is_array, max, sprintf, stripos, strrpos, strtr, substr, trim, }; use crate::cache::Cache; @@ -51,9 +51,7 @@ pub struct SvnDriver { impl SvnDriver { pub fn initialize(&mut self) -> Result<()> { let normalized = Self::normalize_url(&self.inner.url); - self.inner.url = normalized - .trim_end_matches('/') - .to_string(); + self.inner.url = normalized.trim_end_matches('/').to_string(); self.base_url = self.inner.url.clone(); SvnUtil::clean_env(); @@ -90,24 +88,24 @@ impl SvnDriver { todo!("self.inner.io clone"), &format!( "{}/{}", - self.inner.config.get("cache-repo-dir").as_string().unwrap_or(""), + self.inner + .config + .get("cache-repo-dir") + .as_string() + .unwrap_or(""), Preg::replace(r"{[^a-z0-9.]}i", "-", Url::sanitize(self.base_url.clone())), ), None, None, false, )); - self.inner - .cache - .as_mut() - .unwrap() - .set_read_only( - self.inner - .config - .get("cache-read-only") - .as_bool() - .unwrap_or(false), - ); + self.inner.cache.as_mut().unwrap().set_read_only( + self.inner + .config + .get("cache-read-only") + .as_bool() + .unwrap_or(false), + ); self.get_branches(); self.get_tags(); @@ -172,24 +170,22 @@ impl SvnDriver { } // TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch - let composer: Option<IndexMap<String, PhpMixed>> = match self - .inner - .get_base_composer_information(identifier) - { - Ok(c) => c, - Err(e) => { - // TODO(phase-b): downcast to TransportException - let _te: &TransportException = todo!("downcast e to TransportException"); - let message = e.to_string(); - if stripos(&message, "path not found").is_none() - && stripos(&message, "svn: warning: W160013").is_none() - { - return Err(e); + let composer: Option<IndexMap<String, PhpMixed>> = + match self.inner.get_base_composer_information(identifier) { + Ok(c) => c, + Err(e) => { + // TODO(phase-b): downcast to TransportException + let _te: &TransportException = todo!("downcast e to TransportException"); + let message = e.to_string(); + if stripos(&message, "path not found").is_none() + && stripos(&message, "svn: warning: W160013").is_none() + { + return Err(e); + } + // remember a not-existent composer.json + None } - // remember a not-existent composer.json - None - } - }; + }; if self.should_cache(identifier) { let encoded = JsonFile::encode( @@ -221,10 +217,7 @@ impl SvnDriver { if cached.is_none() || !is_array( // TODO(phase-b): wrap IndexMap to PhpMixed for is_array check - &cached - .clone() - .map(PhpMixed::from) - .unwrap_or(PhpMixed::Null), + &cached.clone().map(PhpMixed::from).unwrap_or(PhpMixed::Null), ) { return Ok(None); @@ -233,11 +226,7 @@ impl SvnDriver { Ok(cached) } - pub fn get_file_content( - &mut self, - file: &str, - identifier: &str, - ) -> Result<Option<String>> { + pub fn get_file_content(&mut self, file: &str, identifier: &str) -> Result<Option<String>> { let identifier = format!("/{}/", trim(identifier, Some("/"))); let (path, rev) = if let Ok(Some(m)) = @@ -296,10 +285,9 @@ impl SvnDriver { )?; for line in self.inner.process.split_lines(&output) { if !line.is_empty() { - if let Some(m) = Preg::is_match_strict_groups( - r"{^Last Changed Date: ([^(]+)}", - &line, - ) { + if let Some(m) = + Preg::is_match_strict_groups(r"{^Last Changed Date: ([^(]+)}", &line) + { let date_str = m.get(1).cloned().unwrap_or_default(); // PHP: new \DateTimeImmutable($match[1], new \DateTimeZone('UTC')) return Ok(Utc @@ -318,24 +306,22 @@ impl SvnDriver { // PHP: if ($this->tagsPath !== false) — tagsPath is "string"; treat empty string as false if !self.tags_path.is_empty() { - let output = self.execute( - vec![ - "svn".to_string(), - "ls".to_string(), - "--verbose".to_string(), - ], - &format!("{}/{}", self.base_url, self.tags_path), - ).unwrap_or_default(); + let output = self + .execute( + vec!["svn".to_string(), "ls".to_string(), "--verbose".to_string()], + &format!("{}/{}", self.base_url, self.tags_path), + ) + .unwrap_or_default(); if !output.is_empty() { let mut last_rev: i64 = 0; for line in self.inner.process.split_lines(&output) { let line = trim(&line, None); if !line.is_empty() { - if let Some(m) = Preg::is_match_strict_groups( - r"{^\s*(\S+).*?(\S+)\s*$}", - &line, - ) { - let rev: i64 = m.get(1).map(|s| s.parse().unwrap_or(0)).unwrap_or(0); + if let Some(m) = + Preg::is_match_strict_groups(r"{^\s*(\S+).*?(\S+)\s*$}", &line) + { + let rev: i64 = + m.get(1).map(|s| s.parse().unwrap_or(0)).unwrap_or(0); let path = m.get(2).cloned().unwrap_or_default(); if path == "./" { last_rev = rev; @@ -370,11 +356,7 @@ impl SvnDriver { let output = self .execute( - vec![ - "svn".to_string(), - "ls".to_string(), - "--verbose".to_string(), - ], + vec!["svn".to_string(), "ls".to_string(), "--verbose".to_string()], &trunk_parent, ) .unwrap_or_default(); @@ -382,10 +364,9 @@ impl SvnDriver { for line in self.inner.process.split_lines(&output) { let line = trim(&line, None); if !line.is_empty() { - if let Some(m) = Preg::is_match_strict_groups( - r"{^\s*(\S+).*?(\S+)\s*$}", - &line, - ) { + if let Some(m) = + Preg::is_match_strict_groups(r"{^\s*(\S+).*?(\S+)\s*$}", &line) + { let rev: i64 = m.get(1).map(|s| s.parse().unwrap_or(0)).unwrap_or(0); let path = m.get(2).cloned().unwrap_or_default(); if path == "./" { @@ -407,11 +388,7 @@ impl SvnDriver { if !self.branches_path.is_empty() { let output = self .execute( - vec![ - "svn".to_string(), - "ls".to_string(), - "--verbose".to_string(), - ], + vec!["svn".to_string(), "ls".to_string(), "--verbose".to_string()], &format!("{}/{}", self.base_url, self.branches_path), ) .unwrap_or_default(); @@ -420,10 +397,9 @@ impl SvnDriver { for line in self.inner.process.split_lines(&trim(&output, None)) { let line = trim(&line, None); if !line.is_empty() { - if let Some(m) = Preg::is_match_strict_groups( - r"{^\s*(\S+).*?(\S+)\s*$}", - &line, - ) { + if let Some(m) = + Preg::is_match_strict_groups(r"{^\s*(\S+).*?(\S+)\s*$}", &line) + { let rev: i64 = m.get(1).map(|s| s.parse().unwrap_or(0)).unwrap_or(0); let path = m.get(2).cloned().unwrap_or_default(); @@ -528,7 +504,12 @@ impl SvnDriver { } // TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch - match self.util.as_mut().unwrap().execute(command, url, None, None, false) { + match self + .util + .as_mut() + .unwrap() + .execute(command, url, None, None, false) + { Ok(o) => Ok(o), Err(e) => { if self.util.as_mut().unwrap().binary_version().is_none() { diff --git a/crates/shirabe/src/repository/vcs/vcs_driver.rs b/crates/shirabe/src/repository/vcs/vcs_driver.rs index 8db411d..a16930f 100644 --- a/crates/shirabe/src/repository/vcs/vcs_driver.rs +++ b/crates/shirabe/src/repository/vcs/vcs_driver.rs @@ -2,7 +2,9 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_php_shim::{extension_loaded, PhpMixed, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE}; +use shirabe_php_shim::{ + JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, PhpMixed, extension_loaded, +}; use crate::cache::Cache; use crate::config::Config; @@ -42,7 +44,11 @@ impl VcsDriver { } } - let url = repo_config.get("url").and_then(|v| v.as_string()).unwrap_or("").to_string(); + let url = repo_config + .get("url") + .and_then(|v| v.as_string()) + .unwrap_or("") + .to_string(); Self { origin_url: url.clone(), @@ -61,7 +67,10 @@ impl VcsDriver { self.cache.is_some() && Preg::is_match("{^[a-f0-9]{40}$}iD", identifier).unwrap_or(false) } - pub fn get_composer_information(&mut self, identifier: &str) -> anyhow::Result<Option<IndexMap<String, PhpMixed>>> { + pub fn get_composer_information( + &mut self, + identifier: &str, + ) -> anyhow::Result<Option<IndexMap<String, PhpMixed>>> { if !self.info_cache.contains_key(identifier) { if self.should_cache(identifier) { if let Some(res) = self.cache.as_ref().and_then(|c| c.read(identifier)) { @@ -75,7 +84,10 @@ impl VcsDriver { if self.should_cache(identifier) { if let Some(ref composer_map) = composer { - let encoded = JsonFile::encode_with_options(composer_map, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + let encoded = JsonFile::encode_with_options( + composer_map, + JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES, + ); self.cache.as_ref().map(|c| c.write(identifier, &encoded)); } } @@ -86,7 +98,10 @@ impl VcsDriver { Ok(self.info_cache.get(identifier).and_then(|v| v.clone())) } - pub(crate) fn get_base_composer_information(&mut self, identifier: &str) -> anyhow::Result<Option<IndexMap<String, PhpMixed>>> { + pub(crate) fn get_base_composer_information( + &mut self, + identifier: &str, + ) -> anyhow::Result<Option<IndexMap<String, PhpMixed>>> { let composer_file_content = self.get_file_content("composer.json", identifier)?; let composer_file_content = match composer_file_content { @@ -95,7 +110,10 @@ impl VcsDriver { Some(c) => c, }; - let composer = JsonFile::parse_json(&composer_file_content, Some(&format!("{}:composer.json", identifier)))?; + let composer = JsonFile::parse_json( + &composer_file_content, + Some(&format!("{}:composer.json", identifier)), + )?; let mut composer = match composer { None => return Ok(None), @@ -103,9 +121,16 @@ impl VcsDriver { Some(c) => c, }; - if !composer.contains_key("time") || composer.get("time").map_or(true, |v| v.as_string().map_or(true, |s| s.is_empty())) { + if !composer.contains_key("time") + || composer + .get("time") + .map_or(true, |v| v.as_string().map_or(true, |s| s.is_empty())) + { if let Some(change_date) = self.get_change_date(identifier)? { - composer.insert("time".to_string(), PhpMixed::String(change_date.to_rfc3339())); + composer.insert( + "time".to_string(), + PhpMixed::String(change_date.to_rfc3339()), + ); } } @@ -127,18 +152,29 @@ impl VcsDriver { } pub(crate) fn get_contents(&self, url: &str) -> anyhow::Result<Response, TransportException> { - let options = self.repo_config.get("options").cloned().unwrap_or(PhpMixed::Array(IndexMap::new())); + let options = self + .repo_config + .get("options") + .cloned() + .unwrap_or(PhpMixed::Array(IndexMap::new())); self.http_downloader.get(url, &options) } pub fn cleanup(&self) {} // abstract methods to be implemented by subclasses (via VcsDriverInterface trait) - pub(crate) fn get_file_content(&self, file: &str, identifier: &str) -> anyhow::Result<Option<String>> { + pub(crate) fn get_file_content( + &self, + file: &str, + identifier: &str, + ) -> anyhow::Result<Option<String>> { todo!() } - pub(crate) fn get_change_date(&self, identifier: &str) -> anyhow::Result<Option<chrono::DateTime<chrono::Utc>>> { + pub(crate) fn get_change_date( + &self, + identifier: &str, + ) -> anyhow::Result<Option<chrono::DateTime<chrono::Utc>>> { todo!() } } diff --git a/crates/shirabe/src/repository/vcs/vcs_driver_interface.rs b/crates/shirabe/src/repository/vcs/vcs_driver_interface.rs index a17acad..5b15c2c 100644 --- a/crates/shirabe/src/repository/vcs/vcs_driver_interface.rs +++ b/crates/shirabe/src/repository/vcs/vcs_driver_interface.rs @@ -1,15 +1,18 @@ //! ref: composer/src/Composer/Repository/Vcs/VcsDriverInterface.php +use crate::config::Config; +use crate::io::io_interface::IOInterface; use chrono::{DateTime, Utc}; use indexmap::IndexMap; use shirabe_php_shim::PhpMixed; -use crate::config::Config; -use crate::io::io_interface::IOInterface; pub trait VcsDriverInterface { fn initialize(&mut self) -> anyhow::Result<()>; - fn get_composer_information(&self, identifier: &str) -> anyhow::Result<Option<IndexMap<String, PhpMixed>>>; + fn get_composer_information( + &self, + identifier: &str, + ) -> anyhow::Result<Option<IndexMap<String, PhpMixed>>>; fn get_file_content(&self, file: &str, identifier: &str) -> anyhow::Result<Option<String>>; diff --git a/crates/shirabe/src/repository/vcs_repository.rs b/crates/shirabe/src/repository/vcs_repository.rs index fcfd101..57e44a5 100644 --- a/crates/shirabe/src/repository/vcs_repository.rs +++ b/crates/shirabe/src/repository/vcs_repository.rs @@ -4,8 +4,8 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - array_search_mixed, count, get_class, in_array, str_replace, strpos, InvalidArgumentException, - PhpMixed, + InvalidArgumentException, PhpMixed, array_search_mixed, count, get_class, in_array, + str_replace, strpos, }; use shirabe_semver::constraint::constraint::Constraint; @@ -135,7 +135,10 @@ impl VcsRepository { }); let url = Platform::expand_path( - repo_config.get("url").and_then(|v| v.as_string()).unwrap_or(""), + repo_config + .get("url") + .and_then(|v| v.as_string()) + .unwrap_or(""), ); repo_config.insert("url".to_string(), PhpMixed::String(url.clone())); let r#type = repo_config @@ -145,7 +148,8 @@ impl VcsRepository { .to_string(); let is_verbose = io.is_verbose(); let is_very_verbose = io.is_very_verbose(); - let process_executor = process.unwrap_or_else(|| ProcessExecutor::new(Some(Box::new(&*io)), None)); + let process_executor = + process.unwrap_or_else(|| ProcessExecutor::new(Some(Box::new(&*io)), None)); Ok(Self { inner, @@ -189,11 +193,7 @@ impl VcsRepository { .unwrap_or(driver_class); let _ = driver; - format!( - "vcs repo ({} {})", - driver_type, - Url::sanitize(&self.url) - ) + format!("vcs repo ({} {})", driver_type, Url::sanitize(&self.url)) } pub fn get_repo_config(&self) -> &IndexMap<String, PhpMixed> { @@ -292,7 +292,12 @@ impl VcsRepository { let mut has_root_identifier_composer_json = false; let root_identifier_result = self.driver.as_mut().unwrap().get_root_identifier(); if let Ok(root_identifier) = root_identifier_result { - match self.driver.as_mut().unwrap().has_composer_file(&root_identifier) { + match self + .driver + .as_mut() + .unwrap() + .has_composer_file(&root_identifier) + { Ok(b) => { has_root_identifier_composer_json = b; if has_root_identifier_composer_json { @@ -348,7 +353,9 @@ impl VcsRepository { let mut tag = tag; let msg = format!( "Reading composer.json of <info>{}</info> (<comment>{}</comment>)", - self.package_name.clone().unwrap_or_else(|| self.url.clone()), + self.package_name + .clone() + .unwrap_or_else(|| self.url.clone()), tag ); @@ -411,14 +418,12 @@ impl VcsRepository { // manually versioned package if data.contains_key("version") { - let normalized = self - .version_parser - .as_ref() - .unwrap() - .normalize( - data.get("version").and_then(|v| v.as_string()).unwrap_or(""), - None, - )?; + let normalized = self.version_parser.as_ref().unwrap().normalize( + data.get("version") + .and_then(|v| v.as_string()) + .unwrap_or(""), + None, + )?; data.insert( "version_normalized".to_string(), PhpMixed::String(normalized), @@ -438,7 +443,9 @@ impl VcsRepository { PhpMixed::String(Preg::replace( r"{[.-]?dev$}i", "", - data.get("version").and_then(|v| v.as_string()).unwrap_or(""), + data.get("version") + .and_then(|v| v.as_string()) + .unwrap_or(""), )), ); data.insert( @@ -503,16 +510,14 @@ impl VcsRepository { } if is_very_verbose { - self.io.write_error(&format!( - "Importing tag {} ({})", - tag, version_normalized - )); + self.io + .write_error(&format!("Importing tag {} ({})", tag, version_normalized)); } let driver = self.driver.as_mut().unwrap(); let processed = self.pre_process(&**driver, data, &identifier)?; let loaded = self.loader.as_ref().unwrap().load(processed, None)?; - self.inner.add_package(Box::new(loaded as Box<dyn _>))?; + self.inner.add_package(Box::new(loaded))?; Ok(()) })(); if let Err(e) = result { @@ -575,7 +580,9 @@ impl VcsRepository { for (branch, identifier) in branches { let msg = format!( "Reading composer.json of <info>{}</info> (<comment>{}</comment>)", - self.package_name.clone().unwrap_or_else(|| self.url.clone()), + self.package_name + .clone() + .unwrap_or_else(|| self.url.clone()), branch ); if is_very_verbose { @@ -609,7 +616,11 @@ impl VcsRepository { version = format!("dev-{}", str_replace("#", "+", &branch)); parsed_branch = str_replace("#", "+", &parsed_branch); } else { - let prefix = if strpos(&branch, "v") == Some(0) { "v" } else { "" }; + let prefix = if strpos(&branch, "v") == Some(0) { + "v" + } else { + "" + }; version = format!( "{}{}", prefix, @@ -617,8 +628,7 @@ impl VcsRepository { ); } - let is_default_branch = - self.driver.as_mut().unwrap().get_root_identifier()? == branch; + let is_default_branch = self.driver.as_mut().unwrap().get_root_identifier()? == branch; let cached_package = self.get_cached_package_version( &version, &identifier, @@ -666,26 +676,29 @@ impl VcsRepository { self.io.write_error(&format!( "Importing branch {} ({})", branch, - data.get("version").and_then(|v| v.as_string()).unwrap_or("") + data.get("version") + .and_then(|v| v.as_string()) + .unwrap_or("") )); } let package_data = self.pre_process(&**driver, data, &identifier)?; - let package = self.loader.as_ref().unwrap().load(package_data.clone(), None)?; + let package = self + .loader + .as_ref() + .unwrap() + .load(package_data.clone(), None)?; // TODO(phase-b): `$this->loader instanceof ValidatingArrayLoader` downcast let loader_as_validating: Option<&ValidatingArrayLoader> = None; if let Some(validating) = loader_as_validating { if count(&PhpMixed::Null) > 0 { let _ = validating; - return Err(InvalidPackageException::new( - vec![], - vec![], - package_data, - ) - .into()); + return Err( + InvalidPackageException::new(vec![], vec![], package_data).into() + ); } } - self.inner.add_package(Box::new(package as Box<dyn _>))?; + self.inner.add_package(Box::new(package))?; Ok(()) })(); if let Err(e) = result { @@ -712,10 +725,8 @@ impl VcsRepository { self.io.write_error(""); } self.branch_error_occurred = true; - self.io.write_error(&format!( - "<error>Skipped branch {}, {}</error>", - branch, e - )); + self.io + .write_error(&format!("<error>Skipped branch {}, {}</error>", branch, e)); self.io.write_error(""); continue; } @@ -810,12 +821,10 @@ impl VcsRepository { _ => None, }) .unwrap_or(false); - let source_reference = data - .get("source") - .and_then(|v| match v { - PhpMixed::Array(m) => m.get("reference").cloned(), - _ => None, - }); + let source_reference = data.get("source").and_then(|v| match v { + PhpMixed::Array(m) => m.get("reference").cloned(), + _ => None, + }); if dist_is_array && dist_lacks_reference && source_reference.is_some() { if let Some(PhpMixed::Array(dist_map)) = data.get_mut("dist") { dist_map.insert("reference".to_string(), source_reference.unwrap()); @@ -889,7 +898,9 @@ impl VcsRepository { if let VersionCacheResult::Package(ref mut data) = cached_package { let msg = format!( "Found cached composer.json of <info>{}</info> (<comment>{}</comment>)", - self.package_name.clone().unwrap_or_else(|| self.url.clone()), + self.package_name + .clone() + .unwrap_or_else(|| self.url.clone()), version ); if is_very_verbose { @@ -908,7 +919,11 @@ impl VcsRepository { data.insert("default-branch".to_string(), PhpMixed::Bool(true)); } - let name = data.get("name").and_then(|v| v.as_string()).unwrap_or("").to_string(); + let name = data + .get("name") + .and_then(|v| v.as_string()) + .unwrap_or("") + .to_string(); let version_normalized = data .get("version_normalized") .and_then(|v| v.as_string()) @@ -930,7 +945,7 @@ impl VcsRepository { if let VersionCacheResult::Package(data) = cached_package { let loaded = self.loader.as_ref().unwrap().load(data, None)?; - return Ok(CachedPackageResult::Package(Box::new(loaded as Box<dyn _>))); + return Ok(CachedPackageResult::Package(Box::new(loaded))); } Ok(CachedPackageResult::None) diff --git a/crates/shirabe/src/repository/writable_array_repository.rs b/crates/shirabe/src/repository/writable_array_repository.rs index 54e4b04..5d77fee 100644 --- a/crates/shirabe/src/repository/writable_array_repository.rs +++ b/crates/shirabe/src/repository/writable_array_repository.rs @@ -1,8 +1,8 @@ //! ref: composer/src/Composer/Repository/WritableArrayRepository.php -use anyhow::Result; use crate::installer::installation_manager::InstallationManager; use crate::repository::array_repository::ArrayRepository; +use anyhow::Result; #[derive(Debug)] pub struct WritableArrayRepository { @@ -25,7 +25,11 @@ impl WritableArrayRepository { &self.dev_package_names } - pub fn write(&mut self, dev_mode: bool, _installation_manager: &InstallationManager) -> Result<()> { + pub fn write( + &mut self, + dev_mode: bool, + _installation_manager: &InstallationManager, + ) -> Result<()> { self.dev_mode = Some(dev_mode); Ok(()) } diff --git a/crates/shirabe/src/repository/writable_repository_interface.rs b/crates/shirabe/src/repository/writable_repository_interface.rs index 3cb8b00..5648ef5 100644 --- a/crates/shirabe/src/repository/writable_repository_interface.rs +++ b/crates/shirabe/src/repository/writable_repository_interface.rs @@ -1,9 +1,9 @@ //! ref: composer/src/Composer/Repository/WritableRepositoryInterface.php -use anyhow::Result; use crate::installer::installation_manager::InstallationManager; use crate::package::package_interface::PackageInterface; use crate::repository::repository_interface::RepositoryInterface; +use anyhow::Result; pub trait WritableRepositoryInterface: RepositoryInterface { fn write(&mut self, dev_mode: bool, installation_manager: &InstallationManager) -> Result<()>; diff --git a/crates/shirabe/src/script/event.rs b/crates/shirabe/src/script/event.rs index aeb63f0..4be0814 100644 --- a/crates/shirabe/src/script/event.rs +++ b/crates/shirabe/src/script/event.rs @@ -1,10 +1,10 @@ //! ref: composer/src/Composer/Script/Event.php -use indexmap::IndexMap; -use shirabe_php_shim::PhpMixed; use crate::composer::Composer; use crate::event_dispatcher::event::Event as BaseEvent; use crate::io::io_interface::IOInterface; +use indexmap::IndexMap; +use shirabe_php_shim::PhpMixed; #[derive(Debug)] pub struct Event { diff --git a/crates/shirabe/src/script/mod.rs b/crates/shirabe/src/script/mod.rs new file mode 100644 index 0000000..9b83051 --- /dev/null +++ b/crates/shirabe/src/script/mod.rs @@ -0,0 +1,2 @@ +pub mod event; +pub mod script_events; diff --git a/crates/shirabe/src/self_update/mod.rs b/crates/shirabe/src/self_update/mod.rs new file mode 100644 index 0000000..4f9f188 --- /dev/null +++ b/crates/shirabe/src/self_update/mod.rs @@ -0,0 +1,2 @@ +pub mod keys; +pub mod versions; diff --git a/crates/shirabe/src/self_update/versions.rs b/crates/shirabe/src/self_update/versions.rs index bffb284..f5ba5d2 100644 --- a/crates/shirabe/src/self_update/versions.rs +++ b/crates/shirabe/src/self_update/versions.rs @@ -1,11 +1,14 @@ //! ref: composer/src/Composer/SelfUpdate/Versions.php -use indexmap::IndexMap; -use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_php_shim::{InvalidArgumentException, PhpMixed, UnexpectedValueException, PHP_EOL, PHP_VERSION, PHP_VERSION_ID}; use crate::config::Config; use crate::io::io_interface::IOInterface; use crate::util::http_downloader::HttpDownloader; +use indexmap::IndexMap; +use shirabe_external_packages::composer::pcre::preg::Preg; +use shirabe_php_shim::{ + InvalidArgumentException, PHP_EOL, PHP_VERSION, PHP_VERSION_ID, PhpMixed, + UnexpectedValueException, +}; pub struct Versions { pub channels: Vec<String>, @@ -24,7 +27,8 @@ impl std::fmt::Debug for Versions { } impl Versions { - pub const CHANNELS: &'static [&'static str] = &["stable", "preview", "snapshot", "1", "2", "2.2"]; + pub const CHANNELS: &'static [&'static str] = + &["stable", "preview", "snapshot", "1", "2", "2.2"]; pub fn new(config: Config, http_downloader: HttpDownloader) -> Self { Self { @@ -54,7 +58,11 @@ impl Versions { Ok("stable".to_string()) } - pub fn set_channel(&mut self, channel: String, io: Option<&dyn IOInterface>) -> anyhow::Result<Result<(), InvalidArgumentException>> { + pub fn set_channel( + &mut self, + channel: String, + io: Option<&dyn IOInterface>, + ) -> anyhow::Result<Result<(), InvalidArgumentException>> { if !Self::CHANNELS.contains(&channel.as_str()) { return Ok(Err(InvalidArgumentException { message: format!( @@ -95,7 +103,10 @@ impl Versions { Ok(Ok(())) } - pub fn get_latest(&mut self, channel: Option<&str>) -> anyhow::Result<Result<IndexMap<String, PhpMixed>, UnexpectedValueException>> { + pub fn get_latest( + &mut self, + channel: Option<&str>, + ) -> anyhow::Result<Result<IndexMap<String, PhpMixed>, UnexpectedValueException>> { let versions = self.get_versions_data()?; let effective_channel = match channel { Some(c) => c.to_string(), @@ -107,11 +118,12 @@ impl Versions { if let PhpMixed::List(ref list) = **channel_versions { for version in list { if let PhpMixed::Array(ref v) = **version { - let min_php = v.get("min-php") - .and_then(|p| p.as_int()) - .unwrap_or(0); + let min_php = v.get("min-php").and_then(|p| p.as_int()).unwrap_or(0); if min_php <= PHP_VERSION_ID { - return Ok(Ok(v.iter().map(|(k, val)| (k.clone(), *val.clone())).collect())); + return Ok(Ok(v + .iter() + .map(|(k, val)| (k.clone(), *val.clone())) + .collect())); } } } @@ -139,7 +151,7 @@ impl Versions { self.versions_data = Some( self.http_downloader .get(&format!("{}://getcomposer.org/versions", protocol))? - .decode_json()? + .decode_json()?, ); } diff --git a/crates/shirabe/src/util/auth_helper.rs b/crates/shirabe/src/util/auth_helper.rs index 6368e58..98c2471 100644 --- a/crates/shirabe/src/util/auth_helper.rs +++ b/crates/shirabe/src/util/auth_helper.rs @@ -4,9 +4,9 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - base64_encode, explode, in_array, is_array, is_string, json_decode, parse_url, sprintf, - str_replace, strpos, strtolower, substr, trigger_error, trim, PhpMixed, E_USER_DEPRECATED, - PHP_URL_HOST, PHP_URL_PATH, PHP_URL_SCHEME, + E_USER_DEPRECATED, PHP_URL_HOST, PHP_URL_PATH, PHP_URL_SCHEME, PhpMixed, base64_encode, + explode, in_array, is_array, is_string, json_decode, parse_url, sprintf, str_replace, strpos, + strtolower, substr, trigger_error, trim, }; use crate::config::Config; @@ -208,8 +208,7 @@ impl AuthHelper { if let Some(arr) = decoded.as_array() { if let Some(msg) = arr.get("message") { if is_string(msg) { - git_hub_api_message = - msg.as_string().map(|s| s.to_string()); + git_hub_api_message = msg.as_string().map(|s| s.to_string()); } } } @@ -452,9 +451,7 @@ impl AuthHelper { IOInterface::NORMAL, ); let username = self.io.ask(" Username: ".to_string(), PhpMixed::Null); - let password = self - .io - .ask_and_hide_answer(" Password: ".to_string()); + let password = self.io.ask_and_hide_answer(" Password: ".to_string()); self.io.set_authentication( origin.to_string(), username.as_string().unwrap_or("").to_string(), @@ -538,10 +535,7 @@ impl AuthHelper { }; if !http_has_header { if let Some(PhpMixed::Array(http)) = options.get_mut("http") { - http.insert( - "header".to_string(), - Box::new(PhpMixed::List(vec![])), - ); + http.insert("header".to_string(), Box::new(PhpMixed::List(vec![]))); } } } @@ -691,20 +685,14 @@ impl AuthHelper { ]), true, ) { - return self.add_authentication_options( - options, - &str_replace("api.", "", origin), - url, - ); + return self.add_authentication_options(options, &str_replace("api.", "", origin), url); } // write headers back into options['http']['header'] if let Some(PhpMixed::Array(http)) = options.get_mut("http") { http.insert( "header".to_string(), - Box::new(PhpMixed::List( - headers.into_iter().map(Box::new).collect(), - )), + Box::new(PhpMixed::List(headers.into_iter().map(Box::new).collect())), ); } diff --git a/crates/shirabe/src/util/bitbucket.rs b/crates/shirabe/src/util/bitbucket.rs index 42bfea5..fd7aafc 100644 --- a/crates/shirabe/src/util/bitbucket.rs +++ b/crates/shirabe/src/util/bitbucket.rs @@ -1,10 +1,10 @@ //! ref: composer/src/Composer/Util/Bitbucket.php use indexmap::IndexMap; -use shirabe_php_shim::{time, LogicException, PhpMixed}; +use shirabe_php_shim::{LogicException, PhpMixed, time}; -use crate::config::config_source_interface::ConfigSourceInterface; use crate::config::Config; +use crate::config::config_source_interface::ConfigSourceInterface; use crate::downloader::transport_exception::TransportException; use crate::factory::Factory; use crate::io::io_interface::IOInterface; @@ -102,75 +102,71 @@ impl Bitbucket { "retry-auth-failure".to_string(), Box::new(PhpMixed::Bool(false)), ); - options.insert( - "http".to_string(), - Box::new(PhpMixed::Array(http)), - ); + options.insert("http".to_string(), Box::new(PhpMixed::Array(http))); let options = PhpMixed::Array(options); - let response = - match self - .http_downloader - .get(Self::OAUTH2_ACCESS_TOKEN_URL, &options) - { - Ok(r) => r, - Err(te) => { - if te.code == 400 { - self.io.write_error( - PhpMixed::String( - "<error>Invalid OAuth consumer provided.</error>".to_string(), - ), - true, - IOInterface::NORMAL, - ); - self.io.write_error( - PhpMixed::String("This can have three reasons:".to_string()), - true, - IOInterface::NORMAL, - ); - self.io.write_error( + let response = match self + .http_downloader + .get(Self::OAUTH2_ACCESS_TOKEN_URL, &options) + { + Ok(r) => r, + Err(te) => { + if te.code == 400 { + self.io.write_error( + PhpMixed::String( + "<error>Invalid OAuth consumer provided.</error>".to_string(), + ), + true, + IOInterface::NORMAL, + ); + self.io.write_error( + PhpMixed::String("This can have three reasons:".to_string()), + true, + IOInterface::NORMAL, + ); + self.io.write_error( PhpMixed::String( "1. You are authenticating with a bitbucket username/password combination".to_string(), ), true, IOInterface::NORMAL, ); - self.io.write_error( + self.io.write_error( PhpMixed::String( "2. You are using an OAuth consumer, but didn't configure a (dummy) callback url".to_string(), ), true, IOInterface::NORMAL, ); - self.io.write_error( + self.io.write_error( PhpMixed::String( "3. You are using an OAuth consumer, but didn't configure it as private consumer".to_string(), ), true, IOInterface::NORMAL, ); - return Ok(false); - } - if te.code == 403 || te.code == 401 { - self.io.write_error( - PhpMixed::String( - "<error>Invalid OAuth consumer provided.</error>".to_string(), - ), - true, - IOInterface::NORMAL, - ); - self.io.write_error( + return Ok(false); + } + if te.code == 403 || te.code == 401 { + self.io.write_error( + PhpMixed::String( + "<error>Invalid OAuth consumer provided.</error>".to_string(), + ), + true, + IOInterface::NORMAL, + ); + self.io.write_error( PhpMixed::String( "You can also add it manually later by using \"composer config --global --auth bitbucket-oauth.bitbucket.org <consumer-key> <consumer-secret>\"".to_string(), ), true, IOInterface::NORMAL, ); - return Ok(false); - } - return Err(te.into()); + return Ok(false); } - }; + return Err(te.into()); + } + }; let token = response.decode_json()?; let token_map = match token { @@ -196,12 +192,7 @@ impl Bitbucket { } .into()); } - self.token = Some( - token_map - .into_iter() - .map(|(k, v)| (k, *v)) - .collect(), - ); + self.token = Some(token_map.into_iter().map(|(k, v)| (k, *v)).collect()); Ok(true) } @@ -212,11 +203,8 @@ impl Bitbucket { message: Option<&str>, ) -> anyhow::Result<bool> { if let Some(msg) = message { - self.io.write_error( - PhpMixed::String(msg.to_string()), - true, - IOInterface::NORMAL, - ); + self.io + .write_error(PhpMixed::String(msg.to_string()), true, IOInterface::NORMAL); } let local_auth_config = self.config.get_local_auth_config_source(); @@ -227,11 +215,8 @@ impl Bitbucket { true, IOInterface::NORMAL, ); - self.io.write_error( - PhpMixed::String(url.to_string()), - true, - IOInterface::NORMAL, - ); + self.io + .write_error(PhpMixed::String(url.to_string()), true, IOInterface::NORMAL); let auth_config_source_name = self.config.get_auth_config_source().get_name(); let local_name_prefix = local_auth_config .as_ref() @@ -271,9 +256,7 @@ impl Bitbucket { if consumer_key.is_empty() { self.io.write_error( - PhpMixed::String( - "<warning>No consumer key given, aborting.</warning>".to_string(), - ), + PhpMixed::String("<warning>No consumer key given, aborting.</warning>".to_string()), true, IOInterface::NORMAL, ); @@ -322,7 +305,8 @@ impl Bitbucket { return Ok(false); } - let use_local = store_in_local_auth_config && self.config.get_local_auth_config_source().is_some(); + let use_local = + store_in_local_auth_config && self.config.get_local_auth_config_source().is_some(); if use_local { let mut auth_config_source = self.config.get_local_auth_config_source().unwrap(); self.store_in_auth_config( @@ -428,9 +412,7 @@ impl Bitbucket { .remove_config_setting(&format!("bitbucket-oauth.{}", origin_url))?; let token = self.token.as_ref().ok_or_else(|| LogicException { - message: format!( - "Expected a token configured with expires_in present, got null", - ), + message: format!("Expected a token configured with expires_in present, got null",), code: 0, })?; let expires_in = token @@ -438,7 +420,10 @@ impl Bitbucket { .and_then(|v| v.as_int()) .ok_or_else(|| { let token_mixed = PhpMixed::Array( - token.iter().map(|(k, v)| (k.clone(), Box::new(v.clone()))).collect(), + token + .iter() + .map(|(k, v)| (k.clone(), Box::new(v.clone()))) + .collect(), ); LogicException { message: format!( @@ -461,24 +446,17 @@ impl Bitbucket { ); consumer.insert( "access-token".to_string(), - Box::new( - token - .get("access_token") - .cloned() - .unwrap_or(PhpMixed::Null), - ), + Box::new(token.get("access_token").cloned().unwrap_or(PhpMixed::Null)), ); consumer.insert( "access-token-expiration".to_string(), Box::new(PhpMixed::Int(t + expires_in)), ); - self.config - .get_auth_config_source() - .add_config_setting( - &format!("bitbucket-oauth.{}", origin_url), - PhpMixed::Array(consumer), - )?; + self.config.get_auth_config_source().add_config_setting( + &format!("bitbucket-oauth.{}", origin_url), + PhpMixed::Array(consumer), + )?; Ok(()) } diff --git a/crates/shirabe/src/util/composer_mirror.rs b/crates/shirabe/src/util/composer_mirror.rs index 1ec3e56..0743e9f 100644 --- a/crates/shirabe/src/util/composer_mirror.rs +++ b/crates/shirabe/src/util/composer_mirror.rs @@ -39,7 +39,10 @@ impl ComposerMirror { to.push(pv); } - let url = from.iter().zip(to.iter()).fold(mirror_url.to_string(), |acc, (f, t)| acc.replace(f, t)); + let url = from + .iter() + .zip(to.iter()) + .fold(mirror_url.to_string(), |acc, (f, t)| acc.replace(f, t)); assert!(!url.is_empty()); url } @@ -50,9 +53,14 @@ impl ComposerMirror { url: &str, r#type: Option<&str>, ) -> String { - let normalized_url = if let Some(m) = Preg::match_(r"^(?:(?:https?|git)://github\.com/|git@github\.com:)([^/]+)/(.+?)(?:\.git)?$", url) { + let normalized_url = if let Some(m) = Preg::match_( + r"^(?:(?:https?|git)://github\.com/|git@github\.com:)([^/]+)/(.+?)(?:\.git)?$", + url, + ) { format!("gh-{}/{}", m[1], m[2]) - } else if let Some(m) = Preg::match_(r"^https://bitbucket\.org/([^/]+)/(.+?)(?:\.git)?/?$", url) { + } else if let Some(m) = + Preg::match_(r"^https://bitbucket\.org/([^/]+)/(.+?)(?:\.git)?/?$", url) + { format!("bb-{}/{}", m[1], m[2]) } else { Preg::replace(r"[^a-z0-9_.-]", "-", url.trim_matches('/')) @@ -64,12 +72,7 @@ impl ComposerMirror { .fold(mirror_url.to_string(), |acc, (f, t)| acc.replace(f, t)) } - pub fn process_hg_url( - mirror_url: &str, - package_name: &str, - url: &str, - r#type: &str, - ) -> String { + pub fn process_hg_url(mirror_url: &str, package_name: &str, url: &str, r#type: &str) -> String { Self::process_git_url(mirror_url, package_name, url, Some(r#type)) } } diff --git a/crates/shirabe/src/util/config_validator.rs b/crates/shirabe/src/util/config_validator.rs index c5636ad..cbba32b 100644 --- a/crates/shirabe/src/util/config_validator.rs +++ b/crates/shirabe/src/util/config_validator.rs @@ -1,17 +1,17 @@ //! ref: composer/src/Composer/Util/ConfigValidator.php -use indexmap::IndexMap; -use shirabe_php_shim::PhpMixed; -use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_external_packages::composer::spdx_licenses::spdx_licenses::SpdxLicenses; -use shirabe_external_packages::seld::json_lint::duplicate_key_exception::DuplicateKeyException; -use shirabe_external_packages::seld::json_lint::json_parser::JsonParser; use crate::io::io_interface::IOInterface; use crate::json::json_file::JsonFile; use crate::json::json_validation_exception::JsonValidationException; use crate::package::loader::array_loader::ArrayLoader; use crate::package::loader::invalid_package_exception::InvalidPackageException; use crate::package::loader::validating_array_loader::ValidatingArrayLoader; +use indexmap::IndexMap; +use shirabe_external_packages::composer::pcre::preg::Preg; +use shirabe_external_packages::composer::spdx_licenses::spdx_licenses::SpdxLicenses; +use shirabe_external_packages::seld::json_lint::duplicate_key_exception::DuplicateKeyException; +use shirabe_external_packages::seld::json_lint::json_parser::JsonParser; +use shirabe_php_shim::PhpMixed; #[derive(Debug)] pub struct ConfigValidator { @@ -25,7 +25,12 @@ impl ConfigValidator { Self { io } } - pub fn validate(&self, file: &str, array_loader_validation_flags: i64, flags: i64) -> (Vec<String>, Vec<String>, Vec<String>) { + pub fn validate( + &self, + file: &str, + array_loader_validation_flags: i64, + flags: i64, + ) -> (Vec<String>, Vec<String>, Vec<String>) { let mut errors: Vec<String> = Vec::new(); let mut publish_errors: Vec<String> = Vec::new(); let mut warnings: Vec<String> = Vec::new(); @@ -71,7 +76,10 @@ impl ConfigValidator { Err(e) => { if let Some(dup_e) = e.downcast_ref::<DuplicateKeyException>() { let details = dup_e.get_details(); - warnings.push(format!("Key {} is a duplicate in {} at line {}", details["key"], file, details["line"])); + warnings.push(format!( + "Key {} is a duplicate in {} at line {}", + details["key"], file, details["line"] + )); } } } @@ -83,20 +91,34 @@ impl ConfigValidator { }; // validate actual data - if manifest.get("license").map_or(true, |v| matches!(v, PhpMixed::Null)) || !manifest.contains_key("license") { + if manifest + .get("license") + .map_or(true, |v| matches!(v, PhpMixed::Null)) + || !manifest.contains_key("license") + { warnings.push("No license specified, it is recommended to do so. For closed-source software you may use \"proprietary\" as license.".to_string()); } else { let license_val = manifest.get("license").unwrap(); let licenses: Vec<String> = match license_val { PhpMixed::String(s) => vec![s.clone()], - PhpMixed::List(list) => list.iter().filter_map(|v| { - if let PhpMixed::String(s) = v.as_ref() { Some(s.clone()) } else { None } - }).collect(), + PhpMixed::List(list) => list + .iter() + .filter_map(|v| { + if let PhpMixed::String(s) = v.as_ref() { + Some(s.clone()) + } else { + None + } + }) + .collect(), _ => Vec::new(), }; // strip proprietary since it's not a valid SPDX identifier, but is accepted by composer - let licenses: Vec<String> = licenses.into_iter().filter(|l| l != "proprietary").collect(); + let licenses: Vec<String> = licenses + .into_iter() + .filter(|l| l != "proprietary") + .collect(); let license_validator = SpdxLicenses::new(); for license in &licenses { @@ -131,7 +153,11 @@ impl ConfigValidator { if let Some(PhpMixed::String(name)) = manifest.get("name") { if !name.is_empty() && Preg::is_match(r"{[A-Z]}", name) { - let suggest_name = Preg::replace(r"{(?:([a-z])([A-Z])|([A-Z])([A-Z][a-z]))}", r"\1\3-\2\4", name); + let suggest_name = Preg::replace( + r"{(?:([a-z])([A-Z])|([A-Z])([A-Z][a-z]))}", + r"\1\3-\2\4", + name, + ); let suggest_name = suggest_name.to_lowercase(); publish_errors.push(format!( @@ -148,14 +174,21 @@ impl ConfigValidator { } // check for require-dev overrides - if let (Some(PhpMixed::Array(require)), Some(PhpMixed::Array(require_dev))) = (manifest.get("require"), manifest.get("require-dev")) { - let require_overrides: Vec<String> = require.keys() + if let (Some(PhpMixed::Array(require)), Some(PhpMixed::Array(require_dev))) = + (manifest.get("require"), manifest.get("require-dev")) + { + let require_overrides: Vec<String> = require + .keys() .filter(|k| require_dev.contains_key(*k)) .cloned() .collect(); if !require_overrides.is_empty() { - let plural = if require_overrides.len() > 1 { "are" } else { "is" }; + let plural = if require_overrides.len() > 1 { + "are" + } else { + "is" + }; warnings.push(format!( "{} {} required both in require and require-dev, this can lead to unexpected behavior", require_overrides.join(", "), @@ -250,13 +283,21 @@ impl ConfigValidator { } } - let loader = ValidatingArrayLoader::new(ArrayLoader::new(), true, None, array_loader_validation_flags); + let loader = ValidatingArrayLoader::new( + ArrayLoader::new(), + true, + None, + array_loader_validation_flags, + ); let mut manifest_for_load = manifest.clone(); if !manifest_for_load.contains_key("version") { manifest_for_load.insert("version".to_string(), PhpMixed::String("1.0.0".to_string())); } if !manifest_for_load.contains_key("name") { - manifest_for_load.insert("name".to_string(), PhpMixed::String("dummy/dummy".to_string())); + manifest_for_load.insert( + "name".to_string(), + PhpMixed::String("dummy/dummy".to_string()), + ); } match loader.load(manifest_for_load) { Ok(_) => {} diff --git a/crates/shirabe/src/util/error_handler.rs b/crates/shirabe/src/util/error_handler.rs index 2f8c699..aff263e 100644 --- a/crates/shirabe/src/util/error_handler.rs +++ b/crates/shirabe/src/util/error_handler.rs @@ -1,12 +1,12 @@ //! ref: composer/src/Composer/Util/ErrorHandler.php -use std::sync::{Mutex, OnceLock}; +use crate::io::io_interface::IOInterface; use shirabe_php_shim::{ - debug_backtrace, error_reporting, filter_var, ini_get, is_resource, - set_error_handler, E_ALL, E_DEPRECATED, E_USER_DEPRECATED, E_WARNING, E_USER_WARNING, - FILTER_VALIDATE_BOOLEAN, PHP_EOL, STDERR, PhpMixed, ErrorException, + E_ALL, E_DEPRECATED, E_USER_DEPRECATED, E_USER_WARNING, E_WARNING, ErrorException, + FILTER_VALIDATE_BOOLEAN, PHP_EOL, PhpMixed, STDERR, debug_backtrace, error_reporting, + filter_var, ini_get, is_resource, set_error_handler, }; -use crate::io::io_interface::IOInterface; +use std::sync::{Mutex, OnceLock}; static IO: OnceLock<Mutex<Option<Box<dyn IOInterface + Send>>>> = OnceLock::new(); static HAS_SHOWN_DEPRECATION_NOTICE: Mutex<i64> = Mutex::new(0); @@ -18,7 +18,12 @@ fn io() -> &'static Mutex<Option<Box<dyn IOInterface + Send>>> { pub struct ErrorHandler; impl ErrorHandler { - pub fn handle(level: i64, message: String, file: String, line: i64) -> Result<bool, ErrorException> { + pub fn handle( + level: i64, + message: String, + file: String, + line: i64, + ) -> Result<bool, ErrorException> { let is_deprecation_notice = level == E_DEPRECATED || level == E_USER_DEPRECATED; // error code is not included in error_reporting @@ -37,10 +42,15 @@ impl ErrorHandler { // ignore some newly introduced warnings in new php versions until dependencies // can be fixed as we do not want to abort execution for those if (level == E_WARNING || level == E_USER_WARNING) - && message.contains("should either be used or intentionally ignored by casting it as (void)") + && message.contains( + "should either be used or intentionally ignored by casting it as (void)", + ) { Self::output_warning( - &format!("Ignored new PHP warning but it should be reported and fixed: {} in {}:{}", message, file, line), + &format!( + "Ignored new PHP warning but it should be reported and fixed: {} in {}:{}", + message, file, line + ), true, ); return Ok(true); @@ -67,7 +77,10 @@ impl ErrorHandler { } *HAS_SHOWN_DEPRECATION_NOTICE.lock().unwrap() = 1; drop(io_guard); - Self::output_warning(&format!("Deprecation Notice: {} in {}:{}", message, file, line), false); + Self::output_warning( + &format!("Deprecation Notice: {} in {}:{}", message, file, line), + false, + ); } Ok(true) @@ -92,7 +105,10 @@ impl ErrorHandler { .skip(2) .filter_map(|frame| { let line = frame.get("line").and_then(|v| v.as_int()); - let file = frame.get("file").and_then(|v| v.as_string()).map(|s| s.to_string()); + let file = frame + .get("file") + .and_then(|v| v.as_string()) + .map(|s| s.to_string()); if let (Some(line), Some(file)) = (line, file) { Some(format!("<warning> {}:{}</warning>", file, line)) } else { @@ -110,7 +126,11 @@ impl ErrorHandler { if output_even_without_io { if is_resource(&PhpMixed::Int(STDERR)) { - shirabe_php_shim::fwrite(PhpMixed::Int(STDERR), &format!("Warning: {}{}", message, PHP_EOL), -1); + shirabe_php_shim::fwrite( + PhpMixed::Int(STDERR), + &format!("Warning: {}{}", message, PHP_EOL), + -1, + ); } else { print!("Warning: {}{}", message, PHP_EOL); } diff --git a/crates/shirabe/src/util/filesystem.rs b/crates/shirabe/src/util/filesystem.rs index 98a54c4..6ca20cc 100644 --- a/crates/shirabe/src/util/filesystem.rs +++ b/crates/shirabe/src/util/filesystem.rs @@ -5,14 +5,14 @@ use shirabe_external_packages::react::promise::promise_interface::PromiseInterfa use shirabe_external_packages::symfony::component::filesystem::exception::io_exception::IOException; use shirabe_external_packages::symfony::component::finder::finder::Finder; use shirabe_php_shim::{ - array_pop, basename, chdir, clearstatcache, copy, count, dirname, end, error_get_last, - explode, fclose, feof, file_exists, file_get_contents, file_put_contents, filemtime, fileatime, - filesize, fopen, fread, function_exists, fwrite, implode, is_array, is_dir, is_file, is_link, - is_readable, lstat, mkdir, react_promise_resolve, rename, rmdir, rtrim, sprintf, - str_contains, str_repeat, str_replace, str_starts_with, strlen, strpos, strtolower, - strtoupper, strtr, substr, substr_count, symlink, touch, unlink, usleep, var_export, DIRECTORY_SEPARATOR, ErrorException, InvalidArgumentException, LogicException, PhpMixed, - RuntimeException, UnexpectedValueException, + RuntimeException, UnexpectedValueException, array_pop, basename, chdir, clearstatcache, copy, + count, dirname, end, error_get_last, explode, fclose, feof, file_exists, file_get_contents, + file_put_contents, fileatime, filemtime, filesize, fopen, fread, function_exists, fwrite, + implode, is_array, is_dir, is_file, is_link, is_readable, lstat, mkdir, react_promise_resolve, + rename, rmdir, rtrim, sprintf, str_contains, str_repeat, str_replace, str_starts_with, strlen, + strpos, strtolower, strtoupper, strtr, substr, substr_count, symlink, touch, unlink, usleep, + var_export, }; use crate::util::platform::Platform; @@ -54,7 +54,11 @@ impl Filesystem { count(&finder) == 0 } - pub fn empty_directory(&mut self, dir: &str, ensure_directory_exists: bool) -> anyhow::Result<()> { + pub fn empty_directory( + &mut self, + dir: &str, + ensure_directory_exists: bool, + ) -> anyhow::Result<()> { if is_link(dir) && file_exists(dir) { self.unlink(dir)?; } @@ -115,7 +119,10 @@ impl Filesystem { /// /// Uses the process component if proc_open is enabled on the PHP /// installation. - pub fn remove_directory_async(&mut self, directory: &str) -> anyhow::Result<Box<dyn PromiseInterface>> { + pub fn remove_directory_async( + &mut self, + directory: &str, + ) -> anyhow::Result<Box<dyn PromiseInterface>> { let edge_case_result = self.remove_edge_cases(directory, true)?; if let Some(r) = edge_case_result { return Ok(react_promise_resolve(PhpMixed::Bool(r))); @@ -136,25 +143,38 @@ impl Filesystem { let directory_owned = directory.to_string(); // TODO(plugin): closure capture of $this in PHP — port wires the same logic via a callback handle. - Ok(promise.then(Box::new(move |process: PhpMixed| -> Box<dyn PromiseInterface> { - // clear stat cache because external processes aren't tracked by the php stat cache - clearstatcache(false, ""); + Ok(promise.then(Box::new( + move |process: PhpMixed| -> Box<dyn PromiseInterface> { + // clear stat cache because external processes aren't tracked by the php stat cache + clearstatcache(false, ""); - let is_successful = process.as_object().map(|o| o.call_method("isSuccessful", &[]).as_bool().unwrap_or(false)).unwrap_or(false); - if is_successful && !is_dir(&directory_owned) { - return react_promise_resolve(PhpMixed::Bool(true)); - } + let is_successful = process + .as_object() + .map(|o| { + o.call_method("isSuccessful", &[]) + .as_bool() + .unwrap_or(false) + }) + .unwrap_or(false); + if is_successful && !is_dir(&directory_owned) { + return react_promise_resolve(PhpMixed::Bool(true)); + } - // PHP: \React\Promise\resolve($this->removeDirectoryPhp($directory)) - // The recursive PHP call doesn't have a clean async equivalent; we resort to a sync call. - let mut fs = Filesystem::new(None); - let res = fs.remove_directory_php(&directory_owned).unwrap_or(false); - react_promise_resolve(PhpMixed::Bool(res)) - }))) + // PHP: \React\Promise\resolve($this->removeDirectoryPhp($directory)) + // The recursive PHP call doesn't have a clean async equivalent; we resort to a sync call. + let mut fs = Filesystem::new(None); + let res = fs.remove_directory_php(&directory_owned).unwrap_or(false); + react_promise_resolve(PhpMixed::Bool(res)) + }, + ))) } /// Returns null when no edge case was hit. Otherwise a bool whether removal was successful - fn remove_edge_cases(&mut self, directory: &str, fallback_to_php: bool) -> anyhow::Result<Option<bool>> { + fn remove_edge_cases( + &mut self, + directory: &str, + fallback_to_php: bool, + ) -> anyhow::Result<Option<bool>> { if self.is_symlinked_directory(directory) { return Ok(Some(self.unlink_symlinked_directory(directory)?)); } @@ -198,7 +218,8 @@ impl Filesystem { } // PHP: $it = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS); - let mut it_result = shirabe_php_shim::recursive_directory_iterator(directory, shirabe_php_shim::SKIP_DOTS); + let mut it_result = + shirabe_php_shim::recursive_directory_iterator(directory, shirabe_php_shim::SKIP_DOTS); if let Err(e) = &it_result { if e.downcast_ref::<UnexpectedValueException>().is_some() { // re-try once after clearing the stat cache if it failed as it @@ -208,7 +229,10 @@ impl Filesystem { if !is_dir(directory) { return Ok(true); } - it_result = shirabe_php_shim::recursive_directory_iterator(directory, shirabe_php_shim::SKIP_DOTS); + it_result = shirabe_php_shim::recursive_directory_iterator( + directory, + shirabe_php_shim::SKIP_DOTS, + ); } } let it = it_result?; @@ -243,7 +267,10 @@ impl Filesystem { message: format!( "Could not delete symbolic link {}: {}", directory, - error_get_last().get("message").and_then(|v| v.as_string()).unwrap_or("") + error_get_last() + .get("message") + .and_then(|v| v.as_string()) + .unwrap_or("") ), code: 0, } @@ -255,7 +282,10 @@ impl Filesystem { message: format!( "{} does not exist and could not be created: {}", directory, - error_get_last().get("message").and_then(|v| v.as_string()).unwrap_or("") + error_get_last() + .get("message") + .and_then(|v| v.as_string()) + .unwrap_or("") ), code: 0, }; @@ -293,7 +323,10 @@ impl Filesystem { let mut message = format!( "Could not delete {}: {}", path, - error.get("message").and_then(|v| v.as_string()).unwrap_or("") + error + .get("message") + .and_then(|v| v.as_string()) + .unwrap_or("") ); if Platform::is_windows() { message.push_str("\nThis can be due to an antivirus or the Windows Search Indexer locking the file while they are analyzed"); @@ -321,7 +354,10 @@ impl Filesystem { let mut message = format!( "Could not delete {}: {}", path, - error.get("message").and_then(|v| v.as_string()).unwrap_or("") + error + .get("message") + .and_then(|v| v.as_string()) + .unwrap_or("") ); if Platform::is_windows() { message.push_str("\nThis can be due to an antivirus or the Windows Search Indexer locking the file while they are analyzed"); @@ -356,7 +392,8 @@ impl Filesystem { let target = self.normalize_path(target); if !is_dir(source) { - let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| copy(source, &target))); + let result = + std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| copy(source, &target))); match result { Ok(b) => return Ok(b), Err(payload) => { @@ -390,7 +427,8 @@ impl Filesystem { } } - let it = shirabe_php_shim::recursive_directory_iterator(source, shirabe_php_shim::SKIP_DOTS)?; + let it = + shirabe_php_shim::recursive_directory_iterator(source, shirabe_php_shim::SKIP_DOTS)?; let ri = shirabe_php_shim::recursive_iterator_iterator(it, shirabe_php_shim::SELF_FIRST); self.ensure_directory_exists(&target)?; @@ -461,7 +499,13 @@ impl Filesystem { } /// Returns the shortest path from $from to $to - pub fn find_shortest_path(&self, from: &str, to: &str, directories: bool, prefer_relative: bool) -> String { + pub fn find_shortest_path( + &self, + from: &str, + to: &str, + directories: bool, + prefer_relative: bool, + ) -> String { if !self.is_absolute_path(from) || !self.is_absolute_path(to) { // PHP throws InvalidArgumentException // Returning early-formatted Result is not possible without changing signature; panic to surface in tests. @@ -499,7 +543,8 @@ impl Filesystem { } common_path = format!("{}/", rtrim(&common_path, "/")); - let source_path_depth = substr_count(&substr(&from, strlen(&common_path) as isize, None), "/"); + let source_path_depth = + substr_count(&substr(&from, strlen(&common_path) as isize, None), "/"); let common_path_code = str_repeat("../", source_path_depth); // allow top level /foo & /bar dirs to be addressed relatively as this is common in Docker setups @@ -507,7 +552,11 @@ impl Filesystem { return to; } - let result = format!("{}{}", common_path_code, substr(&to, strlen(&common_path) as isize, None)); + let result = format!( + "{}{}", + common_path_code, + substr(&to, strlen(&common_path) as isize, None) + ); if strlen(&result) == 0 { return "./".to_string(); } @@ -516,7 +565,14 @@ impl Filesystem { } /// Returns PHP code that, when executed in $from, will return the path to $to - pub fn find_shortest_path_code(&self, from: &str, to: &str, directories: bool, static_code: bool, prefer_relative: bool) -> String { + pub fn find_shortest_path_code( + &self, + from: &str, + to: &str, + directories: bool, + static_code: bool, + prefer_relative: bool, + ) -> String { if !self.is_absolute_path(from) || !self.is_absolute_path(to) { panic!( "{}", @@ -552,11 +608,15 @@ impl Filesystem { if str_starts_with(&to, &format!("{}/", from)) { return format!( "__DIR__ . {}", - var_export(&PhpMixed::String(substr(&to, strlen(&from) as isize, None)), true) + var_export( + &PhpMixed::String(substr(&to, strlen(&from) as isize, None)), + true + ) ); } - let source_path_depth = (substr_count(&substr(&from, strlen(&common_path) as isize, None), "/") as i64) - + (if directories { 1 } else { 0 }); + let source_path_depth = + (substr_count(&substr(&from, strlen(&common_path) as isize, None), "/") as i64) + + (if directories { 1 } else { 0 }); // allow top level /foo & /bar dirs to be addressed relatively as this is common in Docker setups if !prefer_relative && "/" == common_path && source_path_depth > 1 { @@ -564,7 +624,10 @@ impl Filesystem { } let common_path_code = if static_code { - format!("__DIR__ . '{}'", str_repeat("/..", source_path_depth as usize)) + format!( + "__DIR__ . '{}'", + str_repeat("/..", source_path_depth as usize) + ) } else { format!( "{}{}{}", @@ -683,10 +746,20 @@ impl Filesystem { // on windows, \\foo indicates network paths so we exclude those from local paths, however it is unsafe // on linux as file:////foo (which would be a network path \\foo on windows) will resolve to /foo which could be a local path if Platform::is_windows() { - return Preg::is_match("{^(file://(?!//)|/(?!/)|/?[a-z]:[\\\\/]|\\.\\.[\\\\/]|[a-z0-9_.-]+[\\\\/])}i", path, None).unwrap_or(false); + return Preg::is_match( + "{^(file://(?!//)|/(?!/)|/?[a-z]:[\\\\/]|\\.\\.[\\\\/]|[a-z0-9_.-]+[\\\\/])}i", + path, + None, + ) + .unwrap_or(false); } - Preg::is_match("{^(file://|/|/?[a-z]:[\\\\/]|\\.\\.[\\\\/]|[a-z0-9_.-]+[\\\\/])}i", path, None).unwrap_or(false) + Preg::is_match( + "{^(file://|/|/?[a-z]:[\\\\/]|\\.\\.[\\\\/]|[a-z0-9_.-]+[\\\\/])}i", + path, + None, + ) + .unwrap_or(false) } pub fn get_platform_path(path: &str) -> String { @@ -708,15 +781,12 @@ impl Filesystem { } if is_file(path) { - return Silencer::call(|| { - Ok(file_get_contents(path).is_some()) - }).unwrap_or(false); + return Silencer::call(|| Ok(file_get_contents(path).is_some())).unwrap_or(false); } if is_dir(path) { - return Silencer::call(|| { - Ok(shirabe_php_shim::opendir(path).is_some()) - }).unwrap_or(false); + return Silencer::call(|| Ok(shirabe_php_shim::opendir(path).is_some())) + .unwrap_or(false); } // assume false otherwise @@ -724,7 +794,9 @@ impl Filesystem { } pub(crate) fn directory_size(&self, directory: &str) -> i64 { - let it = shirabe_php_shim::recursive_directory_iterator(directory, shirabe_php_shim::SKIP_DOTS).unwrap(); + let it = + shirabe_php_shim::recursive_directory_iterator(directory, shirabe_php_shim::SKIP_DOTS) + .unwrap(); let ri = shirabe_php_shim::recursive_iterator_iterator(it, shirabe_php_shim::CHILD_FIRST); let mut size: i64 = 0; @@ -809,14 +881,20 @@ impl Filesystem { pub fn junction(&mut self, target: &str, junction: &str) -> anyhow::Result<()> { if !Platform::is_windows() { return Err(LogicException { - message: format!("Function {} is not available on non-Windows platform", "Composer\\Util\\Filesystem"), + message: format!( + "Function {} is not available on non-Windows platform", + "Composer\\Util\\Filesystem" + ), code: 0, } .into()); } if !is_dir(target) { return Err(IOException::new( - format!("Cannot junction to \"{}\" as it is not a directory.", target), + format!( + "Cannot junction to \"{}\" as it is not a directory.", + target + ), 0, None, Some(target.to_string()), @@ -838,7 +916,10 @@ impl Filesystem { let mut output = String::new(); if self.get_process().execute(&cmd, &mut output) != 0 { return Err(IOException::new( - format!("Failed to create junction to \"{}\" at \"{}\".", target, junction), + format!( + "Failed to create junction to \"{}\" at \"{}\".", + target, junction + ), 0, None, Some(target.to_string()), @@ -891,10 +972,16 @@ impl Filesystem { if !Platform::is_windows() { return Ok(false); } - let junction = rtrim(&str_replace("/", DIRECTORY_SEPARATOR, junction), DIRECTORY_SEPARATOR); + let junction = rtrim( + &str_replace("/", DIRECTORY_SEPARATOR, junction), + DIRECTORY_SEPARATOR, + ); if !self.is_junction(&junction) { return Err(IOException::new( - format!("{} is not a junction and thus cannot be removed as one", junction), + format!( + "{} is not a junction and thus cannot be removed as one", + junction + ), 0, None, None, @@ -906,7 +993,8 @@ impl Filesystem { } pub fn file_put_contents_if_modified(&self, path: &str, content: &str) -> anyhow::Result<i64> { - let current_content = Silencer::call(|| Ok(file_get_contents(path).unwrap_or_default())).unwrap_or_default(); + let current_content = + Silencer::call(|| Ok(file_get_contents(path).unwrap_or_default())).unwrap_or_default(); if current_content.is_empty() || current_content != content { return Ok(file_put_contents(path, content) as i64); } @@ -917,8 +1005,10 @@ impl Filesystem { /// Copy file using stream_copy_to_stream to work around https://bugs.php.net/bug.php?id=6463 pub fn safe_copy(&self, source: &str, target: &str) -> anyhow::Result<()> { if !file_exists(target) || !file_exists(source) || !self.files_are_equal(source, target) { - let source_handle = fopen(source, "r").ok_or_else(|| anyhow::anyhow!("Could not open \"{}\" for reading.", source))?; - let target_handle = fopen(target, "w+").ok_or_else(|| anyhow::anyhow!("Could not open \"{}\" for writing.", target))?; + let source_handle = fopen(source, "r") + .ok_or_else(|| anyhow::anyhow!("Could not open \"{}\" for reading.", source))?; + let target_handle = fopen(target, "w+") + .ok_or_else(|| anyhow::anyhow!("Could not open \"{}\" for writing.", target))?; shirabe_php_shim::stream_copy_to_stream(&source_handle, &target_handle); fclose(&source_handle); diff --git a/crates/shirabe/src/util/forgejo.rs b/crates/shirabe/src/util/forgejo.rs index 56265b7..7f62fb9 100644 --- a/crates/shirabe/src/util/forgejo.rs +++ b/crates/shirabe/src/util/forgejo.rs @@ -135,8 +135,11 @@ impl Forgejo { }, ); - self.io - .write_error("<info>Token stored successfully.</info>", true, IOInterface::NORMAL); + self.io.write_error( + "<info>Token stored successfully.</info>", + true, + IOInterface::NORMAL, + ); Ok(Ok(true)) } diff --git a/crates/shirabe/src/util/forgejo_url.rs b/crates/shirabe/src/util/forgejo_url.rs index ef57e25..3b42f13 100644 --- a/crates/shirabe/src/util/forgejo_url.rs +++ b/crates/shirabe/src/util/forgejo_url.rs @@ -12,10 +12,16 @@ pub struct ForgejoUrl { } impl ForgejoUrl { - pub const URL_REGEX: &'static str = r"^(?:(?:https?|git)://([^/]+)/|git@([^:]+):/?)([^/]+)/([^/]+?)(?:\.git|/)?$"; + pub const URL_REGEX: &'static str = + r"^(?:(?:https?|git)://([^/]+)/|git@([^:]+):/?)([^/]+)/([^/]+?)(?:\.git|/)?$"; fn new(owner: String, repository: String, origin_url: String, api_url: String) -> Self { - Self { owner, repository, origin_url, api_url } + Self { + owner, + repository, + origin_url, + api_url, + } } pub fn create(repo_url: &str) -> Result<Self> { @@ -24,7 +30,8 @@ impl ForgejoUrl { None => Err(InvalidArgumentException { message: format!("This is not a valid Forgejo URL: {}", repo_url), code: 0, - }.into()), + } + .into()), } } @@ -32,7 +39,12 @@ impl ForgejoUrl { let repo_url = repo_url?; let m = Preg::match_(Self::URL_REGEX, repo_url)?; - let origin_url = if !m[1].is_empty() { m[1].clone() } else { m[2].clone() }.to_lowercase(); + let origin_url = if !m[1].is_empty() { + m[1].clone() + } else { + m[2].clone() + } + .to_lowercase(); let api_base = format!("{}/api/v1", origin_url); Some(Self::new( @@ -44,6 +56,9 @@ impl ForgejoUrl { } pub fn generate_ssh_url(&self) -> String { - format!("git@{}:{}/{}.git", self.origin_url, self.owner, self.repository) + format!( + "git@{}:{}/{}.git", + self.origin_url, self.owner, self.repository + ) } } diff --git a/crates/shirabe/src/util/git.rs b/crates/shirabe/src/util/git.rs index 16a1143..62d1e0e 100644 --- a/crates/shirabe/src/util/git.rs +++ b/crates/shirabe/src/util/git.rs @@ -6,10 +6,10 @@ use std::sync::Mutex; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - array_map, array_merge_recursive, clearstatcache, count, explode, implode, in_array, is_array, + InvalidArgumentException, PHP_EOL, PhpMixed, RuntimeException, array_map, + array_merge_recursive, clearstatcache, count, explode, implode, in_array, is_array, is_callable, is_dir, preg_quote, rawurldecode, rawurlencode, str_contains, str_ends_with, str_replace, str_replace_array, strlen, strpos, substr, trim, version_compare, - InvalidArgumentException, PhpMixed, RuntimeException, PHP_EOL, }; use crate::config::Config; @@ -157,7 +157,11 @@ impl Git { let cwd_string = cwd.map(|s| s.to_string()); // PHP closure: $runCommands = function ($url) use (...) { ... }; - let mut run_commands_inline = |url_arg: &str, this_process: &mut ProcessExecutor, last_cmd: &mut PhpMixed, command_output: Option<&mut PhpMixed>| -> i64 { + let mut run_commands_inline = |url_arg: &str, + this_process: &mut ProcessExecutor, + last_cmd: &mut PhpMixed, + command_output: Option<&mut PhpMixed>| + -> i64 { let collect_outputs = !command_output .as_ref() .map(|v| is_callable(v)) @@ -358,7 +362,9 @@ impl Git { self.io.as_ref(), &self.config, &self.process, - self.http_downloader.as_ref().unwrap_or(&HttpDownloader::default()), + self.http_downloader + .as_ref() + .unwrap_or(&HttpDownloader::default()), ); let message = "Cloning failed using an ssh key for authentication, enter your GitHub credentials to access private repos"; @@ -412,7 +418,9 @@ impl Git { self.io.as_ref(), &self.config, &self.process, - self.http_downloader.as_ref().unwrap_or(&HttpDownloader::default()), + self.http_downloader + .as_ref() + .unwrap_or(&HttpDownloader::default()), ); let domain = m.get(2).cloned().unwrap_or_default(); @@ -573,9 +581,12 @@ impl Git { self.io.as_ref(), &self.config, &self.process, - self.http_downloader.as_ref().unwrap_or(&HttpDownloader::default()), + self.http_downloader + .as_ref() + .unwrap_or(&HttpDownloader::default()), ); - let message = "Cloning failed, enter your GitLab credentials to access private repos"; + let message = + "Cloning failed, enter your GitLab credentials to access private repos"; if !git_lab_util.authorize_oauth(&m2) && self.io.is_interactive() { git_lab_util.authorize_oauth_interactively(&m1, &m2, Some(message)); @@ -671,10 +682,7 @@ impl Git { IOInterface::NORMAL, ); self.io.write_error( - PhpMixed::String(format!( - "<warning>{}</warning>", - trim(&error_msg, None) - )), + PhpMixed::String(format!("<warning>{}</warning>", trim(&error_msg, None))), true, IOInterface::VERBOSE, ); @@ -822,11 +830,7 @@ impl Git { "--prune".to_string(), "origin".to_string(), ], - vec![ - "git".to_string(), - "gc".to_string(), - "--auto".to_string(), - ], + vec!["git".to_string(), "gc".to_string(), "--auto".to_string()], ]; self.run_commands(commands, url, Some(dir), false, None)?; @@ -851,10 +855,7 @@ impl Git { if let Err(e) = try_result { self.io.write_error( - PhpMixed::String(format!( - "<error>Sync mirror failed: {}</error>", - e - )), + PhpMixed::String(format!("<error>Sync mirror failed: {}</error>", e)), true, IOInterface::DEBUG, ); diff --git a/crates/shirabe/src/util/github.rs b/crates/shirabe/src/util/github.rs index 23d5b1f..9911084 100644 --- a/crates/shirabe/src/util/github.rs +++ b/crates/shirabe/src/util/github.rs @@ -1,7 +1,7 @@ //! ref: composer/src/Composer/Util/GitHub.php use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_php_shim::{date, stripos, strtolower, PhpMixed}; +use shirabe_php_shim::{PhpMixed, date, stripos, strtolower}; use crate::config::Config; use crate::downloader::transport_exception::TransportException; @@ -47,9 +47,7 @@ impl GitHub { Some(arr) => arr.clone(), None => return false, }; - let origin_in_domains = domains - .values() - .any(|v| v.as_string() == Some(origin_url)); + let origin_in_domains = domains.values().any(|v| v.as_string() == Some(origin_url)); if !origin_in_domains { return false; } @@ -82,11 +80,8 @@ impl GitHub { message: Option<&str>, ) -> anyhow::Result<bool> { if let Some(msg) = message { - self.io.write_error( - PhpMixed::String(msg.to_string()), - true, - IOInterface::NORMAL, - ); + self.io + .write_error(PhpMixed::String(msg.to_string()), true, IOInterface::NORMAL); } let mut note = "Composer".to_string(); @@ -286,7 +281,8 @@ impl GitHub { } } - let use_local = store_in_local_auth_config && self.config.get_local_auth_config_source().is_some(); + let use_local = + store_in_local_auth_config && self.config.get_local_auth_config_source().is_some(); let auth_config_source_name; if use_local { let mut auth_config_source = self.config.get_local_auth_config_source().unwrap(); @@ -367,9 +363,7 @@ impl GitHub { pub fn is_rate_limited(&self, headers: &[String]) -> bool { for header in headers { - if Preg::is_match(r"{^x-ratelimit-remaining: *0$}i", header.trim()) - .unwrap_or(false) - { + if Preg::is_match(r"{^x-ratelimit-remaining: *0$}i", header.trim()).unwrap_or(false) { return true; } } @@ -379,9 +373,7 @@ impl GitHub { pub fn requires_sso(&self, headers: &[String]) -> bool { for header in headers { - if Preg::is_match(r"{^x-github-sso: required}i", header.trim()) - .unwrap_or(false) - { + if Preg::is_match(r"{^x-github-sso: required}i", header.trim()).unwrap_or(false) { return true; } } diff --git a/crates/shirabe/src/util/gitlab.rs b/crates/shirabe/src/util/gitlab.rs index b245fb9..054dc9a 100644 --- a/crates/shirabe/src/util/gitlab.rs +++ b/crates/shirabe/src/util/gitlab.rs @@ -2,7 +2,7 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_php_shim::{http_build_query, json_decode, time, PhpMixed, RuntimeException}; +use shirabe_php_shim::{PhpMixed, RuntimeException, http_build_query, json_decode, time}; use crate::config::Config; use crate::downloader::transport_exception::TransportException; @@ -41,8 +41,8 @@ impl GitLab { pub fn authorize_oauth(&mut self, origin_url: &str) -> bool { // before composer 1.9, origin URLs had no port number in them - let bc_origin_url = Preg::replace("{:\\d+}", "", origin_url) - .unwrap_or_else(|_| origin_url.to_string()); + let bc_origin_url = + Preg::replace("{:\\d+}", "", origin_url).unwrap_or_else(|_| origin_url.to_string()); let gitlab_domains = self.config.get("gitlab-domains"); let domains = match gitlab_domains.as_array() { @@ -50,7 +50,9 @@ impl GitLab { None => return false, }; let origin_in_domains = domains.values().any(|v| v.as_string() == Some(origin_url)); - let bc_in_domains = domains.values().any(|v| v.as_string() == Some(bc_origin_url.as_str())); + let bc_in_domains = domains + .values() + .any(|v| v.as_string() == Some(bc_origin_url.as_str())); if !origin_in_domains && !bc_in_domains { return false; } @@ -246,9 +248,8 @@ impl GitLab { match e.downcast::<TransportException>() { Ok(te) if te.code == 403 || te.code == 401 => { if te.code == 401 { - let response = te - .get_response() - .and_then(|r| json_decode(r, true).ok()); + let response = + te.get_response().and_then(|r| json_decode(r, true).ok()); let is_invalid_grant = response .as_ref() .and_then(|r| r.as_array()) @@ -371,10 +372,7 @@ impl GitLab { Err(e) => match e.downcast::<TransportException>() { Ok(te) => { self.io.write_error( - PhpMixed::String(format!( - "Couldn't refresh access token: {}", - te.message - )), + PhpMixed::String(format!("Couldn't refresh access token: {}", te.message)), true, IOInterface::NORMAL, ); @@ -398,21 +396,15 @@ impl GitLab { ); // store value in user config in auth file - self.config - .get_auth_config_source() - .add_config_setting( - &format!("gitlab-oauth.{}", origin_url), - Self::build_oauth_config(&response, &access_token), - )?; + self.config.get_auth_config_source().add_config_setting( + &format!("gitlab-oauth.{}", origin_url), + Self::build_oauth_config(&response, &access_token), + )?; Ok(true) } - fn create_token( - &mut self, - scheme: &str, - origin_url: &str, - ) -> anyhow::Result<PhpMixed> { + fn create_token(&mut self, scheme: &str, origin_url: &str) -> anyhow::Result<PhpMixed> { let username = match self.io.ask("Username: ".to_string(), PhpMixed::Null) { PhpMixed::String(s) => s, _ => String::new(), @@ -448,10 +440,7 @@ impl GitLab { .collect(), )), ); - http_inner.insert( - "content".to_string(), - Box::new(PhpMixed::String(data)), - ); + http_inner.insert("content".to_string(), Box::new(PhpMixed::String(data))); let mut options: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); options.insert( "retry-auth-failure".to_string(), @@ -495,11 +484,7 @@ impl GitLab { false } - fn refresh_token( - &mut self, - scheme: &str, - origin_url: &str, - ) -> anyhow::Result<PhpMixed> { + fn refresh_token(&mut self, scheme: &str, origin_url: &str) -> anyhow::Result<PhpMixed> { let auth_tokens = self.config.get("gitlab-oauth"); let refresh_token = auth_tokens .as_array() @@ -513,13 +498,10 @@ impl GitLab { Some(t) => t, None => { return Err(RuntimeException { - message: format!( - "No GitLab refresh token present for {}.", - origin_url - ), + message: format!("No GitLab refresh token present for {}.", origin_url), code: 0, } - .into()) + .into()); } }; @@ -547,10 +529,7 @@ impl GitLab { .collect(), )), ); - http_inner.insert( - "content".to_string(), - Box::new(PhpMixed::String(data)), - ); + http_inner.insert("content".to_string(), Box::new(PhpMixed::String(data))); let mut options: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); options.insert( "retry-auth-failure".to_string(), diff --git a/crates/shirabe/src/util/hg.rs b/crates/shirabe/src/util/hg.rs index d5e867c..c3f4b6e 100644 --- a/crates/shirabe/src/util/hg.rs +++ b/crates/shirabe/src/util/hg.rs @@ -1,13 +1,13 @@ //! ref: composer/src/Composer/Util/Hg.php -use std::sync::OnceLock; -use anyhow::Result; -use shirabe_php_shim::rawurlencode; -use shirabe_external_packages::composer::pcre::preg::Preg; use crate::config::Config; use crate::io::io_interface::IOInterface; use crate::util::process_executor::ProcessExecutor; use crate::util::url::Url; +use anyhow::Result; +use shirabe_external_packages::composer::pcre::preg::Preg; +use shirabe_php_shim::rawurlencode; +use std::sync::OnceLock; static VERSION: OnceLock<Option<String>> = OnceLock::new(); @@ -34,7 +34,11 @@ impl Hg { // Try as is let command = command_callable(url.clone()); let mut ignored_output = String::new(); - if self.process.execute(&command, &mut ignored_output, cwd.clone()) == 0 { + if self + .process + .execute(&command, &mut ignored_output, cwd.clone()) + == 0 + { return Ok(()); } @@ -45,7 +49,10 @@ impl Hg { )?; if let Some(matches) = matches { - if self.io.has_authentication(matches.get("host").map(|s| s.as_str()).unwrap_or("")) { + if self + .io + .has_authentication(matches.get("host").map(|s| s.as_str()).unwrap_or("")) + { let authenticated_url = if matches.get("proto").map(|s| s.as_str()) == Some("ssh") { let user = if let Some(u) = matches.get("user") { format!("{}@", rawurlencode(u)) @@ -60,7 +67,9 @@ impl Hg { matches.get("path").unwrap_or(&String::new()), ) } else { - let auth = self.io.get_authentication(matches.get("host").map(|s| s.as_str()).unwrap_or("")); + let auth = self + .io + .get_authentication(matches.get("host").map(|s| s.as_str()).unwrap_or("")); format!( "{}://{}:{}@{}{}", matches.get("proto").unwrap_or(&String::new()), @@ -78,7 +87,8 @@ impl Hg { } let error = self.process.get_error_output(); - return self.throw_exception(&format!("Failed to clone {}, \n\n{}", url, error), &url); + return self + .throw_exception(&format!("Failed to clone {}, \n\n{}", url, error), &url); } } @@ -91,32 +101,38 @@ impl Hg { fn throw_exception(&self, message: &str, url: &str) -> Result<()> { if Self::get_version(&self.process).is_none() { - anyhow::bail!("{}", Url::sanitize(&format!( - "Failed to clone {}, hg was not found, check that it is installed and in your PATH env.\n\n{}", - url, - self.process.get_error_output() - ))); + anyhow::bail!( + "{}", + Url::sanitize(&format!( + "Failed to clone {}, hg was not found, check that it is installed and in your PATH env.\n\n{}", + url, + self.process.get_error_output() + )) + ); } anyhow::bail!("{}", Url::sanitize(message)); } pub fn get_version(process: &ProcessExecutor) -> Option<&'static str> { - VERSION.get_or_init(|| { - let mut output = String::new(); - if process.execute( - &["hg".to_string(), "--version".to_string()], - &mut output, - None, - ) == 0 { - if let Ok(Some(matches)) = Preg::is_match_with_indexed_captures( - r"/^.+? (\d+(?:\.\d+)+)(?:\+.*?)?\)?\r?\n/", - &output, - ) { - return matches.into_iter().nth(1); + VERSION + .get_or_init(|| { + let mut output = String::new(); + if process.execute( + &["hg".to_string(), "--version".to_string()], + &mut output, + None, + ) == 0 + { + if let Ok(Some(matches)) = Preg::is_match_with_indexed_captures( + r"/^.+? (\d+(?:\.\d+)+)(?:\+.*?)?\)?\r?\n/", + &output, + ) { + return matches.into_iter().nth(1); + } } - } - None - }).as_deref() + None + }) + .as_deref() } } diff --git a/crates/shirabe/src/util/http/curl_downloader.rs b/crates/shirabe/src/util/http/curl_downloader.rs index 4105f5e..e4d8b78 100644 --- a/crates/shirabe/src/util/http/curl_downloader.rs +++ b/crates/shirabe/src/util/http/curl_downloader.rs @@ -6,22 +6,23 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - array_diff, array_diff_key, array_merge, count, curl_errno, curl_error, curl_getinfo, - curl_handle_id, curl_init, curl_multi_add_handle, curl_multi_exec, curl_multi_info_read, - curl_multi_init, curl_multi_select, curl_multi_setopt, curl_setopt, curl_setopt_array, - curl_share_init, curl_share_setopt, curl_strerror, curl_version, defined, explode, fclose, - fopen, function_exists, implode, in_array, ini_get, is_resource, json_decode, max, parse_url, - preg_quote, rename, restore_error_handler, rewind, rtrim, set_error_handler_closure, sprintf, - str_contains, strpos, stream_get_contents, stream_get_contents_with_max, stripos, substr, - unlink_silent, usleep, var_export, CurlMultiHandle, CurlShareHandle, LogicException, PhpMixed, - RuntimeException, CURL_HTTP_VERSION_2_0, CURL_HTTP_VERSION_3, CURL_IPRESOLVE_V4, - CURL_IPRESOLVE_V6, CURL_LOCK_DATA_COOKIE, CURL_LOCK_DATA_DNS, CURL_LOCK_DATA_SSL_SESSION, - CURL_VERSION_HTTP2, CURL_VERSION_HTTP3, CURL_VERSION_LIBZ, CURLE_OK, CURLM_BAD_EASY_HANDLE, - CURLM_BAD_HANDLE, CURLM_CALL_MULTI_PERFORM, CURLM_INTERNAL_ERROR, CURLM_OK, CURLM_OUT_OF_MEMORY, + CURL_HTTP_VERSION_2_0, CURL_HTTP_VERSION_3, CURL_IPRESOLVE_V4, CURL_IPRESOLVE_V6, + CURL_LOCK_DATA_COOKIE, CURL_LOCK_DATA_DNS, CURL_LOCK_DATA_SSL_SESSION, CURL_VERSION_HTTP2, + CURL_VERSION_HTTP3, CURL_VERSION_LIBZ, CURLE_OK, CURLM_BAD_EASY_HANDLE, CURLM_BAD_HANDLE, + CURLM_CALL_MULTI_PERFORM, CURLM_INTERNAL_ERROR, CURLM_OK, CURLM_OUT_OF_MEMORY, CURLMOPT_MAX_HOST_CONNECTIONS, CURLMOPT_PIPELINING, CURLOPT_CONNECTTIMEOUT, CURLOPT_ENCODING, CURLOPT_FILE, CURLOPT_FOLLOWLOCATION, CURLOPT_HTTP_VERSION, CURLOPT_IPRESOLVE, CURLOPT_PROTOCOLS, CURLOPT_SHARE, CURLOPT_TIMEOUT, CURLOPT_URL, CURLOPT_WRITEHEADER, - CURLPROTO_HTTP, CURLPROTO_HTTPS, CURLSHOPT_SHARE, PHP_VERSION_ID, + CURLPROTO_HTTP, CURLPROTO_HTTPS, CURLSHOPT_SHARE, CurlMultiHandle, CurlShareHandle, + LogicException, PHP_VERSION_ID, PhpMixed, RuntimeException, array_diff, array_diff_key, + array_merge, count, curl_errno, curl_error, curl_getinfo, curl_handle_id, curl_init, + curl_multi_add_handle, curl_multi_exec, curl_multi_info_read, curl_multi_init, + curl_multi_select, curl_multi_setopt, curl_setopt, curl_setopt_array, curl_share_init, + curl_share_setopt, curl_strerror, curl_version, defined, explode, fclose, fopen, + function_exists, implode, in_array, ini_get, is_resource, json_decode, max, parse_url, + preg_quote, rename, restore_error_handler, rewind, rtrim, set_error_handler_closure, sprintf, + str_contains, stream_get_contents, stream_get_contents_with_max, stripos, strpos, substr, + unlink_silent, usleep, var_export, }; use crate::config::Config; @@ -72,7 +73,10 @@ const BAD_MULTIPLEXING_CURL_VERSIONS: &[&str] = &["7.87.0", "7.88.0", "7.88.1"]; /// @var mixed[] fn options_static() -> IndexMap<String, IndexMap<String, i64>> { let mut http: IndexMap<String, i64> = IndexMap::new(); - http.insert("method".to_string(), shirabe_php_shim::CURLOPT_CUSTOMREQUEST); + http.insert( + "method".to_string(), + shirabe_php_shim::CURLOPT_CUSTOMREQUEST, + ); http.insert("content".to_string(), shirabe_php_shim::CURLOPT_POSTFIELDS); http.insert("header".to_string(), shirabe_php_shim::CURLOPT_HTTPHEADER); http.insert("timeout".to_string(), CURLOPT_TIMEOUT); @@ -80,11 +84,20 @@ fn options_static() -> IndexMap<String, IndexMap<String, i64>> { let mut ssl: IndexMap<String, i64> = IndexMap::new(); ssl.insert("cafile".to_string(), shirabe_php_shim::CURLOPT_CAINFO); ssl.insert("capath".to_string(), shirabe_php_shim::CURLOPT_CAPATH); - ssl.insert("verify_peer".to_string(), shirabe_php_shim::CURLOPT_SSL_VERIFYPEER); - ssl.insert("verify_peer_name".to_string(), shirabe_php_shim::CURLOPT_SSL_VERIFYHOST); + ssl.insert( + "verify_peer".to_string(), + shirabe_php_shim::CURLOPT_SSL_VERIFYPEER, + ); + ssl.insert( + "verify_peer_name".to_string(), + shirabe_php_shim::CURLOPT_SSL_VERIFYHOST, + ); ssl.insert("local_cert".to_string(), shirabe_php_shim::CURLOPT_SSLCERT); ssl.insert("local_pk".to_string(), shirabe_php_shim::CURLOPT_SSLKEY); - ssl.insert("passphrase".to_string(), shirabe_php_shim::CURLOPT_SSLKEYPASSWD); + ssl.insert( + "passphrase".to_string(), + shirabe_php_shim::CURLOPT_SSLKEYPASSWD, + ); let mut out: IndexMap<String, IndexMap<String, i64>> = IndexMap::new(); out.insert("http".to_string(), http); @@ -164,7 +177,11 @@ impl CurlDownloader { ); } if defined("CURLMOPT_MAX_HOST_CONNECTIONS") && !defined("HHVM_VERSION") { - curl_multi_setopt(&multi_handle, CURLMOPT_MAX_HOST_CONNECTIONS, PhpMixed::Int(8)); + curl_multi_setopt( + &multi_handle, + CURLMOPT_MAX_HOST_CONNECTIONS, + PhpMixed::Int(8), + ); } } @@ -172,14 +189,17 @@ impl CurlDownloader { let sh = curl_share_init(); curl_share_setopt(&sh, CURLSHOPT_SHARE, PhpMixed::Int(CURL_LOCK_DATA_COOKIE)); curl_share_setopt(&sh, CURLSHOPT_SHARE, PhpMixed::Int(CURL_LOCK_DATA_DNS)); - curl_share_setopt(&sh, CURLSHOPT_SHARE, PhpMixed::Int(CURL_LOCK_DATA_SSL_SESSION)); + curl_share_setopt( + &sh, + CURLSHOPT_SHARE, + PhpMixed::Int(CURL_LOCK_DATA_SSL_SESSION), + ); share_handle = Some(sh); } // TODO(phase-b): clone io/config for AuthHelper construction without consuming. - let auth_helper = AuthHelper::new(unsafe { std::mem::zeroed() }, unsafe { - std::mem::zeroed() - }); + let auth_helper = + AuthHelper::new(unsafe { std::mem::zeroed() }, unsafe { std::mem::zeroed() }); let mut multi_errors: IndexMap<i64, Vec<String>> = IndexMap::new(); multi_errors.insert( @@ -385,7 +405,11 @@ impl CurlDownloader { ); curl_setopt(&curl_handle, CURLOPT_WRITEHEADER, header_handle.clone()); curl_setopt(&curl_handle, CURLOPT_FILE, body_handle.clone()); - curl_setopt(&curl_handle, CURLOPT_ENCODING, PhpMixed::String(String::new())); // let cURL set the Accept-Encoding header to what it supports + curl_setopt( + &curl_handle, + CURLOPT_ENCODING, + PhpMixed::String(String::new()), + ); // let cURL set the Accept-Encoding header to what it supports curl_setopt( &curl_handle, CURLOPT_PROTOCOLS, @@ -393,9 +417,17 @@ impl CurlDownloader { ); if attributes.get("ipResolve").and_then(|v| v.as_int()) == Some(4) { - curl_setopt(&curl_handle, CURLOPT_IPRESOLVE, PhpMixed::Int(CURL_IPRESOLVE_V4)); + curl_setopt( + &curl_handle, + CURLOPT_IPRESOLVE, + PhpMixed::Int(CURL_IPRESOLVE_V4), + ); } else if attributes.get("ipResolve").and_then(|v| v.as_int()) == Some(6) { - curl_setopt(&curl_handle, CURLOPT_IPRESOLVE, PhpMixed::Int(CURL_IPRESOLVE_V6)); + curl_setopt( + &curl_handle, + CURLOPT_IPRESOLVE, + PhpMixed::Int(CURL_IPRESOLVE_V6), + ); } if function_exists("curl_share_init") { @@ -416,10 +448,7 @@ impl CurlDownloader { .entry("http".to_string()) .or_insert(PhpMixed::Array(IndexMap::new())); if let PhpMixed::Array(a) = http { - a.insert( - "header".to_string(), - Box::new(PhpMixed::List(Vec::new())), - ); + a.insert("header".to_string(), Box::new(PhpMixed::List(Vec::new()))); } } @@ -440,7 +469,9 @@ impl CurlDownloader { .into_iter() .map(|s| Box::new(PhpMixed::String(s))) .collect(); - new_list.push(Box::new(PhpMixed::String("Connection: keep-alive".to_string()))); + new_list.push(Box::new(PhpMixed::String( + "Connection: keep-alive".to_string(), + ))); *list = new_list; } } @@ -601,10 +632,7 @@ impl CurlDownloader { .collect(), ), ); - job.insert( - "progress".to_string(), - PhpMixed::Array(progress.clone()), - ); + job.insert("progress".to_string(), PhpMixed::Array(progress.clone())); // curlHandle, headerHandle, bodyHandle, resolve, reject are PHP resources/callables; // stored as opaque PhpMixed::Null placeholders (real values live in Rust-side fields). // TODO(phase-b): wire handle/closure storage properly. @@ -644,12 +672,12 @@ impl CurlDownloader { _ => None, }) .unwrap_or_default(); - let if_modified = - if stripos(&implode(",", &header_strings), "if-modified-since:").is_some() { - " if modified" - } else { - "" - }; + let if_modified = if stripos(&implode(",", &header_strings), "if-modified-since:").is_some() + { + " if modified" + } else { + "" + }; if attributes.get("redirects").and_then(|v| v.as_int()) == Some(0) && attributes.get("retries").and_then(|v| v.as_int()) == Some(0) { @@ -737,10 +765,7 @@ impl CurlDownloader { .get("handle") .map(|b| (**b).clone()) .unwrap_or(PhpMixed::Null); - let result_code: i64 = progress - .get("result") - .and_then(|b| b.as_int()) - .unwrap_or(0); + let result_code: i64 = progress.get("result").and_then(|b| b.as_int()).unwrap_or(0); // TODO(phase-b): correlate handle in `progress['handle']` to its job id. let i: i64 = 0; if !self.jobs.contains_key(&i) { @@ -908,7 +933,9 @@ impl CurlDownloader { } // TODO: Remove this as soon as https://github.com/curl/curl/issues/10591 is resolved - if errno == 55 /* CURLE_SEND_ERROR */ { + if errno == 55 + /* CURLE_SEND_ERROR */ + { self.io.write_error( PhpMixed::String(format!( "Retrying ({}) {} due to curl error {}", @@ -966,28 +993,18 @@ impl CurlDownloader { ))); } status_code = progress.get("http_code").and_then(|b| b.as_int()); - rewind( - job.get("headerHandle") - .cloned() - .unwrap_or(PhpMixed::Null), - ); + rewind(job.get("headerHandle").cloned().unwrap_or(PhpMixed::Null)); headers = Some(explode( "\r\n", &rtrim( &stream_get_contents( - job.get("headerHandle") - .cloned() - .unwrap_or(PhpMixed::Null), + job.get("headerHandle").cloned().unwrap_or(PhpMixed::Null), ) .unwrap_or_default(), None, ), )); - fclose( - job.get("headerHandle") - .cloned() - .unwrap_or(PhpMixed::Null), - ); + fclose(job.get("headerHandle").cloned().unwrap_or(PhpMixed::Null)); if status_code == Some(0) { anyhow::bail!( @@ -1025,16 +1042,10 @@ impl CurlDownloader { if let Some(PhpMixed::String(filename)) = job.get("filename") { let mut c: PhpMixed = PhpMixed::String(format!("{}~", filename)); if status_code.unwrap_or(0) >= 300 { - rewind( - job.get("bodyHandle") - .cloned() - .unwrap_or(PhpMixed::Null), - ); + rewind(job.get("bodyHandle").cloned().unwrap_or(PhpMixed::Null)); c = PhpMixed::String( stream_get_contents( - job.get("bodyHandle") - .cloned() - .unwrap_or(PhpMixed::Null), + job.get("bodyHandle").cloned().unwrap_or(PhpMixed::Null), ) .unwrap_or_default(), ); @@ -1079,16 +1090,10 @@ impl CurlDownloader { .and_then(|v| v.as_array()) .and_then(|a| a.get("max_file_size")) .and_then(|b| b.as_int()); - rewind( - job.get("bodyHandle") - .cloned() - .unwrap_or(PhpMixed::Null), - ); + rewind(job.get("bodyHandle").cloned().unwrap_or(PhpMixed::Null)); if let Some(max_file_size) = max_file_size { let c = stream_get_contents_with_max( - job.get("bodyHandle") - .cloned() - .unwrap_or(PhpMixed::Null), + job.get("bodyHandle").cloned().unwrap_or(PhpMixed::Null), Some(max_file_size), ); // Gzipped responses with missing Content-Length header cannot be detected during the file download @@ -1113,9 +1118,7 @@ impl CurlDownloader { } else { contents = PhpMixed::String( stream_get_contents( - job.get("bodyHandle") - .cloned() - .unwrap_or(PhpMixed::Null), + job.get("bodyHandle").cloned().unwrap_or(PhpMixed::Null), ) .unwrap_or_default(), ); @@ -1155,11 +1158,7 @@ impl CurlDownloader { <dyn IOInterface>::DEBUG, ); } - fclose( - job.get("bodyHandle") - .cloned() - .unwrap_or(PhpMixed::Null), - ); + fclose(job.get("bodyHandle").cloned().unwrap_or(PhpMixed::Null)); let response_ref = response.as_ref().unwrap(); if response_ref.inner.get_status_code() >= 300 @@ -1169,10 +1168,7 @@ impl CurlDownloader { HttpDownloader::output_warnings( &*self.io, job.get("origin").and_then(|v| v.as_string()).unwrap_or(""), - &match json_decode( - response_ref.inner.get_body().unwrap_or(""), - true, - )? { + &match json_decode(response_ref.inner.get_body().unwrap_or(""), true)? { PhpMixed::Array(a) => a.into_iter().map(|(k, v)| (k, *v)).collect(), _ => IndexMap::new(), }, @@ -1330,10 +1326,7 @@ impl CurlDownloader { return Ok(Ok(())); } - let status_msg = response_ref - .inner - .get_status_message() - .unwrap_or_default(); + let status_msg = response_ref.inner.get_status_message().unwrap_or_default(); return Ok(Err(self.fail_response( &job, response.as_ref().unwrap(), @@ -1514,29 +1507,29 @@ impl CurlDownloader { let job = self.jobs.get(&i).cloned().unwrap_or_default(); self.reject_job( &job, - anyhow::anyhow!(TransportException::new( - sprintf( - "IP \"%s\" is blocked for \"%s\".", - &[ - (**primary_ip).clone(), - progress_now - .get("url") - .map(|b| (**b).clone()) - .unwrap_or(PhpMixed::Null), - ], - ), - 0, - ) - .message), + anyhow::anyhow!( + TransportException::new( + sprintf( + "IP \"%s\" is blocked for \"%s\".", + &[ + (**primary_ip).clone(), + progress_now + .get("url") + .map(|b| (**b).clone()) + .unwrap_or(PhpMixed::Null), + ], + ), + 0, + ) + .message + ), ); } if let Some(job) = self.jobs.get_mut(&i) { job.insert( "primaryIp".to_string(), - PhpMixed::String( - primary_ip.as_string().unwrap_or("").to_string(), - ), + PhpMixed::String(primary_ip.as_string().unwrap_or("").to_string()), ); } } @@ -1688,7 +1681,10 @@ impl CurlDownloader { || substr(location_header.as_deref().unwrap_or(""), -4, None) != ".zip") && Preg::is_match( r"{^text/html\b}i", - &response.inner.get_header("content-type").unwrap_or_default(), + &response + .inner + .get_header("content-type") + .unwrap_or_default(), ) { needs_auth_retry = Some("Bitbucket requires authentication and it was not provided"); @@ -1857,7 +1853,9 @@ impl CurlDownloader { ), &PhpMixed::List(vec![ Box::new(PhpMixed::String("application/json".to_string())), - Box::new(PhpMixed::String("application/json; charset=utf-8".to_string())), + Box::new(PhpMixed::String( + "application/json; charset=utf-8".to_string(), + )), ]), true, ) { @@ -1924,10 +1922,7 @@ impl CurlDownloader { } } -fn maps_equal( - a: &IndexMap<String, Box<PhpMixed>>, - b: &IndexMap<String, Box<PhpMixed>>, -) -> bool { +fn maps_equal(a: &IndexMap<String, Box<PhpMixed>>, b: &IndexMap<String, Box<PhpMixed>>) -> bool { if a.len() != b.len() { return false; } diff --git a/crates/shirabe/src/util/http/mod.rs b/crates/shirabe/src/util/http/mod.rs new file mode 100644 index 0000000..4ee97c5 --- /dev/null +++ b/crates/shirabe/src/util/http/mod.rs @@ -0,0 +1,6 @@ +pub mod curl_downloader; +pub mod curl_response; +pub mod proxy_item; +pub mod proxy_manager; +pub mod request_proxy; +pub mod response; diff --git a/crates/shirabe/src/util/http/proxy_item.rs b/crates/shirabe/src/util/http/proxy_item.rs index f32325d..0e30c44 100644 --- a/crates/shirabe/src/util/http/proxy_item.rs +++ b/crates/shirabe/src/util/http/proxy_item.rs @@ -1,10 +1,10 @@ //! ref: composer/src/Composer/Util/Http/ProxyItem.php +use crate::util::http::request_proxy::RequestProxy; use indexmap::IndexMap; use shirabe_php_shim::{ - base64_encode, parse_url_all, rawurldecode, strpbrk, PhpMixed, RuntimeException, + PhpMixed, RuntimeException, base64_encode, parse_url_all, rawurldecode, strpbrk, }; -use crate::util::http::request_proxy::RequestProxy; #[derive(Debug)] pub struct ProxyItem { @@ -20,12 +20,20 @@ impl ProxyItem { let syntax_error = format!("unsupported `{}` syntax", env_name); if strpbrk(&proxy_url, "\r\n\t").is_some() { - return Err(RuntimeException { message: syntax_error, code: 0 }); + return Err(RuntimeException { + message: syntax_error, + code: 0, + }); } let proxy_parsed = parse_url_all(&proxy_url); let proxy = match proxy_parsed.as_array() { - None => return Err(RuntimeException { message: syntax_error, code: 0 }), + None => { + return Err(RuntimeException { + message: syntax_error, + code: 0, + }); + } Some(a) => a.clone(), }; @@ -37,7 +45,10 @@ impl ProxyItem { } let scheme = if proxy.contains_key("scheme") { - format!("{}://", proxy["scheme"].as_string().unwrap_or("").to_lowercase()) + format!( + "{}://", + proxy["scheme"].as_string().unwrap_or("").to_lowercase() + ) } else { "http://".to_string() }; @@ -92,13 +103,13 @@ impl ProxyItem { return Err(RuntimeException { message: format!("unable to find proxy port in {}", env_name), code: 0, - }) + }); } Some(0) => { return Err(RuntimeException { message: format!("port 0 is reserved in {}", env_name), code: 0, - }) + }); } Some(p) => p, }; diff --git a/crates/shirabe/src/util/http/proxy_manager.rs b/crates/shirabe/src/util/http/proxy_manager.rs index 319a2c4..e2838e6 100644 --- a/crates/shirabe/src/util/http/proxy_manager.rs +++ b/crates/shirabe/src/util/http/proxy_manager.rs @@ -45,9 +45,15 @@ impl ProxyManager { self.http_proxy.is_some() || self.https_proxy.is_some() } - pub fn get_proxy_for_request(&self, request_url: &str) -> Result<RequestProxy, TransportException> { + pub fn get_proxy_for_request( + &self, + request_url: &str, + ) -> Result<RequestProxy, TransportException> { if let Some(ref error) = self.error { - return Err(TransportException::new(format!("Unable to use a proxy: {}", error))); + return Err(TransportException::new(format!( + "Unable to use a proxy: {}", + error + ))); } let scheme = request_url.split("://").next().unwrap_or("").to_string(); diff --git a/crates/shirabe/src/util/http/request_proxy.rs b/crates/shirabe/src/util/http/request_proxy.rs index 1405a32..5bbf5ce 100644 --- a/crates/shirabe/src/util/http/request_proxy.rs +++ b/crates/shirabe/src/util/http/request_proxy.rs @@ -2,9 +2,9 @@ use indexmap::IndexMap; use shirabe_php_shim::{ - curl_version, PhpMixed, CURLAUTH_BASIC, CURL_VERSION_HTTPS_PROXY, CURLOPT_NOPROXY, - CURLOPT_PROXY, CURLOPT_PROXY_CAINFO, CURLOPT_PROXY_CAPATH, CURLOPT_PROXYAUTH, - CURLOPT_PROXYUSERPWD, InvalidArgumentException, + CURL_VERSION_HTTPS_PROXY, CURLAUTH_BASIC, CURLOPT_NOPROXY, CURLOPT_PROXY, CURLOPT_PROXY_CAINFO, + CURLOPT_PROXY_CAPATH, CURLOPT_PROXYAUTH, CURLOPT_PROXYUSERPWD, InvalidArgumentException, + PhpMixed, curl_version, }; use crate::downloader::transport_exception::TransportException; @@ -21,8 +21,18 @@ pub struct RequestProxy { } impl RequestProxy { - pub fn new(url: Option<String>, auth: Option<String>, context_options: Option<ContextOptions>, status: Option<String>) -> Self { - Self { url, auth, context_options, status } + pub fn new( + url: Option<String>, + auth: Option<String>, + context_options: Option<ContextOptions>, + status: Option<String>, + ) -> Self { + Self { + url, + auth, + context_options, + status, + } } pub fn none() -> Self { @@ -37,13 +47,22 @@ impl RequestProxy { self.context_options.as_ref() } - pub fn get_curl_options(&self, ssl_options: &IndexMap<String, PhpMixed>) -> Result<IndexMap<i64, PhpMixed>, TransportException> { + pub fn get_curl_options( + &self, + ssl_options: &IndexMap<String, PhpMixed>, + ) -> Result<IndexMap<i64, PhpMixed>, TransportException> { if self.is_secure() && !self.supports_secure_proxy() { - return Err(TransportException::new("Cannot use an HTTPS proxy. PHP >= 7.3 and cUrl >= 7.52.0 are required.".to_string())); + return Err(TransportException::new( + "Cannot use an HTTPS proxy. PHP >= 7.3 and cUrl >= 7.52.0 are required." + .to_string(), + )); } let mut options: IndexMap<i64, PhpMixed> = IndexMap::new(); - options.insert(CURLOPT_PROXY, PhpMixed::String(self.url.as_deref().unwrap_or("").to_string())); + options.insert( + CURLOPT_PROXY, + PhpMixed::String(self.url.as_deref().unwrap_or("").to_string()), + ); if self.url.is_some() { options.insert(CURLOPT_NOPROXY, PhpMixed::String(String::new())); @@ -100,7 +119,10 @@ impl RequestProxy { return false; } - let features = version.get("features").and_then(|v| v.as_int()).unwrap_or(0); + let features = version + .get("features") + .and_then(|v| v.as_int()) + .unwrap_or(0); (features & CURL_VERSION_HTTPS_PROXY) != 0 } } diff --git a/crates/shirabe/src/util/http/response.rs b/crates/shirabe/src/util/http/response.rs index ff3af06..6a60540 100644 --- a/crates/shirabe/src/util/http/response.rs +++ b/crates/shirabe/src/util/http/response.rs @@ -1,9 +1,9 @@ //! ref: composer/src/Composer/Util/Http/Response.php +use crate::json::json_file::JsonFile; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{LogicException, PhpMixed, preg_quote}; -use crate::json::json_file::JsonFile; #[derive(Debug)] pub struct Response { @@ -63,7 +63,9 @@ impl Response { } pub fn decode_json(&self) -> anyhow::Result<PhpMixed> { - let url = self.request.get("url") + let url = self + .request + .get("url") .and_then(|u| u.as_string()) .unwrap_or(""); JsonFile::parse_json(self.body.as_deref(), Some(url)) diff --git a/crates/shirabe/src/util/http_downloader.rs b/crates/shirabe/src/util/http_downloader.rs index e822630..eb28ca0 100644 --- a/crates/shirabe/src/util/http_downloader.rs +++ b/crates/shirabe/src/util/http_downloader.rs @@ -7,9 +7,9 @@ use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::react::promise::promise::Promise; use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use shirabe_php_shim::{ - array_replace_recursive, chr, extension_loaded, file_get_contents, function_exists, implode, - is_numeric, max, min, rawurldecode, stream_context_create, stripos, strpos, substr, ucfirst, - InvalidArgumentException, LogicException, PhpMixed, Silencer, + InvalidArgumentException, LogicException, PhpMixed, Silencer, array_replace_recursive, chr, + extension_loaded, file_get_contents, function_exists, implode, is_numeric, max, min, + rawurldecode, stream_context_create, stripos, strpos, substr, ucfirst, }; use shirabe_semver::constraint::constraint::Constraint; @@ -119,11 +119,7 @@ impl HttpDownloader { ), ) .as_array() - .map(|m| { - m.iter() - .map(|(k, v)| (k.clone(), (**v).clone())) - .collect() - }) + .map(|m| m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect()) .unwrap_or_default(); let curl = if Self::is_curl_enabled() { @@ -149,7 +145,10 @@ impl HttpDownloader { if is_numeric(&max_jobs_env) { max_jobs = max( 1, - min(50, max_jobs_env.as_string().unwrap_or("0").parse().unwrap_or(0)), + min( + 50, + max_jobs_env.as_string().unwrap_or("0").parse().unwrap_or(0), + ), ); } @@ -169,11 +168,7 @@ impl HttpDownloader { } /// Download a file synchronously - pub fn get( - &mut self, - url: &str, - options: IndexMap<String, PhpMixed>, - ) -> Result<Response> { + pub fn get(&mut self, url: &str, options: IndexMap<String, PhpMixed>) -> Result<Response> { if "" == url { return Err(InvalidArgumentException { message: "$url must not be an empty string".to_string(), @@ -296,19 +291,10 @@ impl HttpDownloader { .map(|(k, v)| (k, Box::new(v))) .collect(), ), - PhpMixed::Array( - options - .into_iter() - .map(|(k, v)| (k, Box::new(v))) - .collect(), - ), + PhpMixed::Array(options.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), ) .as_array() - .map(|m| { - m.iter() - .map(|(k, v)| (k.clone(), (**v).clone())) - .collect() - }) + .map(|m| m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect()) .unwrap_or_default(); } @@ -336,11 +322,7 @@ impl HttpDownloader { ), ) .as_array() - .map(|m| { - m.iter() - .map(|(k, v)| (k.clone(), (**v).clone())) - .collect() - }) + .map(|m| m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect()) .unwrap_or_default(); let id = self.id_gen; @@ -371,10 +353,9 @@ impl HttpDownloader { } // capture username/password from URL if there is one - if let Some(m) = Preg::is_match_strict_groups( - r"{^https?://([^:/]+):([^@/]+)@([^/]+)}i", - &request.url, - ) { + if let Some(m) = + Preg::is_match_strict_groups(r"{^https?://([^:/]+):([^@/]+)@([^/]+)}i", &request.url) + { self.io.set_authentication( origin.clone(), rawurldecode(m.get(1).cloned().unwrap_or_default().as_str()), @@ -391,19 +372,13 @@ impl HttpDownloader { let canceler: Box<dyn Fn()> = Box::new(|| { // PHP canceler logic — TODO(phase-b) let _ = IrrecoverableDownloadException { - inner: TransportException::new( - "Download canceled".to_string(), - 0, - ), + inner: TransportException::new("Download canceled".to_string(), 0), }; let _ = Url::sanitize(""); }); let _ = (resolver, canceler); - let promise = Promise::new( - Box::new(|_resolve, _reject| {}), - Box::new(|| {}), - ); + let promise = Promise::new(Box::new(|_resolve, _reject| {}), Box::new(|| {})); // TODO(phase-b): wire promise.then() side-effects: mark job done & store response/exception let promise: Box<dyn PromiseInterface> = Box::new(promise); @@ -430,7 +405,11 @@ impl HttpDownloader { let (request, origin, copy_to) = { let job = self.jobs.get(&id).unwrap(); - (job.request.clone(), job.origin.clone(), job.request.copy_to.clone()) + ( + job.request.clone(), + job.origin.clone(), + job.request.copy_to.clone(), + ) }; let url = request.url.clone(); let options = request.options.clone(); @@ -473,7 +452,10 @@ impl HttpDownloader { // job.resolve(response) — TODO(phase-b) } else { let mut e = TransportException::new( - format!("Network disabled, request canceled: {}", Url::sanitize(&url)), + format!( + "Network disabled, request canceled: {}", + Url::sanitize(&url) + ), 499, ); e.set_status_code(499); @@ -601,11 +583,7 @@ impl HttpDownloader { ) -> Result<()> { let clean_message = |msg: &str| -> String { if !io.is_decorated() { - return Preg::replace( - &format!("{{{}{}}}u", chr(27), "\\[[;\\d]*m"), - "", - msg, - ); + return Preg::replace(&format!("{{{}{}}}u", chr(27), "\\[[;\\d]*m"), "", msg); } msg.to_string() @@ -711,7 +689,10 @@ impl HttpDownloader { ssl_map.insert("verify_peer".to_string(), Box::new(PhpMixed::Bool(false))); ctx_options.insert("ssl".to_string(), PhpMixed::Array(ssl_map)); let mut http_map: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); - http_map.insert("follow_location".to_string(), Box::new(PhpMixed::Bool(false))); + http_map.insert( + "follow_location".to_string(), + Box::new(PhpMixed::Bool(false)), + ); http_map.insert("ignore_errors".to_string(), Box::new(PhpMixed::Bool(true))); ctx_options.insert("http".to_string(), PhpMixed::Array(http_map)); let test_connectivity = file_get_contents( @@ -744,14 +725,10 @@ impl HttpDownloader { return false; } - let allow_self_signed = job - .request - .options - .get("ssl") - .and_then(|v| match v { - PhpMixed::Array(m) => m.get("allow_self_signed").cloned(), - _ => None, - }); + let allow_self_signed = job.request.options.get("ssl").and_then(|v| match v { + PhpMixed::Array(m) => m.get("allow_self_signed").cloned(), + _ => None, + }); if let Some(v) = allow_self_signed { if !shirabe_php_shim::empty(&v) { return false; diff --git a/crates/shirabe/src/util/loop.rs b/crates/shirabe/src/util/loop.rs index 41e1f25..189b8a3 100644 --- a/crates/shirabe/src/util/loop.rs +++ b/crates/shirabe/src/util/loop.rs @@ -1,12 +1,12 @@ //! ref: composer/src/Composer/Util/Loop.php +use crate::util::http_downloader::HttpDownloader; +use crate::util::process_executor::ProcessExecutor; use anyhow::Result; use indexmap::IndexMap; -use shirabe_php_shim::microtime; use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use shirabe_external_packages::symfony::component::console::helper::progress_bar::ProgressBar; -use crate::util::http_downloader::HttpDownloader; -use crate::util::process_executor::ProcessExecutor; +use shirabe_php_shim::microtime; #[derive(Debug)] pub struct Loop { @@ -17,7 +17,10 @@ pub struct Loop { } impl Loop { - pub fn new(mut http_downloader: HttpDownloader, process_executor: Option<ProcessExecutor>) -> Self { + pub fn new( + mut http_downloader: HttpDownloader, + process_executor: Option<ProcessExecutor>, + ) -> Self { http_downloader.enable_async(); let process_executor = process_executor.map(|mut pe| { @@ -41,7 +44,11 @@ impl Loop { self.process_executor.as_ref() } - pub fn wait(&mut self, promises: Vec<Box<dyn PromiseInterface>>, progress: Option<&mut ProgressBar>) -> Result<()> { + pub fn wait( + &mut self, + promises: Vec<Box<dyn PromiseInterface>>, + progress: Option<&mut ProgressBar>, + ) -> Result<()> { let mut uncaught: Option<anyhow::Error> = None; shirabe_external_packages::react::promise::all(&promises).then( diff --git a/crates/shirabe/src/util/mod.rs b/crates/shirabe/src/util/mod.rs new file mode 100644 index 0000000..6a2d55e --- /dev/null +++ b/crates/shirabe/src/util/mod.rs @@ -0,0 +1,33 @@ +pub mod auth_helper; +pub mod bitbucket; +pub mod composer_mirror; +pub mod config_validator; +pub mod error_handler; +pub mod filesystem; +pub mod forgejo; +pub mod forgejo_repository_data; +pub mod forgejo_url; +pub mod git; +pub mod github; +pub mod gitlab; +pub mod hg; +pub mod http; +pub mod http_downloader; +pub mod ini_helper; +pub mod r#loop; +pub mod metadata_minifier; +pub mod no_proxy_pattern; +pub mod package_info; +pub mod package_sorter; +pub mod perforce; +pub mod platform; +pub mod process_executor; +pub mod remote_filesystem; +pub mod silencer; +pub mod stream_context_factory; +pub mod svn; +pub mod sync_helper; +pub mod tar; +pub mod tls_helper; +pub mod url; +pub mod zip; diff --git a/crates/shirabe/src/util/no_proxy_pattern.rs b/crates/shirabe/src/util/no_proxy_pattern.rs index b3fc3fa..7fbfd0f 100644 --- a/crates/shirabe/src/util/no_proxy_pattern.rs +++ b/crates/shirabe/src/util/no_proxy_pattern.rs @@ -4,10 +4,10 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - array_key_exists, chr, empty, explode, filter_var, filter_var_with_options, floor, inet_pton, - ltrim, parse_url, str_pad, str_repeat, stripos, strlen, strpbrk, strpos, substr, substr_count, - unpack, PhpMixed, RuntimeException, FILTER_VALIDATE_INT, FILTER_VALIDATE_IP, PHP_URL_HOST, - PHP_URL_PORT, PHP_URL_SCHEME, + FILTER_VALIDATE_INT, FILTER_VALIDATE_IP, PHP_URL_HOST, PHP_URL_PORT, PHP_URL_SCHEME, PhpMixed, + RuntimeException, array_key_exists, chr, empty, explode, filter_var, filter_var_with_options, + floor, inet_pton, ltrim, parse_url, str_pad, str_repeat, stripos, strlen, strpbrk, strpos, + substr, substr_count, unpack, }; /// Tests URLs against NO_PROXY patterns @@ -111,12 +111,7 @@ impl NoProxyPattern { } /// Returns true if the url is matched by a rule - pub(crate) fn r#match( - &mut self, - index: i64, - host_name: &str, - url: &UrlData, - ) -> Result<bool> { + pub(crate) fn r#match(&mut self, index: i64, host_name: &str, url: &UrlData) -> Result<bool> { let rule = match self.get_rule(index, host_name)? { Some(r) => r, None => { @@ -154,10 +149,7 @@ impl NoProxyPattern { /// Returns true if the target ip is in the network range pub(crate) fn match_range(&self, network: &IpData, target: &IpData) -> Result<bool> { let net = unpack("C*", &network.ip); - let mask = unpack( - "C*", - network.netmask.as_deref().unwrap_or_default(), - ); + let mask = unpack("C*", network.netmask.as_deref().unwrap_or_default()); let ip = unpack("C*", &target.ip); let net = match net { Some(n) => n, @@ -209,10 +201,7 @@ impl NoProxyPattern { .get(&i.to_string()) .and_then(|v| v.as_int()) .unwrap_or(0); - let ip_byte = ip - .get(&i.to_string()) - .and_then(|v| v.as_int()) - .unwrap_or(0); + let ip_byte = ip.get(&i.to_string()).and_then(|v| v.as_int()).unwrap_or(0); if (net_byte & mask_byte) != (ip_byte & mask_byte) { return Ok(false); } @@ -335,7 +324,12 @@ impl NoProxyPattern { mask.push_str(&chr(0xff ^ (0xff >> remainder))); } - let mask = str_pad(&mask, size as usize, &chr(0), shirabe_php_shim::STR_PAD_RIGHT); + let mask = str_pad( + &mask, + size as usize, + &chr(0), + shirabe_php_shim::STR_PAD_RIGHT, + ); self.ip_map_to_6(mask.as_bytes(), size) } @@ -387,10 +381,7 @@ impl NoProxyPattern { }; for i in 1..17 { - let ip_byte = ip - .get(&i.to_string()) - .and_then(|v| v.as_int()) - .unwrap_or(0); + let ip_byte = ip.get(&i.to_string()).and_then(|v| v.as_int()).unwrap_or(0); let mask_byte = mask .get(&i.to_string()) .and_then(|v| v.as_int()) @@ -468,9 +459,7 @@ impl NoProxyPattern { ip6 = substr(&host_name, 1, Some((index as i64) - 1)); host_name = substr(&host_name, (index as i64) + 1, None); - if strpbrk(&host_name, "[]").is_some() - || substr_count(&host_name, ":") > 1 - { + if strpbrk(&host_name, "[]").is_some() || substr_count(&host_name, ":") > 1 { return Ok(error); } } @@ -500,12 +489,7 @@ impl NoProxyPattern { inner.insert("max_range".to_string(), PhpMixed::Int(max)); options.insert( "options".to_string(), - PhpMixed::Array( - inner - .into_iter() - .map(|(k, v)| (k, Box::new(v))) - .collect(), - ), + PhpMixed::Array(inner.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), ); !matches!( diff --git a/crates/shirabe/src/util/package_info.rs b/crates/shirabe/src/util/package_info.rs index 82fac57..1046f0e 100644 --- a/crates/shirabe/src/util/package_info.rs +++ b/crates/shirabe/src/util/package_info.rs @@ -21,7 +21,9 @@ impl PackageInfo { pub fn get_view_source_or_homepage_url(package: &dyn PackageInterface) -> Option<String> { let url = Self::get_view_source_url(package).or_else(|| { - package.as_complete_package_interface().and_then(|complete| complete.get_homepage()) + package + .as_complete_package_interface() + .and_then(|complete| complete.get_homepage()) }); if url.as_deref() == Some("") { diff --git a/crates/shirabe/src/util/package_sorter.rs b/crates/shirabe/src/util/package_sorter.rs index 5a7aa84..2f38910 100644 --- a/crates/shirabe/src/util/package_sorter.rs +++ b/crates/shirabe/src/util/package_sorter.rs @@ -12,7 +12,9 @@ use crate::package::root_package::RootPackage; pub struct PackageSorter; impl PackageSorter { - pub fn get_most_current_version(packages: Vec<Box<dyn PackageInterface>>) -> Option<Box<dyn PackageInterface>> { + pub fn get_most_current_version( + packages: Vec<Box<dyn PackageInterface>>, + ) -> Option<Box<dyn PackageInterface>> { if packages.is_empty() { return None; } @@ -31,23 +33,32 @@ impl PackageSorter { Some(highest) } - pub fn sort_packages_alphabetically(mut packages: Vec<Box<dyn PackageInterface>>) -> Vec<Box<dyn PackageInterface>> { + pub fn sort_packages_alphabetically( + mut packages: Vec<Box<dyn PackageInterface>>, + ) -> Vec<Box<dyn PackageInterface>> { packages.sort_by_key(|p| p.get_name()); packages } - pub fn sort_packages(packages: Vec<Box<dyn PackageInterface>>, weights: IndexMap<String, i64>) -> Vec<Box<dyn PackageInterface>> { + pub fn sort_packages( + packages: Vec<Box<dyn PackageInterface>>, + weights: IndexMap<String, i64>, + ) -> Vec<Box<dyn PackageInterface>> { let mut usage_list: IndexMap<String, Vec<String>> = IndexMap::new(); for package in &packages { let mut links: IndexMap<String, Link> = package.get_requires(); // TODO: check for RootAliasPackage as well - if let Some(root_package) = (package.as_any() as &dyn Any).downcast_ref::<RootPackage>() { + if let Some(root_package) = (package.as_any() as &dyn Any).downcast_ref::<RootPackage>() + { links.extend(root_package.get_dev_requires()); } for link in links.values() { let target = link.get_target().to_string(); - usage_list.entry(target).or_default().push(package.get_name().to_string()); + usage_list + .entry(target) + .or_default() + .push(package.get_name().to_string()); } } @@ -73,7 +84,8 @@ impl PackageSorter { } }); - let mut packages: Vec<Option<Box<dyn PackageInterface>>> = packages.into_iter().map(Some).collect(); + let mut packages: Vec<Option<Box<dyn PackageInterface>>> = + packages.into_iter().map(Some).collect(); weighted_packages .into_iter() .map(|(_, _, index)| packages[index].take().unwrap()) diff --git a/crates/shirabe/src/util/perforce.rs b/crates/shirabe/src/util/perforce.rs index 0e905c6..a647dbb 100644 --- a/crates/shirabe/src/util/perforce.rs +++ b/crates/shirabe/src/util/perforce.rs @@ -6,9 +6,9 @@ use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::symfony::component::process::executable_finder::ExecutableFinder; use shirabe_external_packages::symfony::component::process::process::Process; use shirabe_php_shim::{ - chdir, count, date, explode, fclose, feof, fgets, file_get_contents, fopen, fwrite, - gethostname, json_decode, str_replace_array, strcmp, strlen, strpos, strrpos, substr, time, - trim, Exception, PhpMixed, PHP_EOL, + Exception, PHP_EOL, PhpMixed, chdir, count, date, explode, fclose, feof, fgets, + file_get_contents, fopen, fwrite, gethostname, json_decode, str_replace_array, strcmp, strlen, + strpos, strrpos, substr, time, trim, }; use crate::io::io_interface::IOInterface; @@ -334,11 +334,7 @@ impl Perforce { /// @internal /// @param non-empty-list<string> $arguments Additional arguments for git rev-list /// @return non-empty-list<string> - pub fn generate_p4_command( - &mut self, - arguments: Vec<String>, - use_client: bool, - ) -> Vec<String> { + pub fn generate_p4_command(&mut self, arguments: Vec<String>, use_client: bool) -> Vec<String> { let mut p4_command: Vec<String> = vec![Self::get_p4_executable()]; if self.get_user().is_some() { p4_command.push("-u".to_string()); @@ -357,8 +353,7 @@ impl Perforce { } pub fn is_logged_in(&mut self) -> Result<bool> { - let command = - self.generate_p4_command(vec!["login".to_string(), "-s".to_string()], false); + let command = self.generate_p4_command(vec!["login".to_string(), "-s".to_string()], false); let exit_code = self.execute_command(PhpMixed::List( command .into_iter() @@ -592,10 +587,7 @@ impl Perforce { if !process.is_successful() { return Err(Exception { - message: format!( - "Error logging in:{}", - self.process.get_error_output() - ), + message: format!("Error logging in:{}", self.process.get_error_output()), code: 0, } .into()); @@ -815,10 +807,8 @@ impl Perforce { pub(crate) fn get_change_list(&mut self, reference: &str) -> Option<String> { let index = strpos(reference, "@")?; let label = substr(reference, index as i64, None); - let command = self.generate_p4_command( - vec!["changes".to_string(), "-m1".to_string(), label], - true, - ); + let command = + self.generate_p4_command(vec!["changes".to_string(), "-m1".to_string(), label], true); self.execute_command(PhpMixed::List( command .into_iter() @@ -835,11 +825,7 @@ impl Perforce { } /// @return mixed|null - pub fn get_commit_logs( - &mut self, - from_reference: &str, - to_reference: &str, - ) -> Option<String> { + pub fn get_commit_logs(&mut self, from_reference: &str, to_reference: &str) -> Option<String> { let from_change_list = self.get_change_list(from_reference)?; let to_change_list = self.get_change_list(to_reference)?; let index = strpos(from_reference, "@").unwrap_or(0); @@ -879,9 +865,10 @@ impl Perforce { P4_EXECUTABLE .get_or_init(|| { let finder = ExecutableFinder::new(); - finder.find("p4", None, vec![]).unwrap_or_else(|| "p4".to_string()) + finder + .find("p4", None, vec![]) + .unwrap_or_else(|| "p4".to_string()) }) .clone() } } - diff --git a/crates/shirabe/src/util/platform.rs b/crates/shirabe/src/util/platform.rs index e8f5dad..96e564f 100644 --- a/crates/shirabe/src/util/platform.rs +++ b/crates/shirabe/src/util/platform.rs @@ -5,11 +5,11 @@ use std::sync::Mutex; use anyhow::Result; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - defined, env_contains_key, env_get, env_set, env_unset, file_exists, file_get_contents, - fopen, fstat, function_exists, getcwd, getenv, in_array, ini_get, is_array, is_readable, - mb_strlen, posix_geteuid, posix_getpwuid, posix_getuid, posix_isatty, putenv, realpath, - server_argv, server_contains_key, server_get, server_set, server_unset, stream_isatty, - stripos, strlen, strtoupper, substr, usleep, PhpMixed, RuntimeException, + PhpMixed, RuntimeException, defined, env_contains_key, env_get, env_set, env_unset, + file_exists, file_get_contents, fopen, fstat, function_exists, getcwd, getenv, in_array, + ini_get, is_array, is_readable, mb_strlen, posix_geteuid, posix_getpwuid, posix_getuid, + posix_isatty, putenv, realpath, server_argv, server_contains_key, server_get, server_set, + server_unset, stream_isatty, stripos, strlen, strtoupper, substr, usleep, }; use crate::util::process_executor::ProcessExecutor; @@ -93,7 +93,11 @@ impl Platform { /// Parses tildes and environment variables in paths. pub fn expand_path(path: &str) -> String { if Preg::is_match(r"#^~[\\/]#", path) { - return format!("{}{}", Self::get_user_directory().unwrap(), substr(path, 1, None)); + return format!( + "{}{}", + Self::get_user_directory().unwrap(), + substr(path, 1, None) + ); } Preg::replace_callback( @@ -180,7 +184,9 @@ impl Platform { .ok() .flatten() .unwrap_or_default(); - if !(ini_get("open_basedir").map(|s| !s.is_empty()).unwrap_or(false)) + if !(ini_get("open_basedir") + .map(|s| !s.is_empty()) + .unwrap_or(false)) && is_readable("/proc/version") && stripos(&file_contents, "microsoft").is_some() && !Self::is_docker() @@ -206,7 +212,10 @@ impl Platform { } // cannot check so assume no - if ini_get("open_basedir").map(|s| !s.is_empty()).unwrap_or(false) { + if ini_get("open_basedir") + .map(|s| !s.is_empty()) + .unwrap_or(false) + { *cached = Some(false); return false; } @@ -294,9 +303,7 @@ impl Platform { // detect msysgit/mingw and assume this is a tty because detection // does not work correctly, see https://github.com/composer/composer/issues/9690 if in_array( - PhpMixed::String(strtoupper( - &Self::get_env("MSYSTEM").unwrap_or_default(), - )), + PhpMixed::String(strtoupper(&Self::get_env("MSYSTEM").unwrap_or_default())), &PhpMixed::List(vec![ Box::new(PhpMixed::String("MINGW32".to_string())), Box::new(PhpMixed::String("MINGW64".to_string())), diff --git a/crates/shirabe/src/util/process_executor.rs b/crates/shirabe/src/util/process_executor.rs index 29c2bfa..2d97322 100644 --- a/crates/shirabe/src/util/process_executor.rs +++ b/crates/shirabe/src/util/process_executor.rs @@ -13,10 +13,10 @@ use shirabe_external_packages::symfony::component::process::exception::runtime_e use shirabe_external_packages::symfony::component::process::executable_finder::ExecutableFinder; use shirabe_external_packages::symfony::component::process::process::Process; use shirabe_php_shim::{ - array_intersect, array_map, call_user_func, defined, escapeshellarg, explode, implode, - in_array, is_array, is_callable, is_dir, is_numeric, is_string, max, min, rtrim, sprintf, - str_replace, strcspn, strlen, strpbrk, strtolower, strtr, substr_replace, trim, usleep, - LogicException, PhpMixed, RuntimeException, + LogicException, PhpMixed, RuntimeException, array_intersect, array_map, call_user_func, + defined, escapeshellarg, explode, implode, in_array, is_array, is_callable, is_dir, is_numeric, + is_string, max, min, rtrim, sprintf, str_replace, strcspn, strlen, strpbrk, strtolower, strtr, + substr_replace, trim, usleep, }; use crate::io::io_interface::IOInterface; @@ -71,17 +71,12 @@ impl ProcessExecutor { "echo", "endlocal", "erase", "exit", "for", "ftype", "goto", "help", "if", "label", "md", "mkdir", "mklink", "move", "path", "pause", "popd", "prompt", "pushd", "rd", "rem", "ren", "rename", "rmdir", "set", "setlocal", "shift", "start", "time", "title", "type", "ver", - "vol", - // unused slots to make 47 above explicit + "vol", // unused slots to make 47 above explicit "", "", "", ]; - const GIT_CMDS_NEED_GIT_DIR: &'static [&'static [&'static str]] = &[ - &["show"], - &["log"], - &["branch"], - &["remote", "set-url"], - ]; + const GIT_CMDS_NEED_GIT_DIR: &'static [&'static [&'static str]] = + &[&["show"], &["log"], &["branch"], &["remote", "set-url"]]; pub fn new(io: Option<Box<dyn IOInterface>>, _: Option<()>) -> Self { let mut this = Self { @@ -170,8 +165,7 @@ impl ProcessExecutor { .iter() .map(|v| v.as_string().unwrap_or("").to_string()) .collect(); - if Platform::is_windows() - && strlen(&cmd_vec[0]) == strcspn(&cmd_vec[0], ":/\\") as i64 + if Platform::is_windows() && strlen(&cmd_vec[0]) == strcspn(&cmd_vec[0], ":/\\") as i64 { cmd_vec[0] = Self::get_executable(&cmd_vec[0]); } @@ -206,7 +200,11 @@ impl ProcessExecutor { let io_for_signal = self.io.as_ref().map(|b| &**b as *const dyn IOInterface); let signal_handler = SignalHandler::create( - vec![SignalHandler::SIGINT, SignalHandler::SIGTERM, SignalHandler::SIGHUP], + vec![ + SignalHandler::SIGINT, + SignalHandler::SIGTERM, + SignalHandler::SIGHUP, + ], Box::new(move |signal: String, _h: &SignalHandler| { if let Some(io_ptr) = io_for_signal { let io = unsafe { &*io_ptr }; @@ -335,10 +333,7 @@ impl ProcessExecutor { }); let _ = (resolver, canceler); - let promise = Promise::new( - Box::new(|_resolve, _reject| {}), - Box::new(|| {}), - ); + let promise = Promise::new(Box::new(|_resolve, _reject| {}), Box::new(|| {})); // TODO(phase-b): wire promise.then() side-effects: mark job done & update status let promise: Box<dyn PromiseInterface> = Box::new(promise); @@ -601,10 +596,16 @@ impl ProcessExecutor { r"{://(?P<user>[^:/\s]+):(?P<password>[^@\s/]+)@}i", |m: &IndexMap<String, String>| -> String { // if the username looks like a long (12char+) hex string, or a modern github token (e.g. ghp_xxx, github_pat_xxx) we obfuscate that - if Preg::is_match(GitHub::GITHUB_TOKEN_REGEX, m.get("user").cloned().unwrap_or_default().as_str()) { + if Preg::is_match( + GitHub::GITHUB_TOKEN_REGEX, + m.get("user").cloned().unwrap_or_default().as_str(), + ) { return "://***:***@".to_string(); } - if Preg::is_match(r"{^[a-f0-9]{12,}$}", m.get("user").cloned().unwrap_or_default().as_str()) { + if Preg::is_match( + r"{^[a-f0-9]{12,}$}", + m.get("user").cloned().unwrap_or_default().as_str(), + ) { return "://***:***@".to_string(); } @@ -664,13 +665,8 @@ impl ProcessExecutor { let mut quote = strpbrk(&argument, " \t,").is_some(); let mut dquotes: i64 = 0; // PHP: Preg::replace('/(\\\\*)"/', '$1$1\\"', $argument, -1, $dquotes) - argument = Preg::replace_with_count( - r#"/(\\*)"/"#, - r#"$1$1\""#, - &argument, - -1, - &mut dquotes, - ); + argument = + Preg::replace_with_count(r#"/(\\*)"/"#, r#"$1$1\""#, &argument, -1, &mut dquotes); let meta = dquotes > 0 || Preg::is_match(r"/%[^%]+%|![^!]+!/", &argument); if !meta && !quote { @@ -695,8 +691,14 @@ impl ProcessExecutor { explode(" ", command.as_string().unwrap_or("")) } else { match command { - PhpMixed::List(l) => l.iter().map(|v| v.as_string().unwrap_or("").to_string()).collect(), - PhpMixed::Array(m) => m.values().map(|v| v.as_string().unwrap_or("").to_string()).collect(), + PhpMixed::List(l) => l + .iter() + .map(|v| v.as_string().unwrap_or("").to_string()) + .collect(), + PhpMixed::Array(m) => m + .values() + .map(|v| v.as_string().unwrap_or("").to_string()) + .collect(), _ => vec![], } }; @@ -738,7 +740,10 @@ impl ProcessExecutor { } } - executables.get(name).cloned().unwrap_or_else(|| name.to_string()) + executables + .get(name) + .cloned() + .unwrap_or_else(|| name.to_string()) } } diff --git a/crates/shirabe/src/util/remote_filesystem.rs b/crates/shirabe/src/util/remote_filesystem.rs index 2c377d7..14b771d 100644 --- a/crates/shirabe/src/util/remote_filesystem.rs +++ b/crates/shirabe/src/util/remote_filesystem.rs @@ -4,10 +4,10 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - array_replace_recursive, base64_encode, explode, extension_loaded, file_put_contents, - filter_var, ini_get, json_decode, parse_url, preg_quote, restore_error_handler, - set_error_handler, sprintf, strpos, strtolower, strtr, substr, trim, - PhpMixed, RuntimeException, FILTER_VALIDATE_BOOLEAN, PHP_URL_HOST, PHP_URL_PATH, PHP_VERSION_ID, + FILTER_VALIDATE_BOOLEAN, PHP_URL_HOST, PHP_URL_PATH, PHP_VERSION_ID, PhpMixed, + RuntimeException, array_replace_recursive, base64_encode, explode, extension_loaded, + file_put_contents, filter_var, ini_get, json_decode, parse_url, preg_quote, + restore_error_handler, set_error_handler, sprintf, strpos, strtolower, strtr, substr, trim, }; use crate::config::Config; @@ -61,7 +61,10 @@ impl RemoteFilesystem { auth_helper: Option<AuthHelper>, ) -> Self { let (computed_options, disable_tls_set) = if !disable_tls { - (StreamContextFactory::get_tls_defaults(&options, &*io), false) + ( + StreamContextFactory::get_tls_defaults(&options, &*io), + false, + ) } else { (IndexMap::new(), true) }; @@ -99,7 +102,13 @@ impl RemoteFilesystem { progress: bool, options: IndexMap<String, PhpMixed>, ) -> anyhow::Result<GetResult> { - self.get(origin_url, file_url, options, Some(file_name.to_string()), progress) + self.get( + origin_url, + file_url, + options, + Some(file_name.to_string()), + progress, + ) } pub fn get_contents( @@ -131,9 +140,7 @@ impl RemoteFilesystem { pub fn find_status_code(headers: &[String]) -> Option<i64> { let mut value: Option<i64> = None; for header in headers { - if let Ok(Some(m)) = - Preg::is_match_strict_groups("{^HTTP/\\S+ (\\d+)}i", header) - { + if let Ok(Some(m)) = Preg::is_match_strict_groups("{^HTTP/\\S+ (\\d+)}i", header) { value = Some(m["1"].parse().unwrap_or(0)); } } @@ -219,10 +226,7 @@ impl RemoteFilesystem { if let Some(http_opts) = options.get_mut("http") { if let PhpMixed::Array(m) = http_opts { - m.insert( - "ignore_errors".to_string(), - Box::new(PhpMixed::Bool(true)), - ); + m.insert("ignore_errors".to_string(), Box::new(PhpMixed::Bool(true))); } } @@ -380,7 +384,10 @@ impl RemoteFilesystem { FILTER_VALIDATE_BOOLEAN, ) { - error_message = format!("allow_url_fopen must be enabled in php.ini ({})", error_message); + error_message = format!( + "allow_url_fopen must be enabled in php.ini ({})", + error_message + ); } restore_error_handler(); if let Some(e) = caught_e { @@ -428,7 +435,9 @@ impl RemoteFilesystem { } let bitbucket_login_match = origin_url == "bitbucket.org" - && !self.auth_helper.is_public_bit_bucket_download(&self.file_url) + && !self + .auth_helper + .is_public_bit_bucket_download(&self.file_url) && substr(&self.file_url, -4, None) == ".zip" && (location_header.is_none() || substr( @@ -582,10 +591,8 @@ impl RemoteFilesystem { let put_error_message = String::new(); // TODO(phase-b): set_error_handler closure that captures `put_error_message` by reference set_error_handler(|_code, _msg, _file, _line| true); - let write_result = file_put_contents( - file_name.as_deref().unwrap(), - result_str.as_bytes(), - ); + let write_result = + file_put_contents(file_name.as_deref().unwrap(), result_str.as_bytes()); restore_error_handler(); if write_result.is_none() { return Err(anyhow::anyhow!(TransportException::new(format!( @@ -800,7 +807,9 @@ impl RemoteFilesystem { self.retry = result.retry; if self.retry { - return Err(anyhow::anyhow!(TransportException::new("RETRY".to_string()))); + return Err(anyhow::anyhow!(TransportException::new( + "RETRY".to_string() + ))); } Ok(()) } @@ -856,9 +865,9 @@ impl RemoteFilesystem { ); } } - options = - self.auth_helper - .add_authentication_options(options, origin_url, &self.file_url); + options = self + .auth_helper + .add_authentication_options(options, origin_url, &self.file_url); let http_entry = options .entry("http".to_string()) @@ -888,9 +897,7 @@ impl RemoteFilesystem { result: Option<String>, ) -> anyhow::Result<Option<String>> { let mut target_url: Option<String> = None; - if let Some(location_header) = - Response::find_header_value(response_headers, "location") - { + if let Some(location_header) = Response::find_header_value(response_headers, "location") { // TODO(phase-b): use PHP_URL_SCHEME once available to detect absolute URLs. if !parse_url(&location_header, PHP_URL_HOST) .as_string() @@ -954,10 +961,7 @@ impl RemoteFilesystem { <dyn IOInterface>::DEBUG, ); - additional_options.insert( - "redirects".to_string(), - PhpMixed::Int(self.redirects), - ); + additional_options.insert("redirects".to_string(), PhpMixed::Int(self.redirects)); let host = parse_url(&target_url, PHP_URL_HOST) .as_string() diff --git a/crates/shirabe/src/util/silencer.rs b/crates/shirabe/src/util/silencer.rs index 39ec1db..f5ee64d 100644 --- a/crates/shirabe/src/util/silencer.rs +++ b/crates/shirabe/src/util/silencer.rs @@ -1,11 +1,11 @@ //! ref: composer/src/Composer/Util/Silencer.php -use std::sync::Mutex; use anyhow::Result; use shirabe_php_shim::{ + E_DEPRECATED, E_NOTICE, E_USER_DEPRECATED, E_USER_NOTICE, E_USER_WARNING, E_WARNING, error_reporting, - E_WARNING, E_NOTICE, E_USER_WARNING, E_USER_NOTICE, E_DEPRECATED, E_USER_DEPRECATED, }; +use std::sync::Mutex; static STACK: Mutex<Vec<i64>> = Mutex::new(Vec::new()); @@ -13,7 +13,14 @@ pub struct Silencer; impl Silencer { pub fn suppress(mask: Option<i64>) -> i64 { - let mask = mask.unwrap_or(E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE | E_DEPRECATED | E_USER_DEPRECATED); + let mask = mask.unwrap_or( + E_WARNING + | E_NOTICE + | E_USER_WARNING + | E_USER_NOTICE + | E_DEPRECATED + | E_USER_DEPRECATED, + ); let old = error_reporting(None); STACK.lock().unwrap().push(old); error_reporting(Some(old & !mask)); diff --git a/crates/shirabe/src/util/stream_context_factory.rs b/crates/shirabe/src/util/stream_context_factory.rs index aba3a50..a81a68a 100644 --- a/crates/shirabe/src/util/stream_context_factory.rs +++ b/crates/shirabe/src/util/stream_context_factory.rs @@ -4,9 +4,9 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::ca_bundle::ca_bundle::CaBundle; use shirabe_external_packages::psr::log::logger_interface::LoggerInterface; use shirabe_php_shim::{ - array_replace_recursive, curl_version, extension_loaded, function_exists, php_uname, - stream_context_create, stripos, uasort, PhpMixed, RuntimeException, - HHVM_VERSION, PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION, + HHVM_VERSION, PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION, PhpMixed, + RuntimeException, array_replace_recursive, curl_version, extension_loaded, function_exists, + php_uname, stream_context_create, stripos, uasort, }; use crate::composer::Composer; @@ -31,13 +31,17 @@ impl StreamContextFactory { http.insert("follow_location".to_string(), PhpMixed::Int(1)); http.insert("max_redirects".to_string(), PhpMixed::Int(20)); let mut o = IndexMap::new(); - o.insert("http".to_string(), PhpMixed::Array( - http.into_iter().map(|(k, v)| (k, Box::new(v))).collect() - )); + o.insert( + "http".to_string(), + PhpMixed::Array(http.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), + ); o }; - options = array_replace_recursive(options, Self::init_options(url, default_options.clone(), false)?); + options = array_replace_recursive( + options, + Self::init_options(url, default_options.clone(), false)?, + ); let default_options = { let mut o = default_options; if let Some(PhpMixed::Array(ref mut http)) = o.get_mut("http") { @@ -53,7 +57,10 @@ impl StreamContextFactory { http.insert( "header".to_string(), Box::new(PhpMixed::List( - fixed.into_iter().map(|s| Box::new(PhpMixed::String(s))).collect(), + fixed + .into_iter() + .map(|s| Box::new(PhpMixed::String(s))) + .collect(), )), ); } @@ -75,10 +82,7 @@ impl StreamContextFactory { .unwrap_or(false); if !has_header { if let Some(PhpMixed::Array(ref mut http)) = options.get_mut("http") { - http.insert( - "header".to_string(), - Box::new(PhpMixed::List(vec![])), - ); + http.insert("header".to_string(), Box::new(PhpMixed::List(vec![]))); } } // Convert string header to array @@ -112,7 +116,8 @@ impl StreamContextFactory { if proxy.is_secure() { if !extension_loaded("openssl") { return Err(TransportException::new( - "You must enable the openssl extension to use a secure proxy.".to_string(), + "You must enable the openssl extension to use a secure proxy." + .to_string(), )); } if is_https_request { @@ -130,7 +135,9 @@ impl StreamContextFactory { let proxy_http = proxy_options.get("http"); if let Some(proxy_header) = proxy_http.and_then(|h| h.get("header")) { if let Some(PhpMixed::Array(ref mut http)) = options.get_mut("http") { - if let Some(PhpMixed::List(ref mut headers)) = http.get_mut("header").map(|v| &mut **v) { + if let Some(PhpMixed::List(ref mut headers)) = + http.get_mut("header").map(|v| &mut **v) + { headers.push(Box::new(*proxy_header.clone())); } } @@ -194,18 +201,32 @@ impl StreamContextFactory { let user_agent = format!( "User-Agent: Composer/{} ({os}; {release}; {php_version}; {http_version}{platform}{ci})", Composer::get_version(), - os = if function_exists("php_uname") { php_uname("s") } else { "Unknown".to_string() }, - release = if function_exists("php_uname") { php_uname("r") } else { "Unknown".to_string() }, + os = if function_exists("php_uname") { + php_uname("s") + } else { + "Unknown".to_string() + }, + release = if function_exists("php_uname") { + php_uname("r") + } else { + "Unknown".to_string() + }, php_version = php_version, http_version = http_version, platform = platform_php_version .as_deref() .map(|v| format!("; Platform-PHP {}", v)) .unwrap_or_default(), - ci = if Platform::get_env("CI").is_some() { "; CI" } else { "" }, + ci = if Platform::get_env("CI").is_some() { + "; CI" + } else { + "" + }, ); if let Some(PhpMixed::Array(ref mut http)) = options.get_mut("http") { - if let Some(PhpMixed::List(ref mut headers)) = http.get_mut("header").map(|v| &mut **v) { + if let Some(PhpMixed::List(ref mut headers)) = + http.get_mut("header").map(|v| &mut **v) + { headers.push(Box::new(PhpMixed::String(user_agent))); } } @@ -279,9 +300,15 @@ impl StreamContextFactory { let mut defaults: IndexMap<String, PhpMixed> = { let mut d = IndexMap::new(); - d.insert("ssl".to_string(), PhpMixed::Array( - ssl_defaults.into_iter().map(|(k, v)| (k, Box::new(v))).collect() - )); + d.insert( + "ssl".to_string(), + PhpMixed::Array( + ssl_defaults + .into_iter() + .map(|(k, v)| (k, Box::new(v))) + .collect(), + ), + ); d }; @@ -322,7 +349,9 @@ impl StreamContextFactory { } } - let cafile = defaults.get("ssl").and_then(|v| v.as_array()) + let cafile = defaults + .get("ssl") + .and_then(|v| v.as_array()) .and_then(|a| a.get("cafile")) .and_then(|v| v.as_string()) .map(|s| s.to_string()); @@ -334,7 +363,9 @@ impl StreamContextFactory { } } - let capath = defaults.get("ssl").and_then(|v| v.as_array()) + let capath = defaults + .get("ssl") + .and_then(|v| v.as_array()) .and_then(|a| a.get("capath")) .and_then(|v| v.as_string()) .map(|s| s.to_string()); diff --git a/crates/shirabe/src/util/svn.rs b/crates/shirabe/src/util/svn.rs index 2277c68..f1a900c 100644 --- a/crates/shirabe/src/util/svn.rs +++ b/crates/shirabe/src/util/svn.rs @@ -6,8 +6,8 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - empty, implode, parse_url, parse_url_all, stripos, strpos, trim, LogicException, PhpMixed, - RuntimeException, PHP_URL_HOST, + LogicException, PHP_URL_HOST, PhpMixed, RuntimeException, empty, implode, parse_url, + parse_url_all, stripos, strpos, trim, }; use crate::config::Config; @@ -148,17 +148,16 @@ impl Svn { }; // TODO(phase-b): pass handler callback to process.execute let mut handler_output = String::new(); - let status = self.process.execute(&command, &mut handler_output, cwd.map(String::from)); + let status = self + .process + .execute(&command, &mut handler_output, cwd.map(String::from)); if 0 == status { return Ok(output); } let error_output = self.process.get_error_output(); let full_output = trim( - &implode( - "\n", - &[output.clone().unwrap_or_default(), error_output], - ), + &implode("\n", &[output.clone().unwrap_or_default(), error_output]), None, ); @@ -425,10 +424,9 @@ impl Svn { None, ) { // TODO(phase-b): Preg::is_match with captures should populate $match - if let Ok(Some(matches)) = Preg::is_match_with_indexed_captures( - r"{(\d+(?:\.\d+)+)}", - &output, - ) { + if let Ok(Some(matches)) = + Preg::is_match_with_indexed_captures(r"{(\d+(?:\.\d+)+)}", &output) + { *cached = Some(matches.get(1).cloned().unwrap_or_default()); } } @@ -437,4 +435,3 @@ impl Svn { cached.clone() } } - diff --git a/crates/shirabe/src/util/sync_helper.rs b/crates/shirabe/src/util/sync_helper.rs index c5ab889..1be9075 100644 --- a/crates/shirabe/src/util/sync_helper.rs +++ b/crates/shirabe/src/util/sync_helper.rs @@ -1,11 +1,11 @@ //! ref: composer/src/Composer/Util/SyncHelper.php -use anyhow::Result; -use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; use crate::downloader::download_manager::DownloadManager; use crate::downloader::downloader_interface::DownloaderInterface; use crate::package::package_interface::PackageInterface; use crate::util::r#loop::Loop; +use anyhow::Result; +use shirabe_external_packages::react::promise::promise_interface::PromiseInterface; pub enum DownloaderOrManager<'a> { Interface(&'a dyn DownloaderInterface), @@ -13,14 +13,25 @@ pub enum DownloaderOrManager<'a> { } impl<'a> DownloaderOrManager<'a> { - fn download(&self, package: &dyn PackageInterface, path: &str, prev_package: Option<&dyn PackageInterface>) -> Box<dyn PromiseInterface> { + fn download( + &self, + package: &dyn PackageInterface, + path: &str, + prev_package: Option<&dyn PackageInterface>, + ) -> Box<dyn PromiseInterface> { match self { Self::Interface(d) => d.download(package, path, prev_package), Self::Manager(d) => d.download(package, path, prev_package), } } - fn prepare(&self, r#type: &str, package: &dyn PackageInterface, path: &str, prev_package: Option<&dyn PackageInterface>) -> Box<dyn PromiseInterface> { + fn prepare( + &self, + r#type: &str, + package: &dyn PackageInterface, + path: &str, + prev_package: Option<&dyn PackageInterface>, + ) -> Box<dyn PromiseInterface> { match self { Self::Interface(d) => d.prepare(r#type, package, path, prev_package), Self::Manager(d) => d.prepare(r#type, package, path, prev_package), @@ -34,14 +45,25 @@ impl<'a> DownloaderOrManager<'a> { } } - fn update(&self, package: &dyn PackageInterface, prev_package: &dyn PackageInterface, path: &str) -> Box<dyn PromiseInterface> { + fn update( + &self, + package: &dyn PackageInterface, + prev_package: &dyn PackageInterface, + path: &str, + ) -> Box<dyn PromiseInterface> { match self { Self::Interface(d) => d.update(package, prev_package, path), Self::Manager(d) => d.update(package, prev_package, path), } } - fn cleanup(&self, r#type: &str, package: &dyn PackageInterface, path: &str, prev_package: Option<&dyn PackageInterface>) -> Box<dyn PromiseInterface> { + fn cleanup( + &self, + r#type: &str, + package: &dyn PackageInterface, + path: &str, + prev_package: Option<&dyn PackageInterface>, + ) -> Box<dyn PromiseInterface> { match self { Self::Interface(d) => d.cleanup(r#type, package, path, prev_package), Self::Manager(d) => d.cleanup(r#type, package, path, prev_package), @@ -59,11 +81,21 @@ impl SyncHelper { package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>, ) -> Result<()> { - let r#type = if prev_package.is_some() { "update" } else { "install" }; + let r#type = if prev_package.is_some() { + "update" + } else { + "install" + }; let result: Result<()> = (|| { - Self::r#await(r#loop, Some(downloader.download(package, &path, prev_package)))?; - Self::r#await(r#loop, Some(downloader.prepare(r#type, package, &path, prev_package)))?; + Self::r#await( + r#loop, + Some(downloader.download(package, &path, prev_package)), + )?; + Self::r#await( + r#loop, + Some(downloader.prepare(r#type, package, &path, prev_package)), + )?; if r#type == "update" { if let Some(prev) = prev_package { Self::r#await(r#loop, Some(downloader.update(package, prev, &path)))?; @@ -75,11 +107,17 @@ impl SyncHelper { })(); if result.is_err() { - Self::r#await(r#loop, Some(downloader.cleanup(r#type, package, &path, prev_package)))?; + Self::r#await( + r#loop, + Some(downloader.cleanup(r#type, package, &path, prev_package)), + )?; return result; } - Self::r#await(r#loop, Some(downloader.cleanup(r#type, package, &path, prev_package)))?; + Self::r#await( + r#loop, + Some(downloader.cleanup(r#type, package, &path, prev_package)), + )?; Ok(()) } diff --git a/crates/shirabe/src/util/tar.rs b/crates/shirabe/src/util/tar.rs index 253f142..2d44392 100644 --- a/crates/shirabe/src/util/tar.rs +++ b/crates/shirabe/src/util/tar.rs @@ -31,7 +31,11 @@ impl Tar { return Err(anyhow::anyhow!(RuntimeException { message: format!( "Archive has more than one top level directories, and no composer.json was found on the top level, so it's an invalid archive. Top level paths found were: {}", - top_level_paths.keys().cloned().collect::<Vec<_>>().join(",") + top_level_paths + .keys() + .cloned() + .collect::<Vec<_>>() + .join(",") ), code: 0, })); @@ -50,7 +54,9 @@ impl Tar { } Err(anyhow::anyhow!(RuntimeException { - message: "No composer.json found either at the top level or within the topmost directory".to_string(), + message: + "No composer.json found either at the top level or within the topmost directory" + .to_string(), code: 0, })) } diff --git a/crates/shirabe/src/util/tls_helper.rs b/crates/shirabe/src/util/tls_helper.rs index ecacbcd..21b53a4 100644 --- a/crates/shirabe/src/util/tls_helper.rs +++ b/crates/shirabe/src/util/tls_helper.rs @@ -3,8 +3,8 @@ use shirabe_external_packages::composer::ca_bundle::ca_bundle::CaBundle; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_php_shim::{ - base64_decode, openssl_get_publickey, openssl_pkey_get_details, openssl_x509_parse, - preg_quote, substr_count, PhpMixed, RuntimeException, + PhpMixed, RuntimeException, base64_decode, openssl_get_publickey, openssl_pkey_get_details, + openssl_x509_parse, preg_quote, substr_count, }; /// @deprecated Use composer/ca-bundle and composer/composer 2.2 if you still need PHP 5 compatibility @@ -53,14 +53,16 @@ impl TlsHelper { } }; - let common_name = info.get("subject") + let common_name = info + .get("subject") .and_then(|v| v.as_array()) .and_then(|subj| subj.get("commonName")) .and_then(|cn| cn.as_string()) .map(|s| s.to_lowercase())?; let mut subject_alt_names = vec![]; - if let Some(san_value) = info.get("extensions") + if let Some(san_value) = info + .get("extensions") .and_then(|v| v.as_array()) .and_then(|ext| ext.get("subjectAltName")) .and_then(|v| v.as_string()) @@ -126,7 +128,8 @@ impl TlsHelper { message: "Failed to retrieve public key details".to_string(), code: 0, })?; - let pubkeypem = pubkeydetails.get("key") + let pubkeypem = pubkeydetails + .get("key") .and_then(|v| v.as_string()) .unwrap_or("") .to_string(); @@ -139,7 +142,10 @@ impl TlsHelper { let der = base64_decode(pemtrim).unwrap_or_default(); - Ok(shirabe_php_shim::hash("sha1", &String::from_utf8_lossy(&der))) + Ok(shirabe_php_shim::hash( + "sha1", + &String::from_utf8_lossy(&der), + )) } pub fn is_openssl_parse_safe() -> bool { diff --git a/crates/shirabe/src/util/url.rs b/crates/shirabe/src/util/url.rs index 78ac7cb..7d0ab21 100644 --- a/crates/shirabe/src/util/url.rs +++ b/crates/shirabe/src/util/url.rs @@ -1,36 +1,99 @@ //! ref: composer/src/Composer/Util/Url.php -use shirabe_external_packages::composer::pcre::preg::Preg; -use shirabe_php_shim::{in_array, parse_url, PhpMixed, PHP_URL_HOST, PHP_URL_PORT}; use crate::config::Config; use crate::util::github::GitHub; +use shirabe_external_packages::composer::pcre::preg::Preg; +use shirabe_php_shim::{PHP_URL_HOST, PHP_URL_PORT, PhpMixed, in_array, parse_url}; pub struct Url; impl Url { pub fn update_dist_reference(config: &Config, mut url: String, r#ref: &str) -> String { - let host = parse_url(&url, PHP_URL_HOST).as_string_opt().map(|s| s.to_string()).unwrap_or_default(); + let host = parse_url(&url, PHP_URL_HOST) + .as_string_opt() + .map(|s| s.to_string()) + .unwrap_or_default(); if host == "api.github.com" || host == "github.com" || host == "www.github.com" { - if let Some(m) = Preg::match_(r"(?i)^https?://(?:www\.)?github\.com/([^/]+)/([^/]+)/(zip|tar)ball/(.+)$", &url) { - url = format!("https://api.github.com/repos/{}/{}/{}ball/{}", m.get("1").unwrap_or(&String::new()), m.get("2").unwrap_or(&String::new()), m.get("3").unwrap_or(&String::new()), r#ref); - } else if let Some(m) = Preg::match_(r"(?i)^https?://(?:www\.)?github\.com/([^/]+)/([^/]+)/archive/.+\.(zip|tar)(?:\.gz)?$", &url) { - url = format!("https://api.github.com/repos/{}/{}/{}ball/{}", m.get("1").unwrap_or(&String::new()), m.get("2").unwrap_or(&String::new()), m.get("3").unwrap_or(&String::new()), r#ref); - } else if let Some(m) = Preg::match_(r"(?i)^https?://api\.github\.com/repos/([^/]+)/([^/]+)/(zip|tar)ball(?:/.+)?$", &url) { - url = format!("https://api.github.com/repos/{}/{}/{}ball/{}", m.get("1").unwrap_or(&String::new()), m.get("2").unwrap_or(&String::new()), m.get("3").unwrap_or(&String::new()), r#ref); + if let Some(m) = Preg::match_( + r"(?i)^https?://(?:www\.)?github\.com/([^/]+)/([^/]+)/(zip|tar)ball/(.+)$", + &url, + ) { + url = format!( + "https://api.github.com/repos/{}/{}/{}ball/{}", + m.get("1").unwrap_or(&String::new()), + m.get("2").unwrap_or(&String::new()), + m.get("3").unwrap_or(&String::new()), + r#ref + ); + } else if let Some(m) = Preg::match_( + r"(?i)^https?://(?:www\.)?github\.com/([^/]+)/([^/]+)/archive/.+\.(zip|tar)(?:\.gz)?$", + &url, + ) { + url = format!( + "https://api.github.com/repos/{}/{}/{}ball/{}", + m.get("1").unwrap_or(&String::new()), + m.get("2").unwrap_or(&String::new()), + m.get("3").unwrap_or(&String::new()), + r#ref + ); + } else if let Some(m) = Preg::match_( + r"(?i)^https?://api\.github\.com/repos/([^/]+)/([^/]+)/(zip|tar)ball(?:/.+)?$", + &url, + ) { + url = format!( + "https://api.github.com/repos/{}/{}/{}ball/{}", + m.get("1").unwrap_or(&String::new()), + m.get("2").unwrap_or(&String::new()), + m.get("3").unwrap_or(&String::new()), + r#ref + ); } } else if host == "bitbucket.org" || host == "www.bitbucket.org" { - if let Some(m) = Preg::match_(r"(?i)^https?://(?:www\.)?bitbucket\.org/([^/]+)/([^/]+)/get/(.+)\.(zip|tar\.gz|tar\.bz2)$", &url) { - url = format!("https://bitbucket.org/{}/{}/get/{}.{}", m.get("1").unwrap_or(&String::new()), m.get("2").unwrap_or(&String::new()), r#ref, m.get("4").unwrap_or(&String::new())); + if let Some(m) = Preg::match_( + r"(?i)^https?://(?:www\.)?bitbucket\.org/([^/]+)/([^/]+)/get/(.+)\.(zip|tar\.gz|tar\.bz2)$", + &url, + ) { + url = format!( + "https://bitbucket.org/{}/{}/get/{}.{}", + m.get("1").unwrap_or(&String::new()), + m.get("2").unwrap_or(&String::new()), + r#ref, + m.get("4").unwrap_or(&String::new()) + ); } } else if host == "gitlab.com" || host == "www.gitlab.com" { - if let Some(m) = Preg::match_(r"(?i)^https?://(?:www\.)?gitlab\.com/api/v[34]/projects/([^/]+)/repository/archive\.(zip|tar\.gz|tar\.bz2|tar)\?sha=.+$", &url) { - url = format!("https://gitlab.com/api/v4/projects/{}/repository/archive.{}?sha={}", m.get("1").unwrap_or(&String::new()), m.get("2").unwrap_or(&String::new()), r#ref); + if let Some(m) = Preg::match_( + r"(?i)^https?://(?:www\.)?gitlab\.com/api/v[34]/projects/([^/]+)/repository/archive\.(zip|tar\.gz|tar\.bz2|tar)\?sha=.+$", + &url, + ) { + url = format!( + "https://gitlab.com/api/v4/projects/{}/repository/archive.{}?sha={}", + m.get("1").unwrap_or(&String::new()), + m.get("2").unwrap_or(&String::new()), + r#ref + ); } - } else if in_array(PhpMixed::String(host.clone()), &config.get("github-domains"), true) { - url = Preg::replace(r"(?i)(/repos/[^/]+/[^/]+/(zip|tar)ball)(?:/.+)?$", &format!("$1/{}", r#ref), url); - } else if in_array(PhpMixed::String(host.clone()), &config.get("gitlab-domains"), true) { - url = Preg::replace(r"(?i)(/api/v[34]/projects/[^/]+/repository/archive\.(?:zip|tar\.gz|tar\.bz2|tar)\?sha=).+$", &format!("${{1}}{}", r#ref), url); + } else if in_array( + PhpMixed::String(host.clone()), + &config.get("github-domains"), + true, + ) { + url = Preg::replace( + r"(?i)(/repos/[^/]+/[^/]+/(zip|tar)ball)(?:/.+)?$", + &format!("$1/{}", r#ref), + url, + ); + } else if in_array( + PhpMixed::String(host.clone()), + &config.get("gitlab-domains"), + true, + ) { + url = Preg::replace( + r"(?i)(/api/v[34]/projects/[^/]+/repository/archive\.(?:zip|tar\.gz|tar\.bz2|tar)\?sha=).+$", + &format!("${{1}}{}", r#ref), + url, + ); } assert!(!url.is_empty()); @@ -43,7 +106,10 @@ impl Url { return url.to_string(); } - let mut origin = parse_url(url, PHP_URL_HOST).as_string_opt().map(|s| s.to_string()).unwrap_or_default(); + let mut origin = parse_url(url, PHP_URL_HOST) + .as_string_opt() + .map(|s| s.to_string()) + .unwrap_or_default(); if let Some(port) = parse_url(url, PHP_URL_PORT).as_i64_opt() { origin = format!("{}:{}", origin, port); } @@ -62,7 +128,13 @@ impl Url { // Gitlab can be installed in a non-root context (i.e. gitlab.com/foo). When downloading archives the originUrl // is the host without the path, so we look for the registered gitlab-domains matching the host here - if !origin.contains('/') && !in_array(PhpMixed::String(origin.clone()), &config.get("gitlab-domains"), true) { + if !origin.contains('/') + && !in_array( + PhpMixed::String(origin.clone()), + &config.get("gitlab-domains"), + true, + ) + { for gitlab_domain in config.get("gitlab-domains").as_vec_string() { if !gitlab_domain.is_empty() && gitlab_domain.starts_with(&origin) { return gitlab_domain; @@ -82,10 +154,20 @@ impl Url { r"(?i)^(?P<prefix>[a-z0-9]+://)?(?P<user>[^:/\s@]+):(?P<password>[^@\s/]+)@", |m| { // if the username looks like a long (12char+) hex string, or a modern github token (e.g. ghp_xxx, github_pat_xxx) we obfuscate that - if Preg::is_match(GitHub::GITHUB_TOKEN_REGEX, m.get("user").map(|s| s.as_str()).unwrap_or("")) { - format!("{}***:***@", m.get("prefix").map(|s| s.as_str()).unwrap_or("")) + if Preg::is_match( + GitHub::GITHUB_TOKEN_REGEX, + m.get("user").map(|s| s.as_str()).unwrap_or(""), + ) { + format!( + "{}***:***@", + m.get("prefix").map(|s| s.as_str()).unwrap_or("") + ) } else { - format!("{}{}:***@", m.get("prefix").map(|s| s.as_str()).unwrap_or(""), m.get("user").map(|s| s.as_str()).unwrap_or("")) + format!( + "{}{}:***@", + m.get("prefix").map(|s| s.as_str()).unwrap_or(""), + m.get("user").map(|s| s.as_str()).unwrap_or("") + ) } }, url, diff --git a/crates/shirabe/src/util/zip.rs b/crates/shirabe/src/util/zip.rs index 9c9135d..d76d805 100644 --- a/crates/shirabe/src/util/zip.rs +++ b/crates/shirabe/src/util/zip.rs @@ -2,7 +2,9 @@ use anyhow::Result; use indexmap::IndexMap; -use shirabe_php_shim::{dirname, extension_loaded, implode, stream_get_contents, RuntimeException, ZipArchive}; +use shirabe_php_shim::{ + RuntimeException, ZipArchive, dirname, extension_loaded, implode, stream_get_contents, +}; pub struct Zip; @@ -101,8 +103,9 @@ impl Zip { } Err(RuntimeException { - message: "No composer.json found either at the top level or within the topmost directory" - .to_string(), + message: + "No composer.json found either at the top level or within the topmost directory" + .to_string(), code: 0, } .into()) |
