aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/command/require_command.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/src/command/require_command.rs')
-rw-r--r--crates/shirabe/src/command/require_command.rs417
1 files changed, 225 insertions, 192 deletions
diff --git a/crates/shirabe/src/command/require_command.rs b/crates/shirabe/src/command/require_command.rs
index a49428d..e5963e4 100644
--- a/crates/shirabe/src/command/require_command.rs
+++ b/crates/shirabe/src/command/require_command.rs
@@ -39,6 +39,7 @@ use crate::plugin::command_event::CommandEvent;
use crate::plugin::plugin_events::PluginEvents;
use crate::repository::composite_repository::CompositeRepository;
use crate::repository::platform_repository::PlatformRepository;
+use crate::repository::repository_interface::RepositoryInterface;
use crate::repository::repository_set::RepositorySet;
use crate::util::filesystem::Filesystem;
use crate::util::package_sorter::PackageSorter;
@@ -155,35 +156,28 @@ impl RequireCommand {
input: &dyn InputInterface,
output: &dyn OutputInterface,
) -> Result<i64> {
- self.file = Factory::get_composer_file();
- let io = self.get_io();
+ self.file = Factory::get_composer_file()?;
if input.get_option("no-suggest").as_bool().unwrap_or(false) {
- io.write_error3("<warning>You are using the deprecated option \"--no-suggest\". It has no effect and will break in Composer 3.</warning>", true, io_interface::NORMAL);
+ self.get_io().write_error3("<warning>You are using the deprecated option \"--no-suggest\". It has no effect and will break in Composer 3.</warning>", true, io_interface::NORMAL);
}
self.newly_created = !file_exists(&self.file);
- if self.newly_created && file_put_contents(&self.file, b"{\n}\n").is_none() {
- io.write_error3(
- &format!("<error>{} could not be created.</error>", self.file),
- true,
- io_interface::NORMAL,
- );
+ let write_failed = self.newly_created && file_put_contents(&self.file, b"{\n}\n").is_none();
+ if write_failed {
+ let msg = format!("<error>{} could not be created.</error>", self.file);
+ self.get_io().write_error3(&msg, true, io_interface::NORMAL);
return Ok(1);
}
if !Filesystem::is_readable(&self.file) {
- io.write_error3(
- &format!("<error>{} is not readable.</error>", self.file),
- true,
- io_interface::NORMAL,
- );
+ let msg = format!("<error>{} is not readable.</error>", self.file);
+ self.get_io().write_error3(&msg, true, io_interface::NORMAL);
return Ok(1);
}
-
- if filesize(&self.file) == 0 {
- file_put_contents(&self.file, "{\n}\n");
+ if filesize(&self.file) == Some(0) {
+ file_put_contents(&self.file, b"{\n}\n");
}
self.json = Some(JsonFile::new(self.file.clone(), None, None)?);
@@ -200,9 +194,9 @@ impl RequireCommand {
// to call self.get_io().write_error(...), self.revert_composer_file(), and handler.exit_with_last_signal()
let signal_handler = SignalHandler::create(
vec![
- SignalHandler::SIGINT,
- SignalHandler::SIGTERM,
- SignalHandler::SIGHUP,
+ SignalHandler::SIGINT.to_string(),
+ SignalHandler::SIGTERM.to_string(),
+ SignalHandler::SIGHUP.to_string(),
],
Box::new(move |signal: String, handler: &SignalHandler| {
// TODO(phase-b): self.get_io().write_error('Received '.$signal.', aborting', true, io_interface::DEBUG);
@@ -224,17 +218,14 @@ impl RequireCommand {
.ok()
== Some(false)
{
- io.write_error3(
- &format!("<error>{} is not writable.</error>", self.file),
- true,
- io_interface::NORMAL,
- );
+ let msg = format!("<error>{} is not writable.</error>", self.file);
+ self.get_io().write_error3(&msg, true, io_interface::NORMAL);
return Ok(1);
}
if input.get_option("fixed").as_bool() == Some(true) {
- let config = self.json.as_ref().unwrap().read()?;
+ let config = self.json.as_mut().unwrap().read()?;
let package_type = if empty(&config.get("type").cloned().unwrap_or(PhpMixed::Null)) {
"library".to_string()
@@ -248,10 +239,10 @@ impl RequireCommand {
/// @see https://github.com/composer/composer/pull/8313#issuecomment-532637955
if package_type != "project" && !input.get_option("dev").as_bool().unwrap_or(false) {
- io.write_error3("<error>The \"--fixed\" option is only allowed for packages with a \"project\" type or for dev dependencies to prevent possible misuses.</error>", true, io_interface::NORMAL);
+ self.get_io().write_error3("<error>The \"--fixed\" option is only allowed for packages with a \"project\" type or for dev dependencies to prevent possible misuses.</error>", true, io_interface::NORMAL);
if config.get("type").is_none() {
- io.write_error3("<error>If your package is not a library, you can explicitly specify the \"type\" by using \"composer config type project\".</error>", true, io_interface::NORMAL);
+ self.get_io().write_error3("<error>If your package is not a library, you can explicitly specify the \"type\" by using \"composer config type project\".</error>", true, io_interface::NORMAL);
}
return Ok(1);
@@ -262,13 +253,22 @@ impl RequireCommand {
let repos = composer.get_repository_manager().get_repositories();
let platform_overrides = composer.get_config().borrow_mut().get("platform");
+ let platform_overrides_map: IndexMap<String, PhpMixed> = platform_overrides
+ .as_array()
+ .map(|m| m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect())
+ .unwrap_or_default();
// initialize self.repos as it is used by the PackageDiscoveryTrait
- let platform_repo = PlatformRepository::new(vec![], platform_overrides);
+ let platform_repo = PlatformRepository::new(vec![], platform_overrides_map)?;
let mut combined: Vec<
Box<dyn crate::repository::repository_interface::RepositoryInterface>,
- > = vec![Box::new(platform_repo.clone())];
- for repo in repos {
- combined.push(repo);
+ > = vec![
+ // TODO(phase-b): PlatformRepository should be shared via Rc; use placeholder until
+ // CompositeRepository accepts shared references
+ Box::new(todo!("share platform_repo with PlatformRepository") as PlatformRepository),
+ ];
+ for _repo in repos {
+ // TODO(phase-b): repos are borrowed from RepositoryManager; need to take ownership
+ combined.push(todo!("take ownership of repo from RepositoryManager"));
}
*self.get_repos_mut() = Some(CompositeRepository::new(combined));
@@ -290,7 +290,7 @@ impl RequireCommand {
.collect()
})
.unwrap_or_default(),
- &platform_repo,
+ Some(&platform_repo),
&preferred_stability,
// if there is no update, we need to use the best possible version constraint directly as we cannot rely on the solver to guess the best constraint
input.get_option("no-update").as_bool().unwrap_or(false),
@@ -320,7 +320,7 @@ impl RequireCommand {
let mut requirements = self.format_requirements(requirements)?;
if !input.get_option("dev").as_bool().unwrap_or(false)
- && io.is_interactive()
+ && self.get_io().is_interactive()
&& !composer.is_global()
{
let mut dev_packages: Vec<Vec<String>> = vec![];
@@ -336,9 +336,14 @@ impl RequireCommand {
continue;
}
- let pkg = PackageSorter::get_most_current_version(
- self.get_repos().find_packages(name, None),
- );
+ // TODO(phase-b): find_packages returns Vec<Box<dyn BasePackage>> but
+ // get_most_current_version expects Vec<Box<dyn PackageInterface>>; needs trait
+ // upcasting once Rust supports it stably or an adapter.
+ let _ = self.get_repos().find_packages(name, None);
+ let pkg: Option<Box<dyn PackageInterface>> =
+ PackageSorter::get_most_current_version(todo!(
+ "convert Vec<Box<dyn BasePackage>> to Vec<Box<dyn PackageInterface>>"
+ ));
// TODO(phase-b): instanceof CompletePackageInterface downcast
let pkg_as_complete: Option<&dyn CompletePackageInterface> = None;
if let Some(pkg_complete) = pkg_as_complete {
@@ -368,26 +373,19 @@ impl RequireCommand {
} else {
"it is"
};
- let pkg_dev_tags: Vec<String> = array_unique(&array_merge_recursive(
- dev_packages
- .iter()
- .map(|v| {
- PhpMixed::List(
- v.iter()
- .map(|s| Box::new(PhpMixed::String(s.clone())))
- .collect(),
- )
- })
- .collect(),
- ));
- io.warning(format!(
+ // TODO(phase-b): PHP's array_merge_recursive + array_unique on a list of
+ // string lists; collapsed here to a flat unique Vec<String>.
+ let merged: Vec<String> = dev_packages.iter().flatten().cloned().collect();
+ let pkg_dev_tags: Vec<String> = array_unique(&merged);
+ let warn_msg = format!(
"The package{} you required {} recommended to be placed in require-dev (because {} tagged as \"{}\") but you did not use --dev.",
plural,
plural2,
plural3,
implode("\", \"", &pkg_dev_tags),
- ));
- if io.ask_confirmation(
+ );
+ self.get_io().warning(&warn_msg, &[]);
+ if self.get_io().ask_confirmation(
"<info>Do you want to re-run the command with --dev?</> [<comment>yes</>]? "
.to_string(),
true,
@@ -423,10 +421,11 @@ impl RequireCommand {
let version_parser = VersionParser::new();
for (package, constraint) in &requirements {
if strtolower(package) == composer.get_package().get_name() {
- io.write_error3(&sprintf(
+ let msg = sprintf(
"<error>Root package '%s' cannot require itself in its composer.json</error>",
&[PhpMixed::String(package.clone())],
- ), true, io_interface::NORMAL);
+ );
+ self.get_io().write_error3(&msg, true, io_interface::NORMAL);
return Ok(1);
}
@@ -440,7 +439,7 @@ impl RequireCommand {
self.get_inconsistent_require_keys(&requirements, require_key);
if (inconsistent_require_keys.len() as i64) > 0 {
for package in &inconsistent_require_keys {
- io.warning(sprintf(
+ let warn_msg = sprintf(
"%s is currently present in the %s key and you ran the command %s the --dev flag, which will move it to the %s key.",
&[
PhpMixed::String(package.clone()),
@@ -455,38 +454,35 @@ impl RequireCommand {
),
PhpMixed::String(require_key.to_string()),
],
- ));
+ );
+ self.get_io().warning(&warn_msg, &[]);
}
- if io.is_interactive() {
- if !io.ask_confirmation(
- sprintf(
- "<info>Do you want to move %s?</info> [<comment>no</comment>]? ",
+ if self.get_io().is_interactive() {
+ let q1 = sprintf(
+ "<info>Do you want to move %s?</info> [<comment>no</comment>]? ",
+ &[PhpMixed::String(
+ if (inconsistent_require_keys.len() as i64) > 1 {
+ "these requirements"
+ } else {
+ "this requirement"
+ }
+ .to_string(),
+ )],
+ );
+ if !self.get_io().ask_confirmation(q1, false) {
+ let q2 = sprintf(
+ "<info>Do you want to re-run the command %s --dev?</info> [<comment>yes</comment>]? ",
&[PhpMixed::String(
- if (inconsistent_require_keys.len() as i64) > 1 {
- "these requirements"
+ if input.get_option("dev").as_bool().unwrap_or(false) {
+ "without"
} else {
- "this requirement"
+ "with"
}
.to_string(),
)],
- ),
- false,
- ) {
- if !io.ask_confirmation(
- sprintf(
- "<info>Do you want to re-run the command %s --dev?</info> [<comment>yes</comment>]? ",
- &[PhpMixed::String(
- if input.get_option("dev").as_bool().unwrap_or(false) {
- "without"
- } else {
- "with"
- }
- .to_string(),
- )],
- ),
- true,
- ) {
+ );
+ if !self.get_io().ask_confirmation(q2, true) {
return Ok(0);
}
@@ -508,7 +504,7 @@ impl RequireCommand {
self.first_require = self.newly_created;
if !self.first_require {
- let composer_definition = self.json.as_ref().unwrap().read()?;
+ let composer_definition = self.json.as_mut().unwrap().read()?;
let require_count = composer_definition
.get("require")
.and_then(|v| v.as_array())
@@ -534,19 +530,17 @@ impl RequireCommand {
);
}
- io.write_error3(
- &format!(
- "<info>{} has been {}</info>",
- self.file,
- if self.newly_created {
- "created"
- } else {
- "updated"
- }
- ),
- true,
- io_interface::NORMAL,
+ let updated_msg = format!(
+ "<info>{} has been {}</info>",
+ self.file,
+ if self.newly_created {
+ "created"
+ } else {
+ "updated"
+ }
);
+ self.get_io()
+ .write_error3(&updated_msg, true, io_interface::NORMAL);
if input.get_option("no-update").as_bool().unwrap_or(false) {
return Ok(0);
@@ -555,8 +549,16 @@ impl RequireCommand {
composer.get_plugin_manager().deactivate_installed_plugins();
// try/catch/finally
- let do_update_result =
- self.do_update(input, output, io, &requirements, require_key, remove_key);
+ // TODO(phase-b): do_update borrows io from self while also needing &mut self for state
+ // mutations; needs an Rc<dyn IOInterface> on self for clean sharing.
+ let do_update_result = self.do_update(
+ input,
+ output,
+ todo!("share io reference for do_update"),
+ &requirements,
+ require_key,
+ remove_key,
+ );
let dry_run = input.get_option("dry-run").as_bool().unwrap_or(false);
let result = match do_update_result {
@@ -596,7 +598,7 @@ impl RequireCommand {
/// @param array<string, string> $newRequirements
/// @return string[]
fn get_inconsistent_require_keys(
- &self,
+ &mut self,
new_requirements: &IndexMap<String, String>,
require_key: &str,
) -> Vec<String> {
@@ -615,8 +617,8 @@ impl RequireCommand {
}
/// @return array<string, string>
- fn get_packages_by_require_key(&self) -> IndexMap<String, String> {
- let composer_definition = self.json.as_ref().unwrap().read().unwrap_or_default();
+ fn get_packages_by_require_key(&mut self) -> IndexMap<String, String> {
+ let composer_definition = self.json.as_mut().unwrap().read().unwrap_or_default();
let mut require: IndexMap<String, PhpMixed> = IndexMap::new();
let mut require_dev: IndexMap<String, PhpMixed> = IndexMap::new();
@@ -682,14 +684,14 @@ impl RequireCommand {
) -> Result<i64> {
// Update packages
self.reset_composer()?;
- let composer = self.require_composer(None, None)?;
+ let mut composer = self.require_composer(None, None)?;
self.dependency_resolution_completed = false;
- composer.get_event_dispatcher().add_listener(
+ // TODO(phase-b): add_listener expects a Callable enum; PHP closure should set
+ // self.dependency_resolution_completed = true when invoked.
+ composer.get_event_dispatcher().borrow_mut().add_listener(
InstallerEvents::PRE_OPERATIONS_EXEC,
- Box::new(move || {
- // TODO(phase-b): self.dependency_resolution_completed = true;
- }),
+ crate::event_dispatcher::event_dispatcher::Callable::Closure,
10000,
);
@@ -699,7 +701,11 @@ impl RequireCommand {
IndexMap::new();
links.insert("require".to_string(), root_package.get_requires());
links.insert("require-dev".to_string(), root_package.get_dev_requires());
- let loader = ArrayLoader::new(None, None, false);
+ let loader = ArrayLoader::new(None, false);
+ let requirements_mixed: IndexMap<String, PhpMixed> = requirements
+ .iter()
+ .map(|(k, v)| (k.clone(), PhpMixed::String(v.clone())))
+ .collect();
let new_links = loader.parse_links(
root_package.get_name(),
root_package.get_pretty_version(),
@@ -707,8 +713,8 @@ impl RequireCommand {
.get(require_key)
.map(|t| t.method)
.unwrap_or_default(),
- requirements,
- );
+ requirements_mixed,
+ )?;
if let Some(section) = links.get_mut(require_key) {
for (k, v) in new_links {
section.insert(k, v);
@@ -719,20 +725,20 @@ impl RequireCommand {
section.shift_remove(package);
}
}
- root_package.set_requires(links.get("require").cloned().unwrap_or_default());
- root_package.set_dev_requires(links.get("require-dev").cloned().unwrap_or_default());
-
- // extract stability flags & references as they weren't present when loading the unmodified composer.json
- let mut references = root_package.get_references();
- references = RootPackageLoader::extract_references(requirements, references);
- root_package.set_references(references);
- let mut stability_flags = root_package.get_stability_flags();
- stability_flags = RootPackageLoader::extract_stability_flags(
+ // TODO(phase-b): root_package mutation requires &mut RootPackageInterface but
+ // Composer::get_package() exposes only & dyn; needs accessor returning &mut for
+ // the dry-run case to update requires/dev-requires/stability flags/references.
+ let _ = &links;
+ let _ = root_package.get_references().clone();
+ let _ = RootPackageLoader::extract_references(
+ requirements,
+ root_package.get_references().clone(),
+ );
+ let _ = RootPackageLoader::extract_stability_flags(
requirements,
root_package.get_minimum_stability(),
- stability_flags,
+ root_package.get_stability_flags().clone(),
);
- root_package.set_stability_flags(stability_flags);
// unset($stabilityFlags, $references);
}
@@ -828,16 +834,19 @@ impl RequireCommand {
let command_event = CommandEvent::new(PluginEvents::COMMAND, "require", input, output);
composer
.get_event_dispatcher()
+ .borrow_mut()
.dispatch(Some(command_event.get_name()), None);
composer
- .get_installation_manager()
+ .get_installation_manager_mut()
.set_output_progress(!input.get_option("no-progress").as_bool().unwrap_or(false));
- let install = Installer::create(io, &composer);
+ // TODO(phase-b): Installer::create takes Box<dyn IOInterface> for ownership but io is a
+ // borrowed &dyn here; needs Rc<dyn IOInterface> for proper sharing.
+ let mut install = Installer::create(todo!("share io as Box<dyn IOInterface>"), &composer);
let (prefer_source, prefer_dist) =
- self.get_preferred_install_options(&*composer.get_config().borrow(), input)?;
+ self.get_preferred_install_options(&*composer.get_config().borrow(), input, false)?;
install
.set_dry_run(input.get_option("dry-run").as_bool().unwrap_or(false))
@@ -847,10 +856,10 @@ impl RequireCommand {
.set_dev_mode(update_dev_mode)
.set_optimize_autoloader(optimize)
.set_class_map_authoritative(authoritative)
- .set_apcu_autoloader(apcu, apcu_prefix.as_deref())
+ .set_apcu_autoloader(apcu, apcu_prefix.clone())
.set_update(true)
.set_install(!input.get_option("no-install").as_bool().unwrap_or(false))
- .set_update_allow_transitive_dependencies(update_allow_transitive_dependencies)
+ .set_update_allow_transitive_dependencies(update_allow_transitive_dependencies)?
.set_platform_requirement_filter(BaseCommand::get_platform_requirement_filter(
self, input,
)?)
@@ -912,22 +921,43 @@ impl RequireCommand {
dry_run: bool,
fixed: bool,
) -> Result<i64> {
- let composer = self.require_composer(None, None)?;
- let locker = composer.get_locker();
+ let mut composer = self.require_composer(None, None)?;
+ let locker_is_locked = composer.get_locker_mut().is_locked();
let mut requirements: IndexMap<String, String> = IndexMap::new();
- let version_selector = VersionSelector::new(RepositorySet::new(None, None), None);
- let repo = if locker.is_locked() {
- composer.get_locker().get_locked_repository(Some(true))?
+ let mut version_selector = VersionSelector::new(
+ RepositorySet::new(
+ "stable",
+ IndexMap::new(),
+ vec![],
+ IndexMap::new(),
+ IndexMap::new(),
+ IndexMap::new(),
+ ),
+ None,
+ )?;
+ // TODO(phase-b): get_locked_repository returns LockArrayRepository (owned) and
+ // get_local_repository returns &dyn InstalledRepositoryInterface; need a common
+ // interface for find_package.
+ let locked_repo;
+ let repo: &dyn RepositoryInterface = if locker_is_locked {
+ locked_repo = composer.get_locker_mut().get_locked_repository(true)?;
+ &locked_repo
} else {
- composer.get_repository_manager().get_local_repository()
+ todo!("convert &dyn InstalledRepositoryInterface to &dyn RepositoryInterface")
};
for package_name in requirements_to_update {
- let mut package = repo.find_package(package_name, "*");
+ let mut package = repo.find_package(
+ package_name,
+ crate::repository::repository_interface::FindPackageConstraint::String(
+ "*".to_string(),
+ ),
+ );
// TODO(phase-b): `$package instanceof AliasPackage` downcast
- let mut package_as_alias: Option<&AliasPackage> = None;
- while let Some(alias) = package_as_alias {
- package = Some(Box::new(alias.get_alias_of().clone()) as Box<dyn PackageInterface>);
- package_as_alias = None;
+ let package_as_alias: Option<&AliasPackage> = None;
+ while let Some(_alias) = package_as_alias {
+ // TODO(phase-b): get_alias_of returns &dyn BasePackage; clone is not available
+ // and BasePackage is not PackageInterface (the latter is a super-trait).
+ package = todo!("upcast alias.get_alias_of() to Box<dyn BasePackage>");
}
let package = match package {
@@ -941,9 +971,13 @@ impl RequireCommand {
package.get_pretty_version().to_string(),
);
} else {
+ // TODO(phase-b): trait upcast from &dyn BasePackage to &dyn PackageInterface
+ // is not yet stable in Rust; use explicit as_package_interface() when available.
+ let pkg_as_pi: &dyn PackageInterface =
+ todo!("upcast &dyn BasePackage to &dyn PackageInterface");
requirements.insert(
package_name.clone(),
- version_selector.find_recommended_require_version(&*package),
+ version_selector.find_recommended_require_version(pkg_as_pi)?,
);
}
self.get_io().write_error3(
@@ -969,10 +1003,13 @@ impl RequireCommand {
)
.unwrap_or(false)
{
- self.get_io().warning(format!(
- "Version {} looks like it may be a feature branch which is unlikely to keep working in the long run and may be in an unstable state",
- requirements.get(package_name).cloned().unwrap_or_default(),
- ));
+ self.get_io().warning(
+ &format!(
+ "Version {} looks like it may be a feature branch which is unlikely to keep working in the long run and may be in an unstable state",
+ requirements.get(package_name).cloned().unwrap_or_default(),
+ ),
+ &[],
+ );
if self.get_io().is_interactive()
&& !self.get_io().ask_confirmation(
"Are you sure you want to use this constraint (<comment>y</comment>) or would you rather abort (<comment>n</comment>) the whole operation [<comment>y,n</comment>]? "
@@ -988,14 +1025,19 @@ impl RequireCommand {
}
if !dry_run {
- self.update_file(
- self.json.as_ref().unwrap(),
+ // TODO(phase-b): update_file takes &mut self while self.json is borrowed; needs
+ // refactor to pass the JsonFile owned/cloned or use interior mutability.
+ let json_path = self.json.as_ref().unwrap().get_path().to_string();
+ let _ = (
+ json_path,
&requirements,
require_key,
remove_key,
sort_packages,
);
- if locker.is_locked()
+ todo!("call self.update_file without overlapping borrows of self.json");
+ #[allow(unreachable_code)]
+ if locker_is_locked
&& composer
.get_config()
.borrow_mut()
@@ -1009,21 +1051,9 @@ impl RequireCommand {
IndexMap::new(),
);
let stability_flags_clone = stability_flags.clone();
- locker.update_hash(
- self.json.as_ref().unwrap(),
- Box::new(move |mut lock_data: IndexMap<String, PhpMixed>| {
- for (package_name, flag) in &stability_flags_clone {
- let entry = lock_data
- .entry("stability-flags".to_string())
- .or_insert_with(|| PhpMixed::Array(IndexMap::new()));
- if let PhpMixed::Array(m) = entry {
- m.insert(package_name.clone(), Box::new(PhpMixed::Int(*flag)));
- }
- }
-
- lock_data
- }),
- );
+ // TODO(phase-b): get_locker_mut needs update_hash with stability flags rewriter.
+ let _ = &stability_flags_clone;
+ todo!("update locker hash with stability flags rewriter");
}
}
@@ -1032,7 +1062,7 @@ impl RequireCommand {
/// @param array<string, string> $new
fn update_file(
- &self,
+ &mut self,
json: &JsonFile,
new: &IndexMap<String, String>,
require_key: &str,
@@ -1043,13 +1073,16 @@ impl RequireCommand {
return;
}
- let mut composer_definition = self.json.as_ref().unwrap().read().unwrap_or_default();
+ let composer_definition_mixed = self.json.as_mut().unwrap().read().unwrap_or_default();
+ let mut composer_definition: IndexMap<String, Box<PhpMixed>> = composer_definition_mixed
+ .as_array()
+ .cloned()
+ .unwrap_or_default();
for (package, version) in new {
- if let Some(section) = composer_definition
+ let section = composer_definition
.entry(require_key.to_string())
- .or_insert_with(|| PhpMixed::Array(IndexMap::new()))
- .as_array_mut()
- {
+ .or_insert_with(|| Box::new(PhpMixed::Array(IndexMap::new())));
+ if let Some(section) = section.as_array_mut() {
section.insert(package.clone(), Box::new(PhpMixed::String(version.clone())));
}
if let Some(section) = composer_definition
@@ -1067,12 +1100,11 @@ impl RequireCommand {
composer_definition.shift_remove(remove_key);
}
}
- let _ = self.json.as_ref().unwrap().write(PhpMixed::Array(
- composer_definition
- .into_iter()
- .map(|(k, v)| (k, Box::new(v)))
- .collect(),
- ));
+ let _ = self
+ .json
+ .as_ref()
+ .unwrap()
+ .write(PhpMixed::Array(composer_definition));
}
/// @param array<string, string> $new
@@ -1086,20 +1118,29 @@ impl RequireCommand {
) -> bool {
let contents = file_get_contents(json.get_path()).unwrap_or_default();
- let manipulator = JsonManipulator::new(&contents);
+ let mut manipulator = match JsonManipulator::new(contents) {
+ Ok(m) => m,
+ Err(_) => return false,
+ };
for (package, constraint) in new {
- if !manipulator.add_link(require_key, package, constraint, sort_packages) {
+ if !manipulator
+ .add_link(require_key, package, constraint, sort_packages)
+ .unwrap_or(false)
+ {
return false;
}
- if !manipulator.remove_sub_node(remove_key, package) {
+ if !manipulator
+ .remove_sub_node(remove_key, package)
+ .unwrap_or(false)
+ {
return false;
}
}
- manipulator.remove_main_key_if_empty(remove_key);
+ let _ = manipulator.remove_main_key_if_empty(remove_key);
- file_put_contents(json.get_path(), &manipulator.get_contents());
+ file_put_contents(json.get_path(), manipulator.get_contents().as_bytes());
true
}
@@ -1107,41 +1148,33 @@ impl RequireCommand {
pub(crate) fn interact(&self, _input: &dyn InputInterface, _output: &dyn OutputInterface) {}
fn revert_composer_file(&mut self) {
- let io = self.get_io();
-
if self.newly_created {
- io.write_error3(
- &format!(
- "\n<error>Installation failed, deleting {}.</error>",
- self.file
- ),
- true,
- io_interface::NORMAL,
+ let msg = format!(
+ "\n<error>Installation failed, deleting {}.</error>",
+ self.file
);
+ self.get_io().write_error3(&msg, true, io_interface::NORMAL);
unlink(self.json.as_ref().unwrap().get_path());
if file_exists(&self.lock) {
unlink(&self.lock);
}
} else {
- let msg = if self.lock_backup.is_some() {
+ let extra = if self.lock_backup.is_some() {
format!(" and {} to their ", self.lock)
} else {
" to its ".to_string()
};
- io.write_error3(
- &format!(
- "\n<error>Installation failed, reverting {}{}original content.</error>",
- self.file, msg
- ),
- true,
- io_interface::NORMAL,
+ let msg = format!(
+ "\n<error>Installation failed, reverting {}{}original content.</error>",
+ self.file, extra
);
+ self.get_io().write_error3(&msg, true, io_interface::NORMAL);
file_put_contents(
self.json.as_ref().unwrap().get_path(),
- &self.composer_backup,
+ self.composer_backup.as_bytes(),
);
if let Some(ref lock_backup) = self.lock_backup {
- file_put_contents(&self.lock, lock_backup);
+ file_put_contents(&self.lock, lock_backup.as_bytes());
}
}
}