From cbadea022a66f3f9e38f26d45fb4b72e07af6000 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 14 Feb 2024 15:13:15 -0800 Subject: [PATCH 1/4] Eval should never return an error Only awaiting eval should error. Eval should always be available, even if using its methods returns errors. --- examples/eval.rs | 3 +- packages/desktop/headless_tests/events.rs | 2 +- packages/desktop/headless_tests/rendering.rs | 1 - packages/desktop/src/eval.rs | 4 +- packages/html/src/eval.rs | 52 +++++++++++++++----- packages/liveview/src/eval.rs | 4 +- packages/playwright-tests/web/src/main.rs | 11 ++--- packages/ssr/src/eval.rs | 4 +- packages/web/src/eval.rs | 28 +++++------ 9 files changed, 65 insertions(+), 44 deletions(-) diff --git a/examples/eval.rs b/examples/eval.rs index 9e0646147..31ee813e9 100644 --- a/examples/eval.rs +++ b/examples/eval.rs @@ -25,8 +25,7 @@ fn app() -> Element { console.log(msg); return "hi from JS!"; "#, - ) - .unwrap(); + ); // Send a message to the JS code. eval.send("Hi from Rust!".into()).unwrap(); diff --git a/packages/desktop/headless_tests/events.rs b/packages/desktop/headless_tests/events.rs index 07be8be96..f9009ebe4 100644 --- a/packages/desktop/headless_tests/events.rs +++ b/packages/desktop/headless_tests/events.rs @@ -45,7 +45,7 @@ fn mock_event(id: &'static str, value: &'static str) { value, id ); - eval(&js).unwrap().await.unwrap(); + eval(&js).await.unwrap(); }); }) } diff --git a/packages/desktop/headless_tests/rendering.rs b/packages/desktop/headless_tests/rendering.rs index 0df48de07..882c70d21 100644 --- a/packages/desktop/headless_tests/rendering.rs +++ b/packages/desktop/headless_tests/rendering.rs @@ -38,7 +38,6 @@ fn use_inner_html(id: &'static str) -> Option { return element.innerHTML"#, id )) - .unwrap() .await .unwrap(); diff --git a/packages/desktop/src/eval.rs b/packages/desktop/src/eval.rs index 1a7b6aa6a..f920a508c 100644 --- a/packages/desktop/src/eval.rs +++ b/packages/desktop/src/eval.rs @@ -15,8 +15,8 @@ impl DesktopEvalProvider { } impl EvalProvider for DesktopEvalProvider { - fn new_evaluator(&self, js: String) -> Result>, EvalError> { - Ok(DesktopEvaluator::create(self.desktop_ctx.clone(), js)) + fn new_evaluator(&self, js: String) -> GenerationalBox> { + DesktopEvaluator::create(self.desktop_ctx.clone(), js) } } diff --git a/packages/html/src/eval.rs b/packages/html/src/eval.rs index 62ac1d4a2..5151750cd 100644 --- a/packages/html/src/eval.rs +++ b/packages/html/src/eval.rs @@ -1,7 +1,7 @@ #![allow(clippy::await_holding_refcell_ref)] use dioxus_core::prelude::*; -use generational_box::GenerationalBox; +use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; use std::future::{poll_fn, Future, IntoFuture}; use std::pin::Pin; use std::rc::Rc; @@ -10,7 +10,7 @@ use std::task::{Context, Poll}; /// A struct that implements EvalProvider is sent through [`ScopeState`]'s provide_context function /// so that [`use_eval`] can provide a platform agnostic interface for evaluating JavaScript code. pub trait EvalProvider { - fn new_evaluator(&self, js: String) -> Result>, EvalError>; + fn new_evaluator(&self, js: String) -> GenerationalBox>; } /// The platform's evaluator. @@ -29,7 +29,7 @@ pub trait Evaluator { ) -> Poll>; } -type EvalCreator = Rc Result>; +type EvalCreator = Rc UseEval>; /// Get a struct that can execute any JavaScript. /// @@ -43,19 +43,45 @@ type EvalCreator = Rc Result>; pub fn eval_provider() -> EvalCreator { let eval_provider = consume_context::>(); - Rc::new(move |script: &str| { - eval_provider - .new_evaluator(script.to_string()) - .map(UseEval::new) - }) as Rc Result> + Rc::new(move |script: &str| UseEval::new(eval_provider.new_evaluator(script.to_string()))) + as Rc UseEval> } -pub fn eval(script: &str) -> Result { - let eval_provider = dioxus_core::prelude::consume_context::>(); +pub fn eval(script: &str) -> UseEval { + let eval_provider = dioxus_core::prelude::try_consume_context::>() + // Create a dummy provider that always hiccups when trying to evaluate + // That way, we can still compile and run the code without a real provider + .unwrap_or_else(|| { + struct DummyProvider; + impl EvalProvider for DummyProvider { + fn new_evaluator(&self, _js: String) -> GenerationalBox> { + UnsyncStorage::owner().insert(Box::new(DummyEvaluator)) + } + } - eval_provider - .new_evaluator(script.to_string()) - .map(UseEval::new) + struct DummyEvaluator; + impl Evaluator for DummyEvaluator { + fn send(&self, _data: serde_json::Value) -> Result<(), EvalError> { + Err(EvalError::Unsupported) + } + fn poll_recv( + &mut self, + _context: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Err(EvalError::Unsupported)) + } + fn poll_join( + &mut self, + _context: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Err(EvalError::Unsupported)) + } + } + + Rc::new(DummyProvider) as Rc + }); + + UseEval::new(eval_provider.new_evaluator(script.to_string())) } /// A wrapper around the target platform's evaluator. diff --git a/packages/liveview/src/eval.rs b/packages/liveview/src/eval.rs index e521b35dc..c7dca0c3d 100644 --- a/packages/liveview/src/eval.rs +++ b/packages/liveview/src/eval.rs @@ -18,8 +18,8 @@ pub struct DesktopEvalProvider { } impl EvalProvider for DesktopEvalProvider { - fn new_evaluator(&self, js: String) -> Result>, EvalError> { - Ok(DesktopEvaluator::create(self.query.clone(), js)) + fn new_evaluator(&self, js: String) -> GenerationalBox> { + DesktopEvaluator::create(self.query.clone(), js) } } diff --git a/packages/playwright-tests/web/src/main.rs b/packages/playwright-tests/web/src/main.rs index 787988c62..3ac275eb1 100644 --- a/packages/playwright-tests/web/src/main.rs +++ b/packages/playwright-tests/web/src/main.rs @@ -24,12 +24,11 @@ fn app() -> Element { class: "eval-button", onclick: move |_| async move { let mut eval = eval( - r#" - window.document.title = 'Hello from Dioxus Eval!'; - dioxus.send("returned eval value"); - "#, - ) - .unwrap(); + r#" + window.document.title = 'Hello from Dioxus Eval!'; + dioxus.send("returned eval value"); + "#, + ); let result = eval.recv().await; if let Ok(serde_json::Value::String(string)) = result { diff --git a/packages/ssr/src/eval.rs b/packages/ssr/src/eval.rs index c55b7167f..af9d56394 100644 --- a/packages/ssr/src/eval.rs +++ b/packages/ssr/src/eval.rs @@ -13,9 +13,9 @@ pub fn init_eval() { /// Reprents the ssr-target's provider of evaluators. pub struct SSREvalProvider; impl EvalProvider for SSREvalProvider { - fn new_evaluator(&self, _: String) -> Result>, EvalError> { + fn new_evaluator(&self, _: String) -> GenerationalBox> { let owner = UnsyncStorage::owner(); - Ok(owner.insert(Box::new(SSREvaluator) as Box)) + owner.insert(Box::new(SSREvaluator) as Box) } } diff --git a/packages/web/src/eval.rs b/packages/web/src/eval.rs index 4156a9005..cb9bc0e42 100644 --- a/packages/web/src/eval.rs +++ b/packages/web/src/eval.rs @@ -16,7 +16,7 @@ pub fn init_eval() { /// Represents the web-target's provider of evaluators. pub struct WebEvalProvider; impl EvalProvider for WebEvalProvider { - fn new_evaluator(&self, js: String) -> Result>, EvalError> { + fn new_evaluator(&self, js: String) -> GenerationalBox> { WebEvaluator::create(js) } } @@ -33,12 +33,12 @@ const PROMISE_WRAPPER: &str = r#" struct WebEvaluator { dioxus: Dioxus, channel_receiver: futures_channel::mpsc::UnboundedReceiver, - result: Option, + result: Option>, } impl WebEvaluator { /// Creates a new evaluator for web-based targets. - fn create(js: String) -> Result>, EvalError> { + fn create(js: String) -> GenerationalBox> { let (mut channel_sender, channel_receiver) = futures_channel::mpsc::unbounded(); let owner = UnsyncStorage::owner(); let invalid = owner.invalid(); @@ -69,23 +69,21 @@ impl WebEvaluator { let string: String = stringified.into(); Value::from_str(&string).map_err(|e| { EvalError::Communication(format!("Failed to parse result - {}", e)) - })? + }) } else { - return Err(EvalError::Communication( + Err(EvalError::Communication( "Failed to stringify result".into(), - )); + )) } } else { - return Err(EvalError::Communication( + Err(EvalError::Communication( "Failed to stringify result".into(), - )); + )) } } - Err(err) => { - return Err(EvalError::InvalidJs( - err.as_string().unwrap_or("unknown".to_string()), - )); - } + Err(err) => Err(EvalError::InvalidJs( + err.as_string().unwrap_or("unknown".to_string()), + )), }; invalid.set(Box::new(Self { @@ -94,7 +92,7 @@ impl WebEvaluator { result: Some(result), }) as Box); - Ok(invalid) + invalid } } @@ -105,7 +103,7 @@ impl Evaluator for WebEvaluator { _cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { if let Some(result) = self.result.take() { - std::task::Poll::Ready(Ok(result)) + std::task::Poll::Ready(result) } else { std::task::Poll::Ready(Err(EvalError::Finished)) } From 22a5acfcb8af2961008e8a0873c53df9155a8f5e Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 14 Feb 2024 16:38:05 -0800 Subject: [PATCH 2/4] Remove SSR evalulator --- .vscode/settings.json | 1 + packages/ssr/src/eval.rs | 46 ---------------------------------------- packages/ssr/src/lib.rs | 2 -- 3 files changed, 1 insertion(+), 48 deletions(-) delete mode 100644 packages/ssr/src/eval.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index ebd52887e..836fb0116 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,5 +3,6 @@ "[toml]": { "editor.formatOnSave": false }, + "rust-analyzer.check.workspace": true, "rust-analyzer.checkOnSave.allTargets": false, } diff --git a/packages/ssr/src/eval.rs b/packages/ssr/src/eval.rs deleted file mode 100644 index af9d56394..000000000 --- a/packages/ssr/src/eval.rs +++ /dev/null @@ -1,46 +0,0 @@ -use async_trait::async_trait; -use dioxus_core::ScopeId; -use dioxus_html::prelude::{EvalError, EvalProvider, Evaluator}; -use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; -use std::rc::Rc; - -/// Provides the SSREvalProvider through [`cx.provide_context`]. -pub fn init_eval() { - let provider: Rc = Rc::new(SSREvalProvider {}); - ScopeId::ROOT.provide_context(provider); -} - -/// Reprents the ssr-target's provider of evaluators. -pub struct SSREvalProvider; -impl EvalProvider for SSREvalProvider { - fn new_evaluator(&self, _: String) -> GenerationalBox> { - let owner = UnsyncStorage::owner(); - owner.insert(Box::new(SSREvaluator) as Box) - } -} - -/// Represents a ssr-target's JavaScript evaluator. -pub struct SSREvaluator; - -// In ssr rendering we never run or resolve evals. -#[async_trait(?Send)] -impl Evaluator for SSREvaluator { - /// Sends a message to the evaluated JavaScript. - fn send(&self, _el: serde_json::Value) -> Result<(), EvalError> { - Ok(()) - } - - fn poll_recv( - &mut self, - _cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - std::task::Poll::Pending - } - - fn poll_join( - &mut self, - _cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - std::task::Poll::Pending - } -} diff --git a/packages/ssr/src/lib.rs b/packages/ssr/src/lib.rs index 6e3e16fd4..d80cdefea 100644 --- a/packages/ssr/src/lib.rs +++ b/packages/ssr/src/lib.rs @@ -11,7 +11,6 @@ pub mod incremental; #[cfg(feature = "incremental")] mod incremental_cfg; -pub mod eval; pub mod renderer; pub mod template; @@ -29,7 +28,6 @@ pub fn render_element(element: Element) -> String { } let mut dom = VirtualDom::new_with_props(lazy_app, element); - dom.in_runtime(crate::eval::init_eval); dom.rebuild(&mut NoOpMutations); Renderer::new().render(&dom) From d1d70995c4fddbdd06fce3d532ce5c67591fabb6 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 14 Feb 2024 16:51:05 -0800 Subject: [PATCH 3/4] Remove eval from incremental --- packages/ssr/src/incremental.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ssr/src/incremental.rs b/packages/ssr/src/incremental.rs index 3a17fd959..d1d4e9665 100644 --- a/packages/ssr/src/incremental.rs +++ b/packages/ssr/src/incremental.rs @@ -79,7 +79,6 @@ impl IncrementalRenderer { ) -> Result { let mut html_buffer = WriteBuffer { buffer: Vec::new() }; { - virtual_dom.in_runtime(crate::eval::init_eval); rebuild_with(&mut virtual_dom).await; renderer.render_before_body(&mut *html_buffer)?; From ea34eee20a0c0528560222c560141f128999c038 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 14 Feb 2024 17:00:12 -0800 Subject: [PATCH 4/4] Remove ssr eval from fullstack --- packages/fullstack/src/render.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/fullstack/src/render.rs b/packages/fullstack/src/render.rs index 8448abbcd..84ca1a6e5 100644 --- a/packages/fullstack/src/render.rs +++ b/packages/fullstack/src/render.rs @@ -44,10 +44,6 @@ impl SsrRendererPool { .expect("couldn't spawn runtime") .block_on(async move { let mut vdom = virtual_dom_factory(); - vdom.in_runtime(|| { - // Make sure the evaluator is initialized - dioxus_ssr::eval::init_eval(); - }); let mut to = WriteBuffer { buffer: Vec::new() }; // before polling the future, we need to set the context let prev_context =