aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/plugin
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/src/plugin')
-rw-r--r--crates/shirabe/src/plugin/php_plugin_proxy.rs144
1 files changed, 131 insertions, 13 deletions
diff --git a/crates/shirabe/src/plugin/php_plugin_proxy.rs b/crates/shirabe/src/plugin/php_plugin_proxy.rs
index 541718af..686c05bd 100644
--- a/crates/shirabe/src/plugin/php_plugin_proxy.rs
+++ b/crates/shirabe/src/plugin/php_plugin_proxy.rs
@@ -8,6 +8,7 @@
use crate::autoload::ClassLoader;
use crate::command::BaseCommand;
use crate::composer::ComposerHandle;
+use crate::dependency_resolver::operation::AnyOperation;
use crate::event_dispatcher::event_dispatcher::dispatch_event_method;
use crate::event_dispatcher::{
EventInterface, EventSubscriberInterface, SubscribedEventEntry, unwrap_php_result,
@@ -52,6 +53,7 @@ enum RustEntity {
EventDispatcher(
std::rc::Rc<std::cell::RefCell<dyn crate::event_dispatcher::EventDispatcherInterface>>,
),
+ Operation(std::rc::Rc<AnyOperation>),
}
/// The pointer identity backing R-table interning: the same shared instance must always cross
@@ -71,6 +73,7 @@ fn entity_ptr_id(entity: &RustEntity) -> usize {
RustEntity::EventDispatcher(dispatcher) => {
std::rc::Rc::as_ptr(dispatcher) as *const () as usize
}
+ RustEntity::Operation(operation) => std::rc::Rc::as_ptr(operation) as *const () as usize,
}
}
@@ -169,6 +172,21 @@ fn repository_stub_class(repository: &RepositoryInterfaceHandle) -> Result<&'sta
}
}
+/// The proxy stub class matching a solver operation's concrete type.
+fn operation_stub_class(operation: &AnyOperation) -> &'static str {
+ match operation {
+ AnyOperation::Install(_) => "Composer\\DependencyResolver\\Operation\\InstallOperation",
+ AnyOperation::Update(_) => "Composer\\DependencyResolver\\Operation\\UpdateOperation",
+ AnyOperation::Uninstall(_) => "Composer\\DependencyResolver\\Operation\\UninstallOperation",
+ AnyOperation::MarkAliasInstalled(_) => {
+ "Composer\\DependencyResolver\\Operation\\MarkAliasInstalledOperation"
+ }
+ AnyOperation::MarkAliasUninstalled(_) => {
+ "Composer\\DependencyResolver\\Operation\\MarkAliasUninstalledOperation"
+ }
+ }
+}
+
/// Registers a package and returns its wire descriptor.
pub(crate) fn package_handle_value(
package: &std::rc::Rc<std::cell::RefCell<AnyPackage>>,
@@ -178,6 +196,13 @@ pub(crate) fn package_handle_value(
rust_handle_value(rhandle, class)
}
+/// Registers a solver operation and returns its wire descriptor.
+pub(crate) fn operation_handle_value(operation: &std::rc::Rc<AnyOperation>) -> PluginValue {
+ let class = operation_stub_class(operation);
+ let rhandle = register_entity(RustEntity::Operation(operation.clone()));
+ rust_handle_value(rhandle, class)
+}
+
/// The PHP class name (= proxy stub class) of a Rust IO instance, for the `__class` field of
/// its handle descriptor.
pub(crate) fn io_stub_class(
@@ -335,6 +360,9 @@ impl RustMethodDispatcher for PluginRpcDispatcher<'_> {
Some(RustEntity::EventDispatcher(dispatcher)) => {
dispatch_event_dispatcher_method(&dispatcher, method_name, &args)
}
+ Some(RustEntity::Operation(operation)) => {
+ dispatch_operation_method(&operation, method_name, &args)
+ }
None => Err(runtime_throw(format!("unknown Rust handle {rhandle}"))),
}
}
@@ -368,7 +396,13 @@ pub(crate) fn construct_entity(args: &[PluginValue]) -> Result<PluginValue, PhpT
))),
}
};
- let entity = match class.as_str() {
+ let alias_package_arg =
+ |position: usize| -> Result<crate::package::AliasPackageHandle, PhpThrow> {
+ package_from_arg(&class, ctor_args.get(position))?
+ .as_alias()
+ .ok_or_else(|| runtime_throw(format!("{class} expects an AliasPackage")))
+ };
+ let package = match class.as_str() {
"Composer\\Package\\Package" => AnyPackage::Package(crate::package::Package::new(
string_arg(0)?,
string_arg(1)?,
@@ -413,6 +447,50 @@ pub(crate) fn construct_entity(args: &[PluginValue]) -> Result<PluginValue, PhpT
string_arg(2)?,
))
}
+ // A solver operation carries no state beyond the packages it names, so a plugin-built
+ // one is a complete instance rather than a second view on a Rust-side service.
+ "Composer\\DependencyResolver\\Operation\\InstallOperation" => {
+ return Ok(operation_construction_result(AnyOperation::Install(
+ crate::dependency_resolver::operation::InstallOperation::new(package_from_arg(
+ &class,
+ ctor_args.first(),
+ )?),
+ )));
+ }
+ "Composer\\DependencyResolver\\Operation\\UpdateOperation" => {
+ return Ok(operation_construction_result(AnyOperation::Update(
+ crate::dependency_resolver::operation::UpdateOperation::new(
+ package_from_arg(&class, ctor_args.first())?,
+ package_from_arg(&class, ctor_args.get(1))?,
+ ),
+ )));
+ }
+ "Composer\\DependencyResolver\\Operation\\UninstallOperation" => {
+ return Ok(operation_construction_result(AnyOperation::Uninstall(
+ crate::dependency_resolver::operation::UninstallOperation::new(package_from_arg(
+ &class,
+ ctor_args.first(),
+ )?),
+ )));
+ }
+ "Composer\\DependencyResolver\\Operation\\MarkAliasInstalledOperation" => {
+ return Ok(operation_construction_result(
+ AnyOperation::MarkAliasInstalled(
+ crate::dependency_resolver::operation::MarkAliasInstalledOperation::new(
+ alias_package_arg(0)?,
+ ),
+ ),
+ ));
+ }
+ "Composer\\DependencyResolver\\Operation\\MarkAliasUninstalledOperation" => {
+ return Ok(operation_construction_result(
+ AnyOperation::MarkAliasUninstalled(
+ crate::dependency_resolver::operation::MarkAliasUninstalledOperation::new(
+ alias_package_arg(0)?,
+ ),
+ ),
+ ));
+ }
// TODO(plugin): the remaining proxied classes get a construction story on demand,
// driven by explicit errors from real plugins. Each one has to decide what a
// plugin-built instance means for the Rust-side graph, which is why none of them is
@@ -424,12 +502,20 @@ pub(crate) fn construct_entity(args: &[PluginValue]) -> Result<PluginValue, PhpT
}
};
let rhandle = register_entity(RustEntity::Package(std::rc::Rc::new(
- std::cell::RefCell::new(entity),
+ std::cell::RefCell::new(package),
)));
- Ok(PluginValue::List(vec![
- PluginValue::Int(rhandle as i64),
- PluginValue::Int(0),
- ]))
+ Ok(construction_result(rhandle))
+}
+
+fn operation_construction_result(operation: AnyOperation) -> PluginValue {
+ construction_result(register_entity(RustEntity::Operation(std::rc::Rc::new(
+ operation,
+ ))))
+}
+
+/// The `[$rhandle, $epoch]` pair a proxy stub's constructor binds itself to.
+fn construction_result(rhandle: u64) -> PluginValue {
+ PluginValue::List(vec![PluginValue::Int(rhandle as i64), PluginValue::Int(0)])
}
/// Serves the `__clone` forwarder every proxy stub carries. Only entities whose Rust type
@@ -452,7 +538,8 @@ fn clone_entity(entity: &RustEntity) -> Result<PluginValue, PhpThrow> {
| RustEntity::InstallationManager(_)
| RustEntity::RepositoryManager(_)
| RustEntity::Repository(_)
- | RustEntity::EventDispatcher(_) => {
+ | RustEntity::EventDispatcher(_)
+ | RustEntity::Operation(_) => {
return Err(runtime_throw(
"cloning this Rust-side entity over RPC is not supported".to_string(),
));
@@ -810,12 +897,7 @@ fn dispatch_repository_manager_method(
method_name: &str,
) -> Result<PluginValue, PhpThrow> {
match method_name {
- "getLocalRepository" => {
- let local = rm.borrow().get_local_repository();
- let class = repository_stub_class(&local)?;
- let rhandle = register_entity(RustEntity::Repository(local));
- Ok(rust_handle_value(rhandle, class))
- }
+ "getLocalRepository" => repository_handle_value(&rm.borrow().get_local_repository()),
// TODO(plugin): the remaining RepositoryManager surface is widened on demand, driven
// by explicit errors from real plugins.
other => Err(runtime_throw(format!(
@@ -1761,6 +1843,42 @@ fn dispatch_package_method(
}
}
+fn dispatch_operation_method(
+ operation: &AnyOperation,
+ method_name: &str,
+ args: &[PluginValue],
+) -> Result<PluginValue, PhpThrow> {
+ match (method_name, operation) {
+ ("getOperationType", _) => Ok(PluginValue::string(operation.get_operation_type())),
+ ("show", _) => Ok(PluginValue::string(
+ operation.show(bool_arg(method_name, args.first())?),
+ )),
+ ("__toString", _) => Ok(PluginValue::string(operation.to_string())),
+ ("getPackage", AnyOperation::Install(op)) => {
+ Ok(package_handle_value(op.get_package().as_rc()))
+ }
+ ("getPackage", AnyOperation::Uninstall(op)) => {
+ Ok(package_handle_value(op.get_package().as_rc()))
+ }
+ ("getPackage", AnyOperation::MarkAliasInstalled(op)) => {
+ Ok(package_handle_value(op.get_package().as_rc()))
+ }
+ ("getPackage", AnyOperation::MarkAliasUninstalled(op)) => {
+ Ok(package_handle_value(op.get_package().as_rc()))
+ }
+ ("getInitialPackage", AnyOperation::Update(op)) => {
+ Ok(package_handle_value(op.get_initial_package().as_rc()))
+ }
+ ("getTargetPackage", AnyOperation::Update(op)) => {
+ Ok(package_handle_value(op.get_target_package().as_rc()))
+ }
+ (other, _) => Err(runtime_throw(format!(
+ "the operation method `{other}` is not available on a {} over RPC yet",
+ operation_stub_class(operation)
+ ))),
+ }
+}
+
fn dispatch_installation_manager_method(
im: &std::rc::Rc<std::cell::RefCell<dyn InstallationManagerInterface>>,
method_name: &str,