aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/installer
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/src/installer')
-rw-r--r--crates/shirabe/src/installer/binary_installer.rs39
-rw-r--r--crates/shirabe/src/installer/installation_manager.rs82
-rw-r--r--crates/shirabe/src/installer/installer_event.rs9
-rw-r--r--crates/shirabe/src/installer/installer_interface.rs47
-rw-r--r--crates/shirabe/src/installer/library_installer.rs9
-rw-r--r--crates/shirabe/src/installer/metapackage_installer.rs99
-rw-r--r--crates/shirabe/src/installer/noop_installer.rs79
-rw-r--r--crates/shirabe/src/installer/package_event.rs2
-rw-r--r--crates/shirabe/src/installer/plugin_installer.rs85
-rw-r--r--crates/shirabe/src/installer/project_installer.rs71
-rw-r--r--crates/shirabe/src/installer/suggested_packages_reporter.rs47
11 files changed, 410 insertions, 159 deletions
diff --git a/crates/shirabe/src/installer/binary_installer.rs b/crates/shirabe/src/installer/binary_installer.rs
index eb533a4..0e5b3d9 100644
--- a/crates/shirabe/src/installer/binary_installer.rs
+++ b/crates/shirabe/src/installer/binary_installer.rs
@@ -2,9 +2,9 @@
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_php_shim::{
- basename, basename_with_suffix, chmod, dirname, fclose, fgets, file_exists, file_get_contents,
- file_put_contents, fopen, is_dir, is_file, is_link, realpath, rmdir, substr, trim, umask,
- PhpMixed,
+ PhpMixed, basename, basename_with_suffix, chmod, dirname, fclose, fgets, file_exists,
+ file_get_contents, file_put_contents, fopen, is_dir, is_file, is_link, realpath, rmdir, substr,
+ trim, umask,
};
use crate::io::io_interface::IOInterface;
@@ -166,10 +166,9 @@ impl BinaryInstaller {
let handle = fopen(bin, "r");
let line = fgets(handle.clone()).unwrap_or_default();
fclose(handle);
- if let Some(m) = Preg::is_match_strict_groups(
- r"{^#!/(?:usr/bin/env )?(?:[^/]+/)*(.+)$}m",
- &line,
- ) {
+ if let Some(m) =
+ Preg::is_match_strict_groups(r"{^#!/(?:usr/bin/env )?(?:[^/]+/)*(.+)$}m", &line)
+ {
return trim(m.get(1).map(|s| s.as_str()).unwrap_or(""), None);
}
@@ -246,7 +245,10 @@ impl BinaryInstaller {
SET BIN_TARGET=%~dp0/{}\r\n\
SET COMPOSER_RUNTIME_BIN_DIR=%~dp0\r\n\
{} \"%BIN_TARGET%\" %*\r\n",
- trim(&ProcessExecutor::escape(&basename_with_suffix(link, ".bat")), Some("\"'")),
+ trim(
+ &ProcessExecutor::escape(&basename_with_suffix(link, ".bat")),
+ Some("\"'")
+ ),
caller,
);
}
@@ -273,12 +275,10 @@ impl BinaryInstaller {
let bin_contents = file_get_contents(bin).unwrap_or_default();
// For php files, we generate a PHP proxy instead of a shell one,
// which allows calling the proxy with a custom php process
- if let Some(m) = Preg::is_match_with_indexed_captures(
- r"{^(#!.*\r?\n)?[\r\n\t ]*<\?php}",
- &bin_contents,
- )
- .ok()
- .flatten()
+ if let Some(m) =
+ Preg::is_match_with_indexed_captures(r"{^(#!.*\r?\n)?[\r\n\t ]*<\?php}", &bin_contents)
+ .ok()
+ .flatten()
{
// carry over the existing shebang if present, otherwise add our own
let proxy_code = if m.get(1).is_none() {
@@ -291,9 +291,7 @@ impl BinaryInstaller {
.find_shortest_path_code(link, bin, false, true);
let mut stream_proxy_code = String::new();
let mut stream_hint = String::new();
- let mut globals_code = format!(
- "$GLOBALS['_composer_bin_dir'] = __DIR__;\n",
- );
+ let mut globals_code = format!("$GLOBALS['_composer_bin_dir'] = __DIR__;\n",);
let mut phpunit_hack1 = String::new();
let mut phpunit_hack2 = String::new();
// Don't expose autoload path when vendor dir was not set in custom installers
@@ -326,11 +324,14 @@ impl BinaryInstaller {
phpunit_hack1 = "'phpvfscomposer://'.".to_string();
phpunit_hack2 = "
$data = str_replace('__DIR__', var_export(dirname($this->realpath), true), $data);
- $data = str_replace('__FILE__', var_export($this->realpath, true), $data);".to_string();
+ $data = str_replace('__FILE__', var_export($this->realpath, true), $data);"
+ .to_string();
}
}
if trim(m.get(0).map(|s| s.as_str()).unwrap_or(""), None) != "<?php" {
- stream_hint = format!(" using a stream wrapper to prevent the shebang from being output on PHP<8\n *");
+ stream_hint = format!(
+ " using a stream wrapper to prevent the shebang from being output on PHP<8\n *"
+ );
stream_proxy_code = format!(
"if (PHP_VERSION_ID < 80000) {{\n if (!class_exists('Composer\\BinProxyWrapper')) {{\n /**\n * @internal\n */\n final class BinProxyWrapper\n {{\n private $handle;\n private $position;\n private $realpath;\n\n public function stream_open($path, $mode, $options, &$opened_path)\n {{\n // get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution\n $opened_path = substr($path, 17);\n $this->realpath = realpath($opened_path) ?: $opened_path;\n $opened_path = {phpunit_hack1}$this->realpath;\n $this->handle = fopen($this->realpath, $mode);\n $this->position = 0;\n\n return (bool) $this->handle;\n }}\n\n public function stream_read($count)\n {{\n $data = fread($this->handle, $count);\n\n if ($this->position === 0) {{\n $data = preg_replace('{{^#!.*\\r?\\n}}', '', $data);\n }}{phpunit_hack2}\n\n $this->position += strlen($data);\n\n return $data;\n }}\n\n public function stream_cast($castAs)\n {{\n return $this->handle;\n }}\n\n public function stream_close()\n {{\n fclose($this->handle);\n }}\n\n public function stream_lock($operation)\n {{\n return $operation ? flock($this->handle, $operation) : true;\n }}\n\n public function stream_seek($offset, $whence)\n {{\n if (0 === fseek($this->handle, $offset, $whence)) {{\n $this->position = ftell($this->handle);\n return true;\n }}\n\n return false;\n }}\n\n public function stream_tell()\n {{\n return $this->position;\n }}\n\n public function stream_eof()\n {{\n return feof($this->handle);\n }}\n\n public function stream_stat()\n {{\n return array();\n }}\n\n public function stream_set_option($option, $arg1, $arg2)\n {{\n return true;\n }}\n\n public function url_stat($path, $flags)\n {{\n $path = substr($path, 17);\n if (file_exists($path)) {{\n return stat($path);\n }}\n\n return false;\n }}\n }}\n }}\n\n if (\n (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))\n || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\\BinProxyWrapper'))\n ) {{\n return include(\"phpvfscomposer://\" . {bin_path_exported});\n }}\n}}\n",
phpunit_hack1 = phpunit_hack1,
diff --git a/crates/shirabe/src/installer/installation_manager.rs b/crates/shirabe/src/installer/installation_manager.rs
index 52831aa..519091f 100644
--- a/crates/shirabe/src/installer/installation_manager.rs
+++ b/crates/shirabe/src/installer/installation_manager.rs
@@ -6,8 +6,8 @@ use shirabe_external_packages::react::promise;
use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
use shirabe_external_packages::seld::signal::signal_handler::SignalHandler;
use shirabe_php_shim::{
- array_search_mixed, array_splice, array_unshift, count, http_build_query, json_encode,
- str_contains, str_replace, strpos, strtolower, ucfirst, InvalidArgumentException, PhpMixed,
+ InvalidArgumentException, PhpMixed, array_search_mixed, array_splice, array_unshift, count,
+ http_build_query, json_encode, str_contains, str_replace, strpos, strtolower, ucfirst,
};
use crate::dependency_resolver::operation::install_operation::InstallOperation;
@@ -143,7 +143,9 @@ impl InstallationManager {
&& self.is_package_installed(repo, alias.get_alias_of())?);
}
- Ok(self.get_installer(package.get_type())?.is_installed(repo, package))
+ Ok(self
+ .get_installer(package.get_type())?
+ .is_installed(repo, package))
}
/// Install binary for the given package.
@@ -177,8 +179,10 @@ impl InstallationManager {
download_only: bool,
) -> Result<()> {
// @var array<callable(): ?PromiseInterface<void|null>> $cleanupPromises
- let mut cleanup_promises: IndexMap<i64, Box<dyn Fn() -> Option<Box<dyn PromiseInterface>>>> =
- IndexMap::new();
+ let mut cleanup_promises: IndexMap<
+ i64,
+ Box<dyn Fn() -> Option<Box<dyn PromiseInterface>>>,
+ > = IndexMap::new();
let signal_handler = SignalHandler::create(
vec![
@@ -452,13 +456,16 @@ impl InstallationManager {
};
if run_scripts && self.event_dispatcher.is_some() {
- self.event_dispatcher.as_mut().unwrap().dispatch_package_event(
- event_name,
- dev_mode,
- repo,
- all_operations,
- operation.as_ref(),
- );
+ self.event_dispatcher
+ .as_mut()
+ .unwrap()
+ .dispatch_package_event(
+ event_name,
+ dev_mode,
+ repo,
+ all_operations,
+ operation.as_ref(),
+ );
}
let _dispatcher = self.event_dispatcher.as_ref();
@@ -524,11 +531,8 @@ impl InstallationManager {
// progress.clear();
// ProgressBar in non-decorated output does not output a final line-break and clear() does nothing
if !self.io.is_decorated() {
- self.io.write_error(
- PhpMixed::String(String::new()),
- true,
- IOInterface::NORMAL,
- );
+ self.io
+ .write_error(PhpMixed::String(String::new()), true, IOInterface::NORMAL);
}
}
}
@@ -536,7 +540,10 @@ impl InstallationManager {
/// Executes download operation.
///
/// @phpstan-return PromiseInterface<void|null>|null
- pub fn download(&mut self, package: &dyn PackageInterface) -> Option<Box<dyn PromiseInterface>> {
+ pub fn download(
+ &mut self,
+ package: &dyn PackageInterface,
+ ) -> Option<Box<dyn PromiseInterface>> {
let installer = self.get_installer(package.get_type()).ok()?;
let promise = installer.cleanup("install", package, None);
@@ -579,7 +586,10 @@ impl InstallationManager {
self.mark_for_notification(target);
promise
} else {
- let promise = self.get_installer(initial_type).ok()?.uninstall(repo, initial);
+ let promise = self
+ .get_installer(initial_type)
+ .ok()?
+ .uninstall(repo, initial);
let promise = match promise {
Some(p) => p,
None => promise::resolve(None),
@@ -667,10 +677,7 @@ impl InstallationManager {
let mut opts: IndexMap<String, PhpMixed> = IndexMap::new();
opts.insert("retry-auth-failure".to_string(), PhpMixed::Bool(false));
let mut http: IndexMap<String, PhpMixed> = IndexMap::new();
- http.insert(
- "method".to_string(),
- PhpMixed::String("POST".to_string()),
- );
+ http.insert("method".to_string(), PhpMixed::String("POST".to_string()));
http.insert(
"header".to_string(),
PhpMixed::List(vec![Box::new(PhpMixed::String(
@@ -685,15 +692,16 @@ impl InstallationManager {
opts.insert(
"http".to_string(),
PhpMixed::Array(
- http.into_iter()
- .map(|(k, v)| (k, Box::new(v)))
- .collect(),
+ http.into_iter().map(|(k, v)| (k, Box::new(v))).collect(),
),
);
- promises.push(self.loop_.get_http_downloader().add(&url, &PhpMixed::Array(
- opts.into_iter().map(|(k, v)| (k, Box::new(v))).collect(),
- )));
+ promises.push(self.loop_.get_http_downloader().add(
+ &url,
+ &PhpMixed::Array(
+ opts.into_iter().map(|(k, v)| (k, Box::new(v))).collect(),
+ ),
+ ));
}
continue;
@@ -715,8 +723,7 @@ impl InstallationManager {
if let Some(metadata) =
FileDownloader::download_metadata().get(package.get_name())
{
- package_notification
- .insert("downloaded".to_string(), metadata.clone());
+ package_notification.insert("downloaded".to_string(), metadata.clone());
} else {
package_notification
.insert("downloaded".to_string(), PhpMixed::Bool(false));
@@ -757,16 +764,13 @@ impl InstallationManager {
http.insert("timeout".to_string(), PhpMixed::Int(6));
opts.insert(
"http".to_string(),
- PhpMixed::Array(
- http.into_iter()
- .map(|(k, v)| (k, Box::new(v)))
- .collect(),
- ),
+ PhpMixed::Array(http.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
);
- promises.push(self.loop_.get_http_downloader().add(repo_url, &PhpMixed::Array(
- opts.into_iter().map(|(k, v)| (k, Box::new(v))).collect(),
- )));
+ promises.push(self.loop_.get_http_downloader().add(
+ repo_url,
+ &PhpMixed::Array(opts.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
+ ));
}
self.loop_.wait(promises, None);
diff --git a/crates/shirabe/src/installer/installer_event.rs b/crates/shirabe/src/installer/installer_event.rs
index 3d0dccc..456a4bd 100644
--- a/crates/shirabe/src/installer/installer_event.rs
+++ b/crates/shirabe/src/installer/installer_event.rs
@@ -25,7 +25,14 @@ impl InstallerEvent {
transaction: Transaction,
) -> Self {
let inner = Event::new(event_name, vec![], vec![]);
- Self { inner, composer, io, dev_mode, execute_operations, transaction }
+ Self {
+ inner,
+ composer,
+ io,
+ dev_mode,
+ execute_operations,
+ transaction,
+ }
}
pub fn get_composer(&self) -> &Composer {
diff --git a/crates/shirabe/src/installer/installer_interface.rs b/crates/shirabe/src/installer/installer_interface.rs
index fc651bb..16cf10c 100644
--- a/crates/shirabe/src/installer/installer_interface.rs
+++ b/crates/shirabe/src/installer/installer_interface.rs
@@ -1,25 +1,56 @@
//! ref: composer/src/Composer/Installer/InstallerInterface.php
-use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
use crate::package::package_interface::PackageInterface;
use crate::repository::installed_repository_interface::InstalledRepositoryInterface;
+use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
pub trait InstallerInterface {
fn supports(&self, package_type: &str) -> bool;
- fn is_installed(&self, repo: &dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> bool;
+ fn is_installed(
+ &self,
+ repo: &dyn InstalledRepositoryInterface,
+ package: &dyn PackageInterface,
+ ) -> bool;
- fn download(&self, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
+ fn download(
+ &self,
+ package: &dyn PackageInterface,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
- fn prepare(&self, r#type: &str, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
+ fn prepare(
+ &self,
+ r#type: &str,
+ package: &dyn PackageInterface,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
- fn install(&self, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
+ fn install(
+ &self,
+ repo: &mut dyn InstalledRepositoryInterface,
+ package: &dyn PackageInterface,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
- fn update(&self, repo: &mut dyn InstalledRepositoryInterface, initial: &dyn PackageInterface, target: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
+ fn update(
+ &self,
+ repo: &mut dyn InstalledRepositoryInterface,
+ initial: &dyn PackageInterface,
+ target: &dyn PackageInterface,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
- fn uninstall(&self, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
+ fn uninstall(
+ &self,
+ repo: &mut dyn InstalledRepositoryInterface,
+ package: &dyn PackageInterface,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
- fn cleanup(&self, r#type: &str, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
+ fn cleanup(
+ &self,
+ r#type: &str,
+ package: &dyn PackageInterface,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
fn get_install_path(&self, package: &dyn PackageInterface) -> Option<String>;
}
diff --git a/crates/shirabe/src/installer/library_installer.rs b/crates/shirabe/src/installer/library_installer.rs
index cc6e7a6..af11ef6 100644
--- a/crates/shirabe/src/installer/library_installer.rs
+++ b/crates/shirabe/src/installer/library_installer.rs
@@ -6,7 +6,7 @@ use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
use shirabe_php_shim::{
- is_link, preg_quote, realpath, rmdir, rtrim, strpos, InvalidArgumentException, LogicException,
+ InvalidArgumentException, LogicException, is_link, preg_quote, realpath, rmdir, rtrim, strpos,
};
use crate::composer::Composer;
@@ -84,8 +84,11 @@ impl LibraryInstaller {
/// Make sure binaries are installed for a given package.
pub fn ensure_binaries_presence(&self, package: &dyn PackageInterface) {
- self.binary_installer
- .install_binaries(package, &self.get_install_path(package).unwrap(), false);
+ self.binary_installer.install_binaries(
+ package,
+ &self.get_install_path(package).unwrap(),
+ false,
+ );
}
/// Returns the base path of the package without target-dir path
diff --git a/crates/shirabe/src/installer/metapackage_installer.rs b/crates/shirabe/src/installer/metapackage_installer.rs
index 8be21e7..0a5f872 100644
--- a/crates/shirabe/src/installer/metapackage_installer.rs
+++ b/crates/shirabe/src/installer/metapackage_installer.rs
@@ -1,8 +1,5 @@
//! ref: composer/src/Composer/Installer/MetapackageInstaller.php
-use anyhow::Result;
-use shirabe_php_shim::InvalidArgumentException;
-use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
use crate::dependency_resolver::operation::install_operation::InstallOperation;
use crate::dependency_resolver::operation::uninstall_operation::UninstallOperation;
use crate::dependency_resolver::operation::update_operation::UpdateOperation;
@@ -10,6 +7,9 @@ use crate::installer::installer_interface::InstallerInterface;
use crate::io::io_interface::IOInterface;
use crate::package::package_interface::PackageInterface;
use crate::repository::installed_repository_interface::InstalledRepositoryInterface;
+use anyhow::Result;
+use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
+use shirabe_php_shim::InvalidArgumentException;
#[derive(Debug)]
pub struct MetapackageInstaller {
@@ -27,59 +27,116 @@ impl InstallerInterface for MetapackageInstaller {
package_type == "metapackage"
}
- fn is_installed(&self, repo: &dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> bool {
+ fn is_installed(
+ &self,
+ repo: &dyn InstalledRepositoryInterface,
+ package: &dyn PackageInterface,
+ ) -> bool {
repo.has_package(package)
}
- fn download(&self, _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>) -> Result<Option<Box<dyn PromiseInterface>>> {
- Ok(Some(shirabe_external_packages::react::promise::resolve(None)))
+ fn download(
+ &self,
+ _package: &dyn PackageInterface,
+ _prev_package: Option<&dyn PackageInterface>,
+ ) -> Result<Option<Box<dyn PromiseInterface>>> {
+ Ok(Some(shirabe_external_packages::react::promise::resolve(
+ None,
+ )))
}
- fn prepare(&self, _type: &str, _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>) -> Result<Option<Box<dyn PromiseInterface>>> {
- Ok(Some(shirabe_external_packages::react::promise::resolve(None)))
+ fn prepare(
+ &self,
+ _type: &str,
+ _package: &dyn PackageInterface,
+ _prev_package: Option<&dyn PackageInterface>,
+ ) -> Result<Option<Box<dyn PromiseInterface>>> {
+ Ok(Some(shirabe_external_packages::react::promise::resolve(
+ None,
+ )))
}
- fn cleanup(&self, _type: &str, _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>) -> Result<Option<Box<dyn PromiseInterface>>> {
- Ok(Some(shirabe_external_packages::react::promise::resolve(None)))
+ fn cleanup(
+ &self,
+ _type: &str,
+ _package: &dyn PackageInterface,
+ _prev_package: Option<&dyn PackageInterface>,
+ ) -> Result<Option<Box<dyn PromiseInterface>>> {
+ Ok(Some(shirabe_external_packages::react::promise::resolve(
+ None,
+ )))
}
- fn install(&self, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> Result<Option<Box<dyn PromiseInterface>>> {
- self.io.write_error(&format!(" - {}", InstallOperation::format(package, false)), true, IOInterface::NORMAL);
+ fn install(
+ &self,
+ repo: &mut dyn InstalledRepositoryInterface,
+ package: &dyn PackageInterface,
+ ) -> Result<Option<Box<dyn PromiseInterface>>> {
+ self.io.write_error(
+ &format!(" - {}", InstallOperation::format(package, false)),
+ true,
+ IOInterface::NORMAL,
+ );
repo.add_package(package.clone_box());
- Ok(Some(shirabe_external_packages::react::promise::resolve(None)))
+ Ok(Some(shirabe_external_packages::react::promise::resolve(
+ None,
+ )))
}
- fn update(&self, repo: &mut dyn InstalledRepositoryInterface, initial: &dyn PackageInterface, target: &dyn PackageInterface) -> Result<Option<Box<dyn PromiseInterface>>> {
+ fn update(
+ &self,
+ repo: &mut dyn InstalledRepositoryInterface,
+ initial: &dyn PackageInterface,
+ target: &dyn PackageInterface,
+ ) -> Result<Option<Box<dyn PromiseInterface>>> {
if !repo.has_package(initial) {
return Err(InvalidArgumentException {
message: format!("Package is not installed: {}", initial),
code: 0,
- }.into());
+ }
+ .into());
}
- self.io.write_error(&format!(" - {}", UpdateOperation::format(initial, target, false)), true, IOInterface::NORMAL);
+ self.io.write_error(
+ &format!(" - {}", UpdateOperation::format(initial, target, false)),
+ true,
+ IOInterface::NORMAL,
+ );
repo.remove_package(initial);
repo.add_package(target.clone_box());
- Ok(Some(shirabe_external_packages::react::promise::resolve(None)))
+ Ok(Some(shirabe_external_packages::react::promise::resolve(
+ None,
+ )))
}
- fn uninstall(&self, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> Result<Option<Box<dyn PromiseInterface>>> {
+ fn uninstall(
+ &self,
+ repo: &mut dyn InstalledRepositoryInterface,
+ package: &dyn PackageInterface,
+ ) -> Result<Option<Box<dyn PromiseInterface>>> {
if !repo.has_package(package) {
return Err(InvalidArgumentException {
message: format!("Package is not installed: {}", package),
code: 0,
- }.into());
+ }
+ .into());
}
- self.io.write_error(&format!(" - {}", UninstallOperation::format(package, false)), true, IOInterface::NORMAL);
+ self.io.write_error(
+ &format!(" - {}", UninstallOperation::format(package, false)),
+ true,
+ IOInterface::NORMAL,
+ );
repo.remove_package(package);
- Ok(Some(shirabe_external_packages::react::promise::resolve(None)))
+ Ok(Some(shirabe_external_packages::react::promise::resolve(
+ None,
+ )))
}
fn get_install_path(&self, _package: &dyn PackageInterface) -> Option<String> {
diff --git a/crates/shirabe/src/installer/noop_installer.rs b/crates/shirabe/src/installer/noop_installer.rs
index fd30f4a..09e6afd 100644
--- a/crates/shirabe/src/installer/noop_installer.rs
+++ b/crates/shirabe/src/installer/noop_installer.rs
@@ -1,10 +1,10 @@
//! ref: composer/src/Composer/Installer/NoopInstaller.php
-use shirabe_php_shim::InvalidArgumentException;
-use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
use crate::installer::installer_interface::InstallerInterface;
use crate::package::package_interface::PackageInterface;
use crate::repository::installed_repository_interface::InstalledRepositoryInterface;
+use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
+use shirabe_php_shim::InvalidArgumentException;
#[derive(Debug)]
pub struct NoopInstaller;
@@ -14,36 +14,72 @@ impl InstallerInterface for NoopInstaller {
true
}
- fn is_installed(&self, repo: &dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> bool {
+ fn is_installed(
+ &self,
+ repo: &dyn InstalledRepositoryInterface,
+ package: &dyn PackageInterface,
+ ) -> bool {
repo.has_package(package)
}
- fn download(&self, _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
- Ok(Some(shirabe_external_packages::react::promise::resolve(None)))
+ fn download(
+ &self,
+ _package: &dyn PackageInterface,
+ _prev_package: Option<&dyn PackageInterface>,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
+ Ok(Some(shirabe_external_packages::react::promise::resolve(
+ None,
+ )))
}
- fn prepare(&self, _type: &str, _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
- Ok(Some(shirabe_external_packages::react::promise::resolve(None)))
+ fn prepare(
+ &self,
+ _type: &str,
+ _package: &dyn PackageInterface,
+ _prev_package: Option<&dyn PackageInterface>,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
+ Ok(Some(shirabe_external_packages::react::promise::resolve(
+ None,
+ )))
}
- fn cleanup(&self, _type: &str, _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
- Ok(Some(shirabe_external_packages::react::promise::resolve(None)))
+ fn cleanup(
+ &self,
+ _type: &str,
+ _package: &dyn PackageInterface,
+ _prev_package: Option<&dyn PackageInterface>,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
+ Ok(Some(shirabe_external_packages::react::promise::resolve(
+ None,
+ )))
}
- fn install(&self, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
+ fn install(
+ &self,
+ repo: &mut dyn InstalledRepositoryInterface,
+ package: &dyn PackageInterface,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
if !repo.has_package(package) {
repo.add_package(package.clone_box());
}
- Ok(Some(shirabe_external_packages::react::promise::resolve(None)))
+ Ok(Some(shirabe_external_packages::react::promise::resolve(
+ None,
+ )))
}
- fn update(&self, repo: &mut dyn InstalledRepositoryInterface, initial: &dyn PackageInterface, target: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
+ fn update(
+ &self,
+ repo: &mut dyn InstalledRepositoryInterface,
+ initial: &dyn PackageInterface,
+ target: &dyn PackageInterface,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
if !repo.has_package(initial) {
return Err(InvalidArgumentException {
message: format!("Package is not installed: {}", initial),
code: 0,
- }.into());
+ }
+ .into());
}
repo.remove_package(initial);
@@ -51,19 +87,28 @@ impl InstallerInterface for NoopInstaller {
repo.add_package(target.clone_box());
}
- Ok(Some(shirabe_external_packages::react::promise::resolve(None)))
+ Ok(Some(shirabe_external_packages::react::promise::resolve(
+ None,
+ )))
}
- fn uninstall(&self, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
+ fn uninstall(
+ &self,
+ repo: &mut dyn InstalledRepositoryInterface,
+ package: &dyn PackageInterface,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
if !repo.has_package(package) {
return Err(InvalidArgumentException {
message: format!("Package is not installed: {}", package),
code: 0,
- }.into());
+ }
+ .into());
}
repo.remove_package(package);
- Ok(Some(shirabe_external_packages::react::promise::resolve(None)))
+ Ok(Some(shirabe_external_packages::react::promise::resolve(
+ None,
+ )))
}
fn get_install_path(&self, package: &dyn PackageInterface) -> Option<String> {
diff --git a/crates/shirabe/src/installer/package_event.rs b/crates/shirabe/src/installer/package_event.rs
index 537e97e..2bf1c6b 100644
--- a/crates/shirabe/src/installer/package_event.rs
+++ b/crates/shirabe/src/installer/package_event.rs
@@ -1,11 +1,11 @@
//! ref: composer/src/Composer/Installer/PackageEvent.php
-use indexmap::IndexMap;
use crate::composer::Composer;
use crate::dependency_resolver::operation::operation_interface::OperationInterface;
use crate::event_dispatcher::event::Event;
use crate::io::io_interface::IOInterface;
use crate::repository::repository_interface::RepositoryInterface;
+use indexmap::IndexMap;
#[derive(Debug)]
pub struct PackageEvent {
diff --git a/crates/shirabe/src/installer/plugin_installer.rs b/crates/shirabe/src/installer/plugin_installer.rs
index 55a936a..1fd2334 100644
--- a/crates/shirabe/src/installer/plugin_installer.rs
+++ b/crates/shirabe/src/installer/plugin_installer.rs
@@ -1,8 +1,5 @@
//! ref: composer/src/Composer/Installer/PluginInstaller.php
-use anyhow::Result;
-use shirabe_php_shim::{empty, LogicException, PhpMixed, UnexpectedValueException};
-use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
use crate::installer::binary_installer::BinaryInstaller;
use crate::installer::installer_interface::InstallerInterface;
use crate::installer::library_installer::LibraryInstaller;
@@ -13,6 +10,9 @@ use crate::plugin::plugin_manager::PluginManager;
use crate::repository::installed_repository_interface::InstalledRepositoryInterface;
use crate::util::filesystem::Filesystem;
use crate::util::platform::Platform;
+use anyhow::Result;
+use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
+use shirabe_php_shim::{LogicException, PhpMixed, UnexpectedValueException, empty};
#[derive(Debug)]
pub struct PluginInstaller {
@@ -27,7 +27,13 @@ impl PluginInstaller {
binary_installer: Option<BinaryInstaller>,
) -> Self {
Self {
- inner: LibraryInstaller::new(io, composer, Some("composer-plugin".to_string()), fs, binary_installer),
+ inner: LibraryInstaller::new(
+ io,
+ composer,
+ Some("composer-plugin".to_string()),
+ fs,
+ binary_installer,
+ ),
}
}
@@ -36,8 +42,16 @@ impl PluginInstaller {
self.get_plugin_manager().disable_plugins();
}
- fn rollback_install(&self, e: anyhow::Error, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> Result<()> {
- self.inner.io.write_error(&format!("Plugin initialization failed ({}), uninstalling plugin", e));
+ fn rollback_install(
+ &self,
+ e: anyhow::Error,
+ repo: &mut dyn InstalledRepositoryInterface,
+ package: &dyn PackageInterface,
+ ) -> Result<()> {
+ self.inner.io.write_error(&format!(
+ "Plugin initialization failed ({}), uninstalling plugin",
+ e
+ ));
self.inner.uninstall(repo, package)?;
Err(e)
}
@@ -48,7 +62,9 @@ impl PluginInstaller {
self.inner.composer.is_full_composer(),
"{}",
LogicException {
- message: "PluginInstaller should be initialized with a fully loaded Composer instance.".to_string(),
+ message:
+ "PluginInstaller should be initialized with a fully loaded Composer instance."
+ .to_string(),
code: 0,
}
);
@@ -62,24 +78,41 @@ impl InstallerInterface for PluginInstaller {
package_type == "composer-plugin" || package_type == "composer-installer"
}
- fn is_installed(&self, repo: &dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> bool {
+ fn is_installed(
+ &self,
+ repo: &dyn InstalledRepositoryInterface,
+ package: &dyn PackageInterface,
+ ) -> bool {
self.inner.is_installed(repo, package)
}
- fn prepare(&self, r#type: &str, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> Result<Option<Box<dyn PromiseInterface>>> {
- if (r#type == "install" || r#type == "update") && !self.get_plugin_manager().are_plugins_disabled("local") {
- let plugin_optional = package.get_extra()
+ fn prepare(
+ &self,
+ r#type: &str,
+ package: &dyn PackageInterface,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> Result<Option<Box<dyn PromiseInterface>>> {
+ if (r#type == "install" || r#type == "update")
+ && !self.get_plugin_manager().are_plugins_disabled("local")
+ {
+ let plugin_optional = package
+ .get_extra()
.get("plugin-optional")
.map(|v| matches!(v, PhpMixed::Bool(true)))
.unwrap_or(false);
// TODO(plugin): check if plugin is allowed
- self.get_plugin_manager().is_plugin_allowed(package.get_name(), false, plugin_optional);
+ self.get_plugin_manager()
+ .is_plugin_allowed(package.get_name(), false, plugin_optional);
}
self.inner.prepare(r#type, package, prev_package)
}
- fn download(&self, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> Result<Option<Box<dyn PromiseInterface>>> {
+ fn download(
+ &self,
+ package: &dyn PackageInterface,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> Result<Option<Box<dyn PromiseInterface>>> {
let extra = package.get_extra();
let class = extra.get("class").cloned().unwrap_or(PhpMixed::Null);
if empty(&class) {
@@ -95,7 +128,11 @@ impl InstallerInterface for PluginInstaller {
self.inner.download(package, prev_package)
}
- fn install(&self, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> Result<Option<Box<dyn PromiseInterface>>> {
+ fn install(
+ &self,
+ repo: &mut dyn InstalledRepositoryInterface,
+ package: &dyn PackageInterface,
+ ) -> Result<Option<Box<dyn PromiseInterface>>> {
let promise = self.inner.install(repo, package)?;
let promise = match promise {
Some(p) => p,
@@ -111,7 +148,12 @@ impl InstallerInterface for PluginInstaller {
}))))
}
- fn update(&self, repo: &mut dyn InstalledRepositoryInterface, initial: &dyn PackageInterface, target: &dyn PackageInterface) -> Result<Option<Box<dyn PromiseInterface>>> {
+ fn update(
+ &self,
+ repo: &mut dyn InstalledRepositoryInterface,
+ initial: &dyn PackageInterface,
+ target: &dyn PackageInterface,
+ ) -> Result<Option<Box<dyn PromiseInterface>>> {
let promise = self.inner.update(repo, initial, target)?;
let promise = match promise {
Some(p) => p,
@@ -128,14 +170,23 @@ impl InstallerInterface for PluginInstaller {
}))))
}
- fn uninstall(&self, repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> Result<Option<Box<dyn PromiseInterface>>> {
+ fn uninstall(
+ &self,
+ repo: &mut dyn InstalledRepositoryInterface,
+ package: &dyn PackageInterface,
+ ) -> Result<Option<Box<dyn PromiseInterface>>> {
// TODO(plugin): uninstall package from plugin manager
self.get_plugin_manager().uninstall_package(package);
self.inner.uninstall(repo, package)
}
- fn cleanup(&self, r#type: &str, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> Result<Option<Box<dyn PromiseInterface>>> {
+ fn cleanup(
+ &self,
+ r#type: &str,
+ package: &dyn PackageInterface,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> Result<Option<Box<dyn PromiseInterface>>> {
self.inner.cleanup(r#type, package, prev_package)
}
diff --git a/crates/shirabe/src/installer/project_installer.rs b/crates/shirabe/src/installer/project_installer.rs
index 6472472..1a097b0 100644
--- a/crates/shirabe/src/installer/project_installer.rs
+++ b/crates/shirabe/src/installer/project_installer.rs
@@ -1,12 +1,12 @@
//! ref: composer/src/Composer/Installer/ProjectInstaller.php
-use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
-use shirabe_php_shim::InvalidArgumentException;
use crate::downloader::download_manager::DownloadManager;
use crate::installer::installer_interface::InstallerInterface;
use crate::package::package_interface::PackageInterface;
use crate::repository::installed_repository_interface::InstalledRepositoryInterface;
use crate::util::filesystem::Filesystem;
+use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
+use shirabe_php_shim::InvalidArgumentException;
#[derive(Debug)]
pub struct ProjectInstaller {
@@ -31,49 +31,88 @@ impl InstallerInterface for ProjectInstaller {
true
}
- fn is_installed(&self, _repo: &dyn InstalledRepositoryInterface, _package: &dyn PackageInterface) -> bool {
+ fn is_installed(
+ &self,
+ _repo: &dyn InstalledRepositoryInterface,
+ _package: &dyn PackageInterface,
+ ) -> bool {
false
}
- fn download(&self, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
+ fn download(
+ &self,
+ package: &dyn PackageInterface,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
let install_path = &self.install_path;
- if std::path::Path::new(install_path).exists() && !self.filesystem.is_dir_empty(install_path) {
+ if std::path::Path::new(install_path).exists()
+ && !self.filesystem.is_dir_empty(install_path)
+ {
return Err(InvalidArgumentException {
message: format!("Project directory {} is not empty.", install_path),
code: 0,
- }.into());
+ }
+ .into());
}
if !std::path::Path::new(install_path).is_dir() {
std::fs::create_dir_all(install_path)?;
}
- self.download_manager.download(package, install_path, prev_package)
+ self.download_manager
+ .download(package, install_path, prev_package)
}
- fn prepare(&self, r#type: &str, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
- self.download_manager.prepare(r#type, package, &self.install_path, prev_package)
+ fn prepare(
+ &self,
+ r#type: &str,
+ package: &dyn PackageInterface,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
+ self.download_manager
+ .prepare(r#type, package, &self.install_path, prev_package)
}
- fn cleanup(&self, r#type: &str, package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
- self.download_manager.cleanup(r#type, package, &self.install_path, prev_package)
+ fn cleanup(
+ &self,
+ r#type: &str,
+ package: &dyn PackageInterface,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
+ self.download_manager
+ .cleanup(r#type, package, &self.install_path, prev_package)
}
- fn install(&self, _repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
+ fn install(
+ &self,
+ _repo: &mut dyn InstalledRepositoryInterface,
+ package: &dyn PackageInterface,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
self.download_manager.install(package, &self.install_path)
}
- fn update(&self, _repo: &mut dyn InstalledRepositoryInterface, _initial: &dyn PackageInterface, _target: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
+ fn update(
+ &self,
+ _repo: &mut dyn InstalledRepositoryInterface,
+ _initial: &dyn PackageInterface,
+ _target: &dyn PackageInterface,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
Err(InvalidArgumentException {
message: "not supported".to_string(),
code: 0,
- }.into())
+ }
+ .into())
}
- fn uninstall(&self, _repo: &mut dyn InstalledRepositoryInterface, _package: &dyn PackageInterface) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
+ fn uninstall(
+ &self,
+ _repo: &mut dyn InstalledRepositoryInterface,
+ _package: &dyn PackageInterface,
+ ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
Err(InvalidArgumentException {
message: "not supported".to_string(),
code: 0,
- }.into())
+ }
+ .into())
}
fn get_install_path(&self, _package: &dyn PackageInterface) -> Option<String> {
diff --git a/crates/shirabe/src/installer/suggested_packages_reporter.rs b/crates/shirabe/src/installer/suggested_packages_reporter.rs
index 77b4c34..cfee20b 100644
--- a/crates/shirabe/src/installer/suggested_packages_reporter.rs
+++ b/crates/shirabe/src/installer/suggested_packages_reporter.rs
@@ -1,11 +1,11 @@
//! ref: composer/src/Composer/Installer/SuggestedPackagesReporter.php
-use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::formatter::output_formatter::OutputFormatter;
use crate::io::io_interface::IOInterface;
use crate::package::package_interface::PackageInterface;
use crate::repository::installed_repository::InstalledRepository;
+use indexmap::IndexMap;
+use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::symfony::component::console::formatter::output_formatter::OutputFormatter;
#[derive(Debug)]
pub struct SuggestedPackagesReporter {
@@ -48,7 +48,12 @@ impl SuggestedPackagesReporter {
self
}
- pub fn output(&self, mode: i64, installed_repo: Option<&InstalledRepository>, only_dependents_of: Option<&dyn PackageInterface>) {
+ pub fn output(
+ &self,
+ mode: i64,
+ installed_repo: Option<&InstalledRepository>,
+ only_dependents_of: Option<&dyn PackageInterface>,
+ ) {
let suggested_packages = self.get_filtered_suggestions(installed_repo, only_dependents_of);
let mut suggesters: IndexMap<String, IndexMap<String, String>> = IndexMap::new();
@@ -78,7 +83,8 @@ impl SuggestedPackagesReporter {
// Grouped by package
if mode & Self::MODE_BY_PACKAGE != 0 {
for (suggester, suggestions) in &suggesters {
- self.io.write(&format!("<comment>{}</comment> suggests:", suggester));
+ self.io
+ .write(&format!("<comment>{}</comment> suggests:", suggester));
for (suggestion, reason) in suggestions {
self.io.write(&format!(
@@ -102,7 +108,10 @@ impl SuggestedPackagesReporter {
self.io.write(&"-".repeat(78));
}
for (suggestion, suggesters) in &suggested {
- self.io.write(&format!("<comment>{}</comment> is suggested by:", suggestion));
+ self.io.write(&format!(
+ "<comment>{}</comment> is suggested by:",
+ suggestion
+ ));
for (suggester, reason) in suggesters {
self.io.write(&format!(
@@ -128,7 +137,11 @@ impl SuggestedPackagesReporter {
}
}
- pub fn output_minimalistic(&self, installed_repo: Option<&InstalledRepository>, only_dependents_of: Option<&dyn PackageInterface>) {
+ pub fn output_minimalistic(
+ &self,
+ installed_repo: Option<&InstalledRepository>,
+ only_dependents_of: Option<&dyn PackageInterface>,
+ ) {
let suggested_packages = self.get_filtered_suggestions(installed_repo, only_dependents_of);
if !suggested_packages.is_empty() {
self.io.write_error(&format!(
@@ -138,7 +151,11 @@ impl SuggestedPackagesReporter {
}
}
- fn get_filtered_suggestions(&self, installed_repo: Option<&InstalledRepository>, only_dependents_of: Option<&dyn PackageInterface>) -> Vec<IndexMap<String, String>> {
+ fn get_filtered_suggestions(
+ &self,
+ installed_repo: Option<&InstalledRepository>,
+ only_dependents_of: Option<&dyn PackageInterface>,
+ ) -> Vec<IndexMap<String, String>> {
let suggested_packages = self.get_packages();
let mut installed_names: Vec<String> = Vec::new();
if installed_repo.is_some() && !suggested_packages.is_empty() {
@@ -149,7 +166,9 @@ impl SuggestedPackagesReporter {
let mut source_filter: Vec<String> = Vec::new();
if let Some(only_dependents_of) = only_dependents_of {
- source_filter = only_dependents_of.get_requires().values()
+ source_filter = only_dependents_of
+ .get_requires()
+ .values()
.chain(only_dependents_of.get_dev_requires().values())
.map(|link| link.get_target().to_string())
.collect();
@@ -171,16 +190,10 @@ impl SuggestedPackagesReporter {
}
fn escape_output(&self, string: &str) -> String {
- OutputFormatter::escape(
- &self.remove_control_characters(string)
- )
+ OutputFormatter::escape(&self.remove_control_characters(string))
}
fn remove_control_characters(&self, string: &str) -> String {
- Preg::replace(
- "/[[:cntrl:]]/",
- "",
- &string.replace('\n', " "),
- )
+ Preg::replace("/[[:cntrl:]]/", "", &string.replace('\n', " "))
}
}