Skip to main content

powhttp_sdk/runtime/
mod.rs

1use std::future::Future;
2use std::sync::Arc;
3use crate::error::Error;
4use crate::runtime::handle::ExtensionHandle;
5use crate::runtime::state::{ExtensionState, ShutdownHandle};
6
7pub(crate) mod handlers;
8pub(crate) mod handle;
9pub(crate) mod state;
10
11/// Starts the extension runtime, calling `init` with an [`ExtensionHandle`].
12///
13/// This is the main entry point for an extension. The runtime runs until the
14/// host disconnects or [`ExtensionHandle::shutdown`] is called.
15///
16/// ```rust,no_run
17/// use powhttp_sdk::{run, Error, ExtensionHandle};
18///
19/// #[tokio::main]
20/// async fn main() -> Result<(), Error> {
21///     run(async |handle: ExtensionHandle| {
22///         Ok(())
23///     }).await
24/// }
25/// ```
26pub async fn run<F, Fut>(init: F) -> Result<(), Error>
27where
28    F: FnOnce(ExtensionHandle) -> Fut + Send + 'static,
29    Fut: Future<Output = Result<(), Error>> + Send + 'static,
30{
31    let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel();
32    let state = Arc::new(ExtensionState::new(ShutdownHandle::new(shutdown_tx)));
33
34    let app = tokio_jrpc::App::new(tokio::io::stdin(), tokio::io::stdout(), state.clone())
35        .method("context_menu/call_item_handler_single", handlers::context_menu::call_item_handler_single)
36        .method("context_menu/call_item_handler_multi", handlers::context_menu::call_item_handler_multi)
37        .method("overview/get_field_value", handlers::overview::get_field_value)
38        .method("inspector/is_request_tab_visible", handlers::inspector::is_request_tab_visible)
39        .method("inspector/is_response_tab_visible", handlers::inspector::is_response_tab_visible)
40        .method("inspector/get_request_tab_content", handlers::inspector::get_request_tab_content)
41        .method("inspector/get_response_tab_content", handlers::inspector::get_response_tab_content)
42        .method("proxy_server/call_connect_handler", handlers::proxy_server::call_connect_handler);
43
44    let client = app.client_handle();
45    let handle = ExtensionHandle::new(client, state);
46
47    let init_task = tokio::spawn(async move {
48        let stop = handle.clone();
49        match init(handle).await {
50            Ok(()) => Ok(()),
51            Err(err) => {
52                eprintln!("extension init error: {err}");
53                stop.shutdown().await;
54                Err(err)
55            }
56        }
57    });
58
59    enum AppResult {
60        Ran(Result<(), tokio_jrpc::RuntimeError>),
61        Shutdown,
62    }
63
64    let run_fut = app.run();
65    tokio::pin!(run_fut);
66
67    let app_result = tokio::select! {
68        r = &mut run_fut => AppResult::Ran(r),
69        _ = shutdown_rx => AppResult::Shutdown,
70    };
71
72    init_task.await.map_err(Error::new)??;
73
74    match app_result {
75        AppResult::Ran(r) => r.map_err(Error::from),
76        AppResult::Shutdown => Ok(()),
77    }
78}