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/src/util | |
| 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/src/util')
36 files changed, 1076 insertions, 720 deletions
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()) |
