feat: add subsecond serve (#4588)
* feat: add subsecond serve * typos...
This commit is contained in:
1 parent
9ec474962b
commit
0a04a199a4
3 files changed
+112
-2
No files matched your search
Generated
+4
-2
@@ -5398,6 +5398,8 @@ dependencies = [
|
||||
"dioxus-core",
|
||||
"dioxus-devtools-types",
|
||||
"dioxus-signals",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"subsecond",
|
||||
@@ -7236,9 +7238,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
version = "2.6.0"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
|
||||
checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"futures-core",
|
||||
|
||||
@@ -19,6 +19,9 @@ serde_json = { workspace = true }
|
||||
subsecond = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
futures-util = { workspace = true, features = ["sink", "async-await-macro"], optional = true }
|
||||
futures-channel = { workspace = true, optional = true }
|
||||
|
||||
# hot reloading serve
|
||||
tracing = { workspace = true }
|
||||
warnings = { workspace = true }
|
||||
@@ -32,3 +35,7 @@ serde_json = { workspace = true }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
serve = ["dep:futures-util", "dep:futures-channel"]
|
||||
@@ -109,3 +109,104 @@ pub fn connect_at(endpoint: String, mut callback: impl FnMut(DevserverMsg) + Sen
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Run this asynchronous future to completion.
|
||||
///
|
||||
/// Whenever your code changes, the future is dropped and a new one is created using the new function.
|
||||
///
|
||||
/// This is useful for using subsecond outside of dioxus, like with axum. To pass args to the underlying
|
||||
/// function, you can use the `serve_subsecond_with_args` function.
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// dioxus_devtools::serve_subsecond(router_main).await;
|
||||
/// }
|
||||
///
|
||||
/// async fn router_main() {
|
||||
/// use axum::{Router, routing::get};
|
||||
///
|
||||
/// let app = Router::new().route("/", get(test_route));
|
||||
///
|
||||
/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
|
||||
/// println!("Server running on http://localhost:3000");
|
||||
///
|
||||
/// axum::serve(listener, app.clone()).await.unwrap()
|
||||
/// }
|
||||
///
|
||||
/// async fn test_route() -> axum::response::Html<&'static str> {
|
||||
/// "axum works!!!!!".into()
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "serve")]
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
pub async fn serve_subsecond<O, F>(mut callback: impl FnMut() -> F)
|
||||
where
|
||||
F: std::future::Future<Output = O> + 'static,
|
||||
{
|
||||
serve_subsecond_with_args((), move |_args| callback()).await
|
||||
}
|
||||
|
||||
/// Run this asynchronous future to completion.
|
||||
///
|
||||
/// Whenever your code changes, the future is dropped and a new one is created using the new function.
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let args = ("abc".to_string(),);
|
||||
/// dioxus_devtools::serve_subsecond_with_args(args, router_main).await;
|
||||
/// }
|
||||
///
|
||||
/// async fn router_main(args: (String,)) {
|
||||
/// use axum::{Router, routing::get};
|
||||
///
|
||||
/// let app = Router::new().route("/", get(test_route));
|
||||
///
|
||||
/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
|
||||
/// println!("Server running on http://localhost:3000 -> {}", args.0);
|
||||
///
|
||||
/// axum::serve(listener, app.clone()).await.unwrap()
|
||||
/// }
|
||||
///
|
||||
/// async fn test_route() -> axum::response::Html<&'static str> {
|
||||
/// "axum works!!!!!".into()
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "serve")]
|
||||
pub async fn serve_subsecond_with_args<A: Clone, O, F>(args: A, mut callback: impl FnMut(A) -> F)
|
||||
where
|
||||
F: std::future::Future<Output = O> + 'static,
|
||||
{
|
||||
let (tx, mut rx) = futures_channel::mpsc::unbounded();
|
||||
|
||||
connect(move |msg| {
|
||||
if let DevserverMsg::HotReload(hot_reload_msg) = msg {
|
||||
if let Some(jumptable) = hot_reload_msg.jump_table {
|
||||
if hot_reload_msg.for_pid == Some(std::process::id()) {
|
||||
unsafe { subsecond::apply_patch(jumptable).unwrap() };
|
||||
tx.unbounded_send(()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let wrapped = move |args| -> std::pin::Pin<Box<dyn std::future::Future<Output = O>>> {
|
||||
Box::pin(callback(args))
|
||||
};
|
||||
|
||||
let mut hotfn = subsecond::HotFn::current(wrapped);
|
||||
let mut cur_future = hotfn.call((args.clone(),));
|
||||
|
||||
loop {
|
||||
use futures_util::StreamExt;
|
||||
let res = futures_util::future::select(cur_future, rx.next()).await;
|
||||
|
||||
match res {
|
||||
futures_util::future::Either::Left(_completed) => _ = rx.next().await,
|
||||
futures_util::future::Either::Right(_reload) => {}
|
||||
}
|
||||
|
||||
cur_future = hotfn.call((args.clone(),));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user