|
| 1 | +use std::panic::AssertUnwindSafe; |
| 2 | +use std::pin::Pin; |
| 3 | + |
| 4 | +use subsecond::{HotFn, HotFnPanic}; |
| 5 | + |
| 6 | +/// Runs given future with [`subsecond`], dropping the future and re-running it |
| 7 | +/// when the code changes. |
| 8 | +/// |
| 9 | +/// When the hot-patching feature is not enabled, the function just runs the |
| 10 | +/// future once. |
1 | 11 | #[allow( |
2 | 12 | clippy::allow_attributes, |
3 | 13 | reason = "Only happens when hot-patching is enabled" |
|
6 | 16 | clippy::future_not_send, |
7 | 17 | reason = "Send not needed; serve/Bootstrapper is run async in a single thread" |
8 | 18 | )] |
9 | | -#[doc(hidden)] |
10 | 19 | pub async fn serve<O, F>(callback: impl FnMut() -> F) |
11 | 20 | where |
12 | 21 | F: Future<Output = O> + 'static, |
13 | 22 | { |
| 23 | + println!("1"); |
| 24 | + |
14 | 25 | #[cfg(feature = "hot-patching")] |
15 | 26 | { |
16 | | - dioxus_devtools::serve_subsecond(callback).await; |
| 27 | + println!("2"); |
| 28 | + // dioxus_devtools::serve_subsecond(callback).await; |
| 29 | + dioxus_devtools::connect_subsecond(); |
| 30 | + let mut callback = callback; |
| 31 | + callback().await; |
17 | 32 | } |
18 | 33 |
|
19 | 34 | #[cfg(not(feature = "hot-patching"))] |
|
23 | 38 | } |
24 | 39 | } |
25 | 40 |
|
26 | | -#[doc(hidden)] |
| 41 | +/// Calls the function using [`subsecond::HotFn`]. |
| 42 | +/// |
| 43 | +/// This causes the function passed to be hot-reloadable. If the hot-reloading |
| 44 | +/// feature is not enabled, the function is called directly. |
27 | 45 | pub fn call_hot<F, A, R>(func: F, args: A) -> R |
28 | 46 | where |
29 | 47 | F: FnMut(A) -> R, |
|
40 | 58 | func(args) |
41 | 59 | } |
42 | 60 | } |
| 61 | + |
| 62 | +pub fn call_async<F, Fut, A, O>(f: F, args: A) -> Pin<Box<dyn Future<Output = O> + Send>> |
| 63 | +where |
| 64 | + F: FnOnce(A) -> Fut, |
| 65 | + Fut: Future<Output = O> + Send + 'static, |
| 66 | +{ |
| 67 | + // return Box::pin(f(args)); |
| 68 | + |
| 69 | + // For FnOnce, we need to handle this differently since we can only call it once |
| 70 | + // We'll store the closure in an Option and take it when needed |
| 71 | + let mut f_option = Some(f); |
| 72 | + |
| 73 | + // Create a wrapper function that boxes the future |
| 74 | + let wrapper = move |args| -> Pin<Box<dyn Future<Output = O> + Send>> { |
| 75 | + if let Some(closure) = f_option.take() { |
| 76 | + Box::pin(closure(args)) |
| 77 | + } else { |
| 78 | + // This shouldn't happen in normal hot reload scenarios since each |
| 79 | + // hot reload creates a new call_async invocation |
| 80 | + panic!( |
| 81 | + "Hot reload closure already consumed - this indicates a problem with the hot reload system" |
| 82 | + ) |
| 83 | + } |
| 84 | + }; |
| 85 | + |
| 86 | + let mut hotfn = HotFn::current(wrapper); |
| 87 | + loop { |
| 88 | + let res = std::panic::catch_unwind(AssertUnwindSafe(|| hotfn.call((args,)))); |
| 89 | + |
| 90 | + // If the call succeeds just return the result, otherwise we try to handle the |
| 91 | + // panic if its our own. |
| 92 | + let err = match res { |
| 93 | + Ok(res) => return res, |
| 94 | + Err(err) => err, |
| 95 | + }; |
| 96 | + |
| 97 | + // If this is our panic then let's handle it, otherwise we just resume unwinding |
| 98 | + let Some(_hot_payload) = err.downcast_ref::<HotFnPanic>() else { |
| 99 | + std::panic::resume_unwind(err); |
| 100 | + }; |
| 101 | + |
| 102 | + // For hot reload with FnOnce, we can't retry with the same closure |
| 103 | + // The hot reload system should create a new function call entirely |
| 104 | + panic!( |
| 105 | + "Hot reload detected but cannot retry with FnOnce closure - hot reload should create new function instance" |
| 106 | + ); |
| 107 | + } |
| 108 | +} |
0 commit comments