diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-07-25 12:16:25 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-07-25 12:16:25 +0900 |
| commit | 42b5f9e321c918cef542c120ad21ba8a7339eb29 (patch) | |
| tree | 39f2b5a3cb856de9068f72fb670d170c09a2438f /crates/shirabe/src/dependency_resolver/operation | |
| parent | f93d9b49382c8f79fcea4f03361d50bda534bcc4 (diff) | |
| download | php-shirabe-42b5f9e321c918cef542c120ad21ba8a7339eb29.tar.gz php-shirabe-42b5f9e321c918cef542c120ad21ba8a7339eb29.tar.zst php-shirabe-42b5f9e321c918cef542c120ad21ba8a7339eb29.zip | |
refactor(operation): replace OperationInterface with AnyOperation enum
Operations are only ever constructed by the dependency resolver, so a
plugin has no way to inject an implementation of its own and the set is
closed. Modelling it as an enum, like AnyPackage, removes the
OperationInterface trait together with its two parallel downcast
mechanisms (as_any() + downcast_ref, and as_*_operation()) and the
get_package() default method that panicked on UpdateOperation.
The PHP idiom `$op instanceof UpdateOperation ? getTargetPackage() :
getPackage()`, written out at six call sites, becomes
AnyOperation::get_target_package(). InstallationManager's three blocks
that matched on the type string and then recovered the type with
expect() collapse into exhaustive matches.
SolverOperation keeps only its TYPE constant; the shared
getOperationType()/__toString() implementations move to AnyOperation,
which also drops the five Self::TYPE.to_string() allocations.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/dependency_resolver/operation')
8 files changed, 126 insertions, 150 deletions
diff --git a/crates/shirabe/src/dependency_resolver/operation/any_operation.rs b/crates/shirabe/src/dependency_resolver/operation/any_operation.rs new file mode 100644 index 00000000..6675b280 --- /dev/null +++ b/crates/shirabe/src/dependency_resolver/operation/any_operation.rs @@ -0,0 +1,92 @@ +//! ref: composer/src/Composer/DependencyResolver/Operation/OperationInterface.php +//! +//! PHP's `OperationInterface` is not ported as a trait. Operations are only ever constructed by the +//! dependency resolver, so a plugin has no way to inject an implementation of its own and the set is +//! closed. The shared implementations PHP puts on `SolverOperation` (`getOperationType()`, +//! `__toString()`) live here. + +use crate::dependency_resolver::operation::{ + InstallOperation, MarkAliasInstalledOperation, MarkAliasUninstalledOperation, SolverOperation, + UninstallOperation, UpdateOperation, +}; +use crate::package::PackageInterfaceHandle; + +/// Any solver operation. +#[derive(Debug, Clone)] +pub enum AnyOperation { + Install(InstallOperation), + Update(UpdateOperation), + Uninstall(UninstallOperation), + MarkAliasInstalled(MarkAliasInstalledOperation), + MarkAliasUninstalled(MarkAliasUninstalledOperation), +} + +impl AnyOperation { + pub fn get_operation_type(&self) -> &'static str { + match self { + Self::Install(_) => InstallOperation::TYPE, + Self::Update(_) => UpdateOperation::TYPE, + Self::Uninstall(_) => UninstallOperation::TYPE, + Self::MarkAliasInstalled(_) => MarkAliasInstalledOperation::TYPE, + Self::MarkAliasUninstalled(_) => MarkAliasUninstalledOperation::TYPE, + } + } + + pub fn show(&self, lock: bool) -> String { + match self { + Self::Install(op) => op.show(lock), + Self::Update(op) => op.show(lock), + Self::Uninstall(op) => op.show(lock), + Self::MarkAliasInstalled(op) => op.show(lock), + Self::MarkAliasUninstalled(op) => op.show(lock), + } + } + + /// The package the operation results in. PHP spells this out at every call site as + /// `$op instanceof UpdateOperation ? $op->getTargetPackage() : $op->getPackage()`. + pub fn get_target_package(&self) -> PackageInterfaceHandle { + match self { + Self::Install(op) => op.get_package(), + Self::Update(op) => op.get_target_package(), + Self::Uninstall(op) => op.get_package(), + Self::MarkAliasInstalled(op) => op.get_package().into(), + Self::MarkAliasUninstalled(op) => op.get_package().into(), + } + } +} + +impl std::fmt::Display for AnyOperation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.show(false)) + } +} + +impl From<InstallOperation> for AnyOperation { + fn from(op: InstallOperation) -> Self { + Self::Install(op) + } +} + +impl From<UpdateOperation> for AnyOperation { + fn from(op: UpdateOperation) -> Self { + Self::Update(op) + } +} + +impl From<UninstallOperation> for AnyOperation { + fn from(op: UninstallOperation) -> Self { + Self::Uninstall(op) + } +} + +impl From<MarkAliasInstalledOperation> for AnyOperation { + fn from(op: MarkAliasInstalledOperation) -> Self { + Self::MarkAliasInstalled(op) + } +} + +impl From<MarkAliasUninstalledOperation> for AnyOperation { + fn from(op: MarkAliasUninstalledOperation) -> Self { + Self::MarkAliasUninstalled(op) + } +} diff --git a/crates/shirabe/src/dependency_resolver/operation/install_operation.rs b/crates/shirabe/src/dependency_resolver/operation/install_operation.rs index ef492393..05349c2d 100644 --- a/crates/shirabe/src/dependency_resolver/operation/install_operation.rs +++ b/crates/shirabe/src/dependency_resolver/operation/install_operation.rs @@ -1,10 +1,9 @@ //! ref: composer/src/Composer/DependencyResolver/Operation/InstallOperation.php -use crate::dependency_resolver::operation::OperationInterface; use crate::dependency_resolver::operation::SolverOperation; use crate::package::PackageInterfaceHandle; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct InstallOperation { pub(crate) package: PackageInterfaceHandle, } @@ -18,6 +17,10 @@ impl InstallOperation { self.package.clone() } + pub fn show(&self, lock: bool) -> String { + Self::format(self.package.clone(), lock) + } + pub fn format(package: PackageInterfaceHandle, lock: bool) -> String { format!( "{}<info>{}</info> (<comment>{}</comment>)", @@ -32,28 +35,6 @@ impl SolverOperation for InstallOperation { const TYPE: &'static str = "install"; } -impl OperationInterface for InstallOperation { - fn as_any(&self) -> &dyn std::any::Any { - self - } - - fn get_operation_type(&self) -> String { - Self::TYPE.to_string() - } - - fn show(&self, lock: bool) -> String { - Self::format(self.package.clone(), lock) - } - - fn as_install_operation(&self) -> Option<&InstallOperation> { - Some(self) - } - - fn get_package(&self) -> PackageInterfaceHandle { - self.package.clone() - } -} - impl std::fmt::Display for InstallOperation { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.show(false)) diff --git a/crates/shirabe/src/dependency_resolver/operation/mark_alias_installed_operation.rs b/crates/shirabe/src/dependency_resolver/operation/mark_alias_installed_operation.rs index 28bf511a..72066204 100644 --- a/crates/shirabe/src/dependency_resolver/operation/mark_alias_installed_operation.rs +++ b/crates/shirabe/src/dependency_resolver/operation/mark_alias_installed_operation.rs @@ -1,10 +1,9 @@ //! ref: composer/src/Composer/DependencyResolver/Operation/MarkAliasInstalledOperation.php -use crate::dependency_resolver::operation::OperationInterface; use crate::dependency_resolver::operation::SolverOperation; use crate::package::AliasPackageHandle; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct MarkAliasInstalledOperation { pub(crate) package: AliasPackageHandle, } @@ -17,22 +16,8 @@ impl MarkAliasInstalledOperation { pub fn get_package(&self) -> AliasPackageHandle { self.package.clone() } -} - -impl SolverOperation for MarkAliasInstalledOperation { - const TYPE: &'static str = "markAliasInstalled"; -} - -impl OperationInterface for MarkAliasInstalledOperation { - fn as_any(&self) -> &dyn std::any::Any { - self - } - fn get_operation_type(&self) -> String { - Self::TYPE.to_string() - } - - fn show(&self, _lock: bool) -> String { + pub fn show(&self, _lock: bool) -> String { format!( "Marking <info>{}</info> (<comment>{}</comment>) as installed, alias of <info>{}</info> (<comment>{}</comment>)", self.package.get_pretty_name(), @@ -44,10 +29,10 @@ impl OperationInterface for MarkAliasInstalledOperation { .get_full_pretty_version(true, crate::package::DisplayMode::SourceRefIfDev), ) } +} - fn get_package(&self) -> crate::package::PackageInterfaceHandle { - self.package.clone().into() - } +impl SolverOperation for MarkAliasInstalledOperation { + const TYPE: &'static str = "markAliasInstalled"; } impl std::fmt::Display for MarkAliasInstalledOperation { diff --git a/crates/shirabe/src/dependency_resolver/operation/mark_alias_uninstalled_operation.rs b/crates/shirabe/src/dependency_resolver/operation/mark_alias_uninstalled_operation.rs index 3c0de332..4e4000df 100644 --- a/crates/shirabe/src/dependency_resolver/operation/mark_alias_uninstalled_operation.rs +++ b/crates/shirabe/src/dependency_resolver/operation/mark_alias_uninstalled_operation.rs @@ -1,10 +1,9 @@ //! ref: composer/src/Composer/DependencyResolver/Operation/MarkAliasUninstalledOperation.php -use crate::dependency_resolver::operation::OperationInterface; use crate::dependency_resolver::operation::SolverOperation; use crate::package::AliasPackageHandle; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct MarkAliasUninstalledOperation { pub(crate) package: AliasPackageHandle, } @@ -17,22 +16,8 @@ impl MarkAliasUninstalledOperation { pub fn get_package(&self) -> AliasPackageHandle { self.package.clone() } -} - -impl SolverOperation for MarkAliasUninstalledOperation { - const TYPE: &'static str = "markAliasUninstalled"; -} - -impl OperationInterface for MarkAliasUninstalledOperation { - fn as_any(&self) -> &dyn std::any::Any { - self - } - fn get_operation_type(&self) -> String { - Self::TYPE.to_string() - } - - fn show(&self, _lock: bool) -> String { + pub fn show(&self, _lock: bool) -> String { format!( "Marking <info>{}</info> (<comment>{}</comment>) as uninstalled, alias of <info>{}</info> (<comment>{}</comment>)", self.package.get_pretty_name(), @@ -44,10 +29,10 @@ impl OperationInterface for MarkAliasUninstalledOperation { .get_full_pretty_version(true, crate::package::DisplayMode::SourceRefIfDev), ) } +} - fn get_package(&self) -> crate::package::PackageInterfaceHandle { - self.package.clone().into() - } +impl SolverOperation for MarkAliasUninstalledOperation { + const TYPE: &'static str = "markAliasUninstalled"; } impl std::fmt::Display for MarkAliasUninstalledOperation { diff --git a/crates/shirabe/src/dependency_resolver/operation/operation_interface.rs b/crates/shirabe/src/dependency_resolver/operation/operation_interface.rs deleted file mode 100644 index 30ed8eb5..00000000 --- a/crates/shirabe/src/dependency_resolver/operation/operation_interface.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! ref: composer/src/Composer/DependencyResolver/Operation/OperationInterface.php - -use crate::dependency_resolver::operation::InstallOperation; -use crate::dependency_resolver::operation::UninstallOperation; -use crate::dependency_resolver::operation::UpdateOperation; - -pub trait OperationInterface: std::fmt::Display + std::fmt::Debug { - fn as_any(&self) -> &dyn std::any::Any; - - fn get_operation_type(&self) -> String; - - fn show(&self, lock: bool) -> String; - - fn as_install_operation(&self) -> Option<&InstallOperation> { - None - } - - fn as_update_operation(&self) -> Option<&UpdateOperation> { - None - } - - fn as_uninstall_operation(&self) -> Option<&UninstallOperation> { - None - } - - /// PHP duck-typed accessor. Only InstallOperation/UninstallOperation/MarkAlias*Operation - /// expose this; UpdateOperation has getInitialPackage()/getTargetPackage() instead. - fn get_package(&self) -> crate::package::PackageInterfaceHandle { - todo!("get_package is not available on this operation type") - } -} diff --git a/crates/shirabe/src/dependency_resolver/operation/solver_operation.rs b/crates/shirabe/src/dependency_resolver/operation/solver_operation.rs index 2710ec50..eb9c6315 100644 --- a/crates/shirabe/src/dependency_resolver/operation/solver_operation.rs +++ b/crates/shirabe/src/dependency_resolver/operation/solver_operation.rs @@ -1,11 +1,9 @@ //! ref: composer/src/Composer/DependencyResolver/Operation/SolverOperation.php -use crate::dependency_resolver::operation::OperationInterface; - -pub trait SolverOperation: OperationInterface { +/// PHP's abstract `SolverOperation` carries the per-class `TYPE` constant and the +/// `getOperationType()` / `__toString()` implementations shared by every operation. Only the +/// constant remains here; the shared implementations live on +/// [`AnyOperation`](crate::dependency_resolver::operation::AnyOperation). +pub trait SolverOperation { const TYPE: &'static str; - - fn get_operation_type(&self) -> &str { - Self::TYPE - } } diff --git a/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs b/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs index d2dd6651..2fd35af5 100644 --- a/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs +++ b/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs @@ -1,10 +1,9 @@ //! ref: composer/src/Composer/DependencyResolver/Operation/UninstallOperation.php -use crate::dependency_resolver::operation::OperationInterface; use crate::dependency_resolver::operation::SolverOperation; use crate::package::PackageInterfaceHandle; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct UninstallOperation { pub(crate) package: PackageInterfaceHandle, } @@ -18,6 +17,10 @@ impl UninstallOperation { self.package.clone() } + pub fn show(&self, lock: bool) -> String { + Self::format(self.package.clone(), lock) + } + pub fn format(package: PackageInterfaceHandle, _lock: bool) -> String { format!( "Removing <info>{}</info> (<comment>{}</comment>)", @@ -31,28 +34,6 @@ impl SolverOperation for UninstallOperation { const TYPE: &'static str = "uninstall"; } -impl OperationInterface for UninstallOperation { - fn as_any(&self) -> &dyn std::any::Any { - self - } - - fn get_operation_type(&self) -> String { - Self::TYPE.to_string() - } - - fn show(&self, lock: bool) -> String { - Self::format(self.package.clone(), lock) - } - - fn as_uninstall_operation(&self) -> Option<&UninstallOperation> { - Some(self) - } - - fn get_package(&self) -> PackageInterfaceHandle { - self.package.clone() - } -} - impl std::fmt::Display for UninstallOperation { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.show(false)) diff --git a/crates/shirabe/src/dependency_resolver/operation/update_operation.rs b/crates/shirabe/src/dependency_resolver/operation/update_operation.rs index 76b17189..a06a9cdc 100644 --- a/crates/shirabe/src/dependency_resolver/operation/update_operation.rs +++ b/crates/shirabe/src/dependency_resolver/operation/update_operation.rs @@ -1,11 +1,10 @@ //! ref: composer/src/Composer/DependencyResolver/Operation/UpdateOperation.php -use crate::dependency_resolver::operation::OperationInterface; use crate::dependency_resolver::operation::SolverOperation; use crate::package::PackageInterfaceHandle; use crate::package::version::VersionParser; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct UpdateOperation { pub(crate) initial_package: PackageInterfaceHandle, pub(crate) target_package: PackageInterfaceHandle, @@ -27,6 +26,14 @@ impl UpdateOperation { self.target_package.clone() } + pub fn show(&self, lock: bool) -> String { + Self::format( + self.initial_package.clone(), + self.target_package.clone(), + lock, + ) + } + pub fn format( initial_package: PackageInterfaceHandle, target_package: PackageInterfaceHandle, @@ -78,28 +85,6 @@ impl SolverOperation for UpdateOperation { const TYPE: &'static str = "update"; } -impl OperationInterface for UpdateOperation { - fn as_any(&self) -> &dyn std::any::Any { - self - } - - fn get_operation_type(&self) -> String { - Self::TYPE.to_string() - } - - fn show(&self, lock: bool) -> String { - Self::format( - self.initial_package.clone(), - self.target_package.clone(), - lock, - ) - } - - fn as_update_operation(&self) -> Option<&UpdateOperation> { - Some(self) - } -} - impl std::fmt::Display for UpdateOperation { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.show(false)) |
