//! ref: composer/src/Composer/Command/SuggestsCommand.php use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; use crate::composer::Composer; use crate::console::input::InputArgument; use crate::console::input::InputOption; use crate::installer::SuggestedPackagesReporter; use crate::io::IOInterface; use crate::repository::InstalledRepository; use crate::repository::PlatformRepository; use crate::repository::RepositoryInterface; use crate::repository::RootPackageRepository; use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::symfony::component::console::input::InputInterface; use shirabe_external_packages::symfony::component::console::output::OutputInterface; use shirabe_php_shim::{PhpMixed, empty, in_array}; #[derive(Debug)] pub struct SuggestsCommand { base_command_data: BaseCommandData, } impl SuggestsCommand { pub fn configure(&mut self) { // TODO(cli-completion): suggest_installed_package() for `packages` argument self .set_name("suggests") .set_description("Shows package suggestions") .set_definition(&[ InputOption::new("by-package", None, Some(InputOption::VALUE_NONE), "Groups output by suggesting package (default)", None).unwrap().into(), InputOption::new("by-suggestion", None, Some(InputOption::VALUE_NONE), "Groups output by suggested package", None).unwrap().into(), InputOption::new("all", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Show suggestions from all dependencies, including transitive ones", None).unwrap().into(), InputOption::new("list", None, Some(InputOption::VALUE_NONE), "Show only list of suggested package names", None).unwrap().into(), InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Exclude suggestions from require-dev packages", None).unwrap().into(), InputArgument::new("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Packages that you want to list suggestions from.", None).unwrap().into(), ]) .set_help( "\nThe %command.name% command shows a sorted list of suggested packages.\n\nRead more at https://getcomposer.org/doc/03-cli.md#suggests", ); } pub fn execute( &mut self, input: &dyn InputInterface, _output: &dyn OutputInterface, ) -> Result { let mut composer = self.require_composer(None, None)?; let mut installed_repos: Vec> = vec![Box::new( RootPackageRepository::new(composer.get_package().clone_box()), )]; if composer.get_locker_mut().is_locked() { // TODO(phase-b): get_platform_overrides returns IndexMap; PlatformRepository::new expects IndexMap let _platform_overrides = composer.get_locker_mut().get_platform_overrides()?; let platform_overrides: IndexMap = todo!("convert IndexMap to IndexMap"); installed_repos.push(Box::new(PlatformRepository::new( vec![], platform_overrides, )?)); let locked_repo = composer .get_locker_mut() .get_locked_repository(!input.get_option("no-dev").as_bool().unwrap_or(false))?; installed_repos.push(Box::new(locked_repo)); } else { // TODO(phase-b): Config::get returns PhpMixed; need to coerce to IndexMap let _platform_cfg = composer.get_config().borrow().get("platform"); let platform_overrides: IndexMap = todo!("extract IndexMap from PhpMixed config value"); installed_repos.push(Box::new(PlatformRepository::new( vec![], platform_overrides, )?)); installed_repos.push( composer .get_repository_manager() .get_local_repository() .clone_box(), ); } let installed_repo = InstalledRepository::new(installed_repos); // TODO(phase-b): SuggestedPackagesReporter::new expects Box; self.get_io() returns &mut dyn IOInterface let io_box: Box = todo!("share IOInterface as Box"); let mut reporter = SuggestedPackagesReporter::new(io_box); let filter = input.get_argument("packages"); let mut packages = RepositoryInterface::get_packages(&installed_repo); // TODO(phase-b): composer.get_package() returns &dyn RootPackageInterface; pushing into Vec> requires conversion let root_pkg_as_base: Box = todo!("convert RootPackageInterface to Box"); packages.push(root_pkg_as_base); for package in &packages { if !empty(&filter) && !in_array( PhpMixed::String(package.get_name().to_string()), &filter, false, ) { continue; } // TODO(phase-b): add_suggestions_from_package expects &dyn PackageInterface; BasePackage is a separate trait reporter.add_suggestions_from_package(todo!( "convert Box to &dyn PackageInterface" )); } let mut mode = SuggestedPackagesReporter::MODE_BY_PACKAGE; if input.get_option("by-suggestion").as_bool().unwrap_or(false) { mode = SuggestedPackagesReporter::MODE_BY_SUGGESTION; } if input.get_option("by-package").as_bool().unwrap_or(false) { mode |= SuggestedPackagesReporter::MODE_BY_PACKAGE; } if input.get_option("list").as_bool().unwrap_or(false) { mode = SuggestedPackagesReporter::MODE_LIST; } let only_dependents_of: Option<&dyn crate::package::PackageInterface> = if empty(&filter) && !input.get_option("all").as_bool().unwrap_or(false) { // TODO(phase-b): composer.get_package() returns &dyn RootPackageInterface; need conversion to &dyn PackageInterface Some(todo!( "convert RootPackageInterface to &dyn PackageInterface" )) } else { None }; reporter.output(mode, Some(&installed_repo), only_dependents_of); Ok(0) } } impl HasBaseCommandData for SuggestsCommand { fn base_command_data(&self) -> &BaseCommandData { &self.base_command_data } fn base_command_data_mut(&mut self) -> &mut BaseCommandData { &mut self.base_command_data } }