folk_api/executor.rs
1//! The `Executor` trait — how plugins send work to PHP workers.
2//!
3//! `folk-core` provides the concrete implementation, backed by the worker pool.
4
5use std::sync::Arc;
6
7use anyhow::Result;
8use async_trait::async_trait;
9use bytes::Bytes;
10
11/// Sends a serialized payload to a PHP worker and returns the response.
12///
13/// Plugins call this; they never see the worker pool directly.
14#[async_trait]
15pub trait Executor: Send + Sync + 'static {
16 /// Send `payload` to a worker and return the response bytes.
17 /// Uses the RPC method name "dispatch" by default.
18 async fn execute(&self, payload: Bytes) -> Result<Bytes> {
19 self.execute_method("dispatch", payload).await
20 }
21
22 /// Send `payload` to a worker using a specific RPC method name.
23 async fn execute_method(&self, method: &str, payload: Bytes) -> Result<Bytes>;
24
25 /// Send structured data directly (no serialization).
26 ///
27 /// The default implementation falls back to JSON serialization
28 /// via [`execute_method`]. Extension runtimes override this to
29 /// pass values through the channel without JSON encode/decode.
30 async fn execute_value(
31 &self,
32 method: &str,
33 payload: serde_json::Value,
34 ) -> Result<serde_json::Value> {
35 let bytes = serde_json::to_vec(&payload)?;
36 let result = self.execute_method(method, Bytes::from(bytes)).await?;
37 Ok(serde_json::from_slice(&result)?)
38 }
39
40 /// Like [`execute_value`], but also returns the `request_id` under which the
41 /// request was executed — the same id exposed to PHP via `folk_request_id()`.
42 ///
43 /// Plugins that maintain an access log (e.g. HTTP) use this to record the id
44 /// for log correlation. The default implementation calls [`execute_value`]
45 /// and returns an empty id (runtimes that don't track one).
46 async fn execute_value_traced(
47 &self,
48 method: &str,
49 payload: serde_json::Value,
50 ) -> Result<(serde_json::Value, Arc<str>)> {
51 let value = self.execute_value(method, payload).await?;
52 Ok((value, Arc::from("")))
53 }
54}
55
56/// Blanket impl: an `Arc<dyn Executor>` is also an `Executor`.
57#[async_trait]
58impl<T: Executor + ?Sized> Executor for Arc<T> {
59 async fn execute_method(&self, method: &str, payload: Bytes) -> Result<Bytes> {
60 (**self).execute_method(method, payload).await
61 }
62
63 async fn execute_value(
64 &self,
65 method: &str,
66 payload: serde_json::Value,
67 ) -> Result<serde_json::Value> {
68 (**self).execute_value(method, payload).await
69 }
70
71 async fn execute_value_traced(
72 &self,
73 method: &str,
74 payload: serde_json::Value,
75 ) -> Result<(serde_json::Value, Arc<str>)> {
76 (**self).execute_value_traced(method, payload).await
77 }
78}