diff options
Diffstat (limited to 'crates/shirabe/src/plugin/php_plugin_proxy.rs')
| -rw-r--r-- | crates/shirabe/src/plugin/php_plugin_proxy.rs | 80 |
1 files changed, 78 insertions, 2 deletions
diff --git a/crates/shirabe/src/plugin/php_plugin_proxy.rs b/crates/shirabe/src/plugin/php_plugin_proxy.rs index 715a21dd..8e24dbfe 100644 --- a/crates/shirabe/src/plugin/php_plugin_proxy.rs +++ b/crates/shirabe/src/plugin/php_plugin_proxy.rs @@ -54,6 +54,7 @@ enum RustEntity { std::rc::Rc<std::cell::RefCell<dyn crate::event_dispatcher::EventDispatcherInterface>>, ), Operation(std::rc::Rc<AnyOperation>), + Plugin(std::rc::Rc<std::cell::RefCell<dyn PluginInterface>>), } /// The pointer identity backing R-table interning: the same shared instance must always cross @@ -74,6 +75,7 @@ fn entity_ptr_id(entity: &RustEntity) -> usize { std::rc::Rc::as_ptr(dispatcher) as *const () as usize } RustEntity::Operation(operation) => std::rc::Rc::as_ptr(operation) as *const () as usize, + RustEntity::Plugin(plugin) => std::rc::Rc::as_ptr(plugin) as *const () as usize, } } @@ -196,6 +198,22 @@ pub(crate) fn package_handle_value( rust_handle_value(rhandle, class) } +/// Registers a Rust-implemented plugin and returns its wire descriptor. A PHP-implemented +/// plugin never takes this route: its entity lives in the child's P table already. +pub fn plugin_handle_value( + plugin: &std::rc::Rc<std::cell::RefCell<dyn PluginInterface>>, +) -> PluginValue { + // TODO(plugin): a Rust-implemented plugin that is also an EventSubscriberInterface crosses + // as one of these two classes, so `instanceof EventSubscriberInterface` is false in the + // child; Composer's own subscriber dispatch runs on the Rust side and never asks. + let class = match plugin.borrow().as_capable() { + Some(_) => "Shirabe\\RustCapablePluginStub", + None => "Shirabe\\RustPluginStub", + }; + let rhandle = register_entity(RustEntity::Plugin(plugin.clone())); + 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); @@ -363,6 +381,7 @@ impl RustMethodDispatcher for PluginRpcDispatcher<'_> { Some(RustEntity::Operation(operation)) => { dispatch_operation_method(&operation, method_name, &args) } + Some(RustEntity::Plugin(plugin)) => dispatch_plugin_method(&plugin, method_name), None => Err(runtime_throw(format!("unknown Rust handle {rhandle}"))), } } @@ -539,7 +558,8 @@ fn clone_entity(entity: &RustEntity) -> Result<PluginValue, PhpThrow> { | RustEntity::RepositoryManager(_) | RustEntity::Repository(_) | RustEntity::EventDispatcher(_) - | RustEntity::Operation(_) => { + | RustEntity::Operation(_) + | RustEntity::Plugin(_) => { return Err(runtime_throw( "cloning this Rust-side entity over RPC is not supported".to_string(), )); @@ -592,6 +612,33 @@ fn dispatch_property_access( ))) } +fn dispatch_plugin_method( + plugin: &std::rc::Rc<std::cell::RefCell<dyn PluginInterface>>, + method_name: &str, +) -> Result<PluginValue, PhpThrow> { + match method_name { + "getCapabilities" => { + let plugin = plugin.borrow(); + let capable = plugin.as_capable().ok_or_else(|| { + runtime_throw(format!( + "plugin {} does not implement Capable", + plugin.get_class_name() + )) + })?; + let capabilities = capable + .get_capabilities() + .map_err(|error| runtime_throw(format!("getCapabilities failed: {error:#}")))?; + Ok(PluginValue::from_php_mixed(&PhpMixed::Array(capabilities))) + } + // TODO(plugin): the lifecycle methods would have to turn the `$composer`/`$io` stubs the + // child passes back into the Rust handles they stand for; nothing calls them, because + // Composer activates a Rust-implemented plugin on the Rust side. + other => Err(runtime_throw(format!( + "the plugin method `{other}` is not available over RPC yet" + ))), + } +} + fn dispatch_composer_method( composer: &ComposerHandle, method_name: &str, @@ -2582,9 +2629,38 @@ impl PhpCapabilityProxy { pub(crate) fn new(handle: PhpObjHandle) -> Self { Self { handle } } + + /// For testing only: the entity descriptor, whose class and interface list answer the + /// `assertInstanceOf` checks PHPUnit makes on a capability. + pub fn __handle(&self) -> &PhpObjHandle { + &self.handle + } + + /// For testing only: reads a public property of the capability entity in the child. Unlike + /// `PhpPluginProxy::__get_property` the value keeps its wire form, so a test can assert the + /// object identity behind a handle instead of only the plain data around it. + pub fn __get_property(&self, name: &str) -> anyhow::Result<PluginValue> { + let outcome = shirabe_php_rpc::call_function( + "__shirabe_get_property", + vec![ + PluginValue::PhpHandle(self.handle.clone()), + PluginValue::string(name), + ], + )?; + match outcome { + Ok(value) => Ok(value), + Err(throw) => { + Err(shirabe_php_shim::RuntimeException::with_code(throw.message, throw.code).into()) + } + } + } } -impl Capability for PhpCapabilityProxy {} +impl Capability for PhpCapabilityProxy { + fn __as_php_capability_proxy(&self) -> Option<&PhpCapabilityProxy> { + Some(self) + } +} impl Drop for PhpCapabilityProxy { fn drop(&mut self) { |
