Merge pull request #1940 from DioxusLabs/jk/tweak-eval

calling `eval` should not return an error - eval() should always be available
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2024-02-15 14:29:10 -08:00
commit 6cd9c695dc
13 files changed
+64 -95

No files matched your search

+1
View File
@@ -3,5 +3,6 @@
"[toml]": { "[toml]": {
"editor.formatOnSave": false "editor.formatOnSave": false
}, },
"rust-analyzer.check.workspace": true,
"rust-analyzer.checkOnSave.allTargets": false, "rust-analyzer.checkOnSave.allTargets": false,
} }
+1 -2
View File
@@ -25,8 +25,7 @@ fn app() -> Element {
console.log(msg); console.log(msg);
return "hi from JS!"; return "hi from JS!";
"#, "#,
) );
.unwrap();
// Send a message to the JS code. // Send a message to the JS code.
eval.send("Hi from Rust!".into()).unwrap(); eval.send("Hi from Rust!".into()).unwrap();
+1 -1
View File
@@ -45,7 +45,7 @@ fn mock_event(id: &'static str, value: &'static str) {
value, id value, id
); );
eval(&js).unwrap().await.unwrap(); eval(&js).await.unwrap();
}); });
}) })
} }
@@ -38,7 +38,6 @@ fn use_inner_html(id: &'static str) -> Option<String> {
return element.innerHTML"#, return element.innerHTML"#,
id id
)) ))
.unwrap()
.await .await
.unwrap(); .unwrap();
+2 -2
View File
@@ -15,8 +15,8 @@ impl DesktopEvalProvider {
} }
impl EvalProvider for DesktopEvalProvider { impl EvalProvider for DesktopEvalProvider {
fn new_evaluator(&self, js: String) -> Result<GenerationalBox<Box<dyn Evaluator>>, EvalError> { fn new_evaluator(&self, js: String) -> GenerationalBox<Box<dyn Evaluator>> {
Ok(DesktopEvaluator::create(self.desktop_ctx.clone(), js)) DesktopEvaluator::create(self.desktop_ctx.clone(), js)
} }
} }
-4
View File
@@ -44,10 +44,6 @@ impl SsrRendererPool {
.expect("couldn't spawn runtime") .expect("couldn't spawn runtime")
.block_on(async move { .block_on(async move {
let mut vdom = virtual_dom_factory(); 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() }; let mut to = WriteBuffer { buffer: Vec::new() };
// before polling the future, we need to set the context // before polling the future, we need to set the context
let prev_context = let prev_context =
+39 -13
View File
@@ -1,7 +1,7 @@
#![allow(clippy::await_holding_refcell_ref)] #![allow(clippy::await_holding_refcell_ref)]
use dioxus_core::prelude::*; use dioxus_core::prelude::*;
use generational_box::GenerationalBox; use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage};
use std::future::{poll_fn, Future, IntoFuture}; use std::future::{poll_fn, Future, IntoFuture};
use std::pin::Pin; use std::pin::Pin;
use std::rc::Rc; 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 /// 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. /// so that [`use_eval`] can provide a platform agnostic interface for evaluating JavaScript code.
pub trait EvalProvider { pub trait EvalProvider {
fn new_evaluator(&self, js: String) -> Result<GenerationalBox<Box<dyn Evaluator>>, EvalError>; fn new_evaluator(&self, js: String) -> GenerationalBox<Box<dyn Evaluator>>;
} }
/// The platform's evaluator. /// The platform's evaluator.
@@ -29,7 +29,7 @@ pub trait Evaluator {
) -> Poll<Result<serde_json::Value, EvalError>>; ) -> Poll<Result<serde_json::Value, EvalError>>;
} }
type EvalCreator = Rc<dyn Fn(&str) -> Result<UseEval, EvalError>>; type EvalCreator = Rc<dyn Fn(&str) -> UseEval>;
/// Get a struct that can execute any JavaScript. /// Get a struct that can execute any JavaScript.
/// ///
@@ -43,19 +43,45 @@ type EvalCreator = Rc<dyn Fn(&str) -> Result<UseEval, EvalError>>;
pub fn eval_provider() -> EvalCreator { pub fn eval_provider() -> EvalCreator {
let eval_provider = consume_context::<Rc<dyn EvalProvider>>(); let eval_provider = consume_context::<Rc<dyn EvalProvider>>();
Rc::new(move |script: &str| { Rc::new(move |script: &str| UseEval::new(eval_provider.new_evaluator(script.to_string())))
eval_provider as Rc<dyn Fn(&str) -> UseEval>
.new_evaluator(script.to_string())
.map(UseEval::new)
}) as Rc<dyn Fn(&str) -> Result<UseEval, EvalError>>
} }
pub fn eval(script: &str) -> Result<UseEval, EvalError> { pub fn eval(script: &str) -> UseEval {
let eval_provider = dioxus_core::prelude::consume_context::<Rc<dyn EvalProvider>>(); let eval_provider = dioxus_core::prelude::try_consume_context::<Rc<dyn EvalProvider>>()
// 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<Box<dyn Evaluator>> {
UnsyncStorage::owner().insert(Box::new(DummyEvaluator))
}
}
eval_provider struct DummyEvaluator;
.new_evaluator(script.to_string()) impl Evaluator for DummyEvaluator {
.map(UseEval::new) fn send(&self, _data: serde_json::Value) -> Result<(), EvalError> {
Err(EvalError::Unsupported)
}
fn poll_recv(
&mut self,
_context: &mut Context<'_>,
) -> Poll<Result<serde_json::Value, EvalError>> {
Poll::Ready(Err(EvalError::Unsupported))
}
fn poll_join(
&mut self,
_context: &mut Context<'_>,
) -> Poll<Result<serde_json::Value, EvalError>> {
Poll::Ready(Err(EvalError::Unsupported))
}
}
Rc::new(DummyProvider) as Rc<dyn EvalProvider>
});
UseEval::new(eval_provider.new_evaluator(script.to_string()))
} }
/// A wrapper around the target platform's evaluator. /// A wrapper around the target platform's evaluator.
+2 -2
View File
@@ -18,8 +18,8 @@ pub struct DesktopEvalProvider {
} }
impl EvalProvider for DesktopEvalProvider { impl EvalProvider for DesktopEvalProvider {
fn new_evaluator(&self, js: String) -> Result<GenerationalBox<Box<dyn Evaluator>>, EvalError> { fn new_evaluator(&self, js: String) -> GenerationalBox<Box<dyn Evaluator>> {
Ok(DesktopEvaluator::create(self.query.clone(), js)) DesktopEvaluator::create(self.query.clone(), js)
} }
} }
+5 -6
View File
@@ -24,12 +24,11 @@ fn app() -> Element {
class: "eval-button", class: "eval-button",
onclick: move |_| async move { onclick: move |_| async move {
let mut eval = eval( let mut eval = eval(
r#" r#"
window.document.title = 'Hello from Dioxus Eval!'; window.document.title = 'Hello from Dioxus Eval!';
dioxus.send("returned eval value"); dioxus.send("returned eval value");
"#, "#,
) );
.unwrap();
let result = eval.recv().await; let result = eval.recv().await;
if let Ok(serde_json::Value::String(string)) = result { if let Ok(serde_json::Value::String(string)) = result {
-46
View File
@@ -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<dyn EvalProvider> = 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) -> Result<GenerationalBox<Box<dyn Evaluator>>, EvalError> {
let owner = UnsyncStorage::owner();
Ok(owner.insert(Box::new(SSREvaluator) as Box<dyn Evaluator + 'static>))
}
}
/// 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<Result<serde_json::Value, EvalError>> {
std::task::Poll::Pending
}
fn poll_join(
&mut self,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<serde_json::Value, EvalError>> {
std::task::Poll::Pending
}
}
-1
View File
@@ -79,7 +79,6 @@ impl IncrementalRenderer {
) -> Result<RenderFreshness, IncrementalRendererError> { ) -> Result<RenderFreshness, IncrementalRendererError> {
let mut html_buffer = WriteBuffer { buffer: Vec::new() }; let mut html_buffer = WriteBuffer { buffer: Vec::new() };
{ {
virtual_dom.in_runtime(crate::eval::init_eval);
rebuild_with(&mut virtual_dom).await; rebuild_with(&mut virtual_dom).await;
renderer.render_before_body(&mut *html_buffer)?; renderer.render_before_body(&mut *html_buffer)?;
-2
View File
@@ -11,7 +11,6 @@ pub mod incremental;
#[cfg(feature = "incremental")] #[cfg(feature = "incremental")]
mod incremental_cfg; mod incremental_cfg;
pub mod eval;
pub mod renderer; pub mod renderer;
pub mod template; pub mod template;
@@ -29,7 +28,6 @@ pub fn render_element(element: Element) -> String {
} }
let mut dom = VirtualDom::new_with_props(lazy_app, element); let mut dom = VirtualDom::new_with_props(lazy_app, element);
dom.in_runtime(crate::eval::init_eval);
dom.rebuild(&mut NoOpMutations); dom.rebuild(&mut NoOpMutations);
Renderer::new().render(&dom) Renderer::new().render(&dom)
+13 -15
View File
@@ -16,7 +16,7 @@ pub fn init_eval() {
/// Represents the web-target's provider of evaluators. /// Represents the web-target's provider of evaluators.
pub struct WebEvalProvider; pub struct WebEvalProvider;
impl EvalProvider for WebEvalProvider { impl EvalProvider for WebEvalProvider {
fn new_evaluator(&self, js: String) -> Result<GenerationalBox<Box<dyn Evaluator>>, EvalError> { fn new_evaluator(&self, js: String) -> GenerationalBox<Box<dyn Evaluator>> {
WebEvaluator::create(js) WebEvaluator::create(js)
} }
} }
@@ -33,12 +33,12 @@ const PROMISE_WRAPPER: &str = r#"
struct WebEvaluator { struct WebEvaluator {
dioxus: Dioxus, dioxus: Dioxus,
channel_receiver: futures_channel::mpsc::UnboundedReceiver<serde_json::Value>, channel_receiver: futures_channel::mpsc::UnboundedReceiver<serde_json::Value>,
result: Option<serde_json::Value>, result: Option<Result<serde_json::Value, EvalError>>,
} }
impl WebEvaluator { impl WebEvaluator {
/// Creates a new evaluator for web-based targets. /// Creates a new evaluator for web-based targets.
fn create(js: String) -> Result<GenerationalBox<Box<dyn Evaluator>>, EvalError> { fn create(js: String) -> GenerationalBox<Box<dyn Evaluator>> {
let (mut channel_sender, channel_receiver) = futures_channel::mpsc::unbounded(); let (mut channel_sender, channel_receiver) = futures_channel::mpsc::unbounded();
let owner = UnsyncStorage::owner(); let owner = UnsyncStorage::owner();
let invalid = owner.invalid(); let invalid = owner.invalid();
@@ -69,23 +69,21 @@ impl WebEvaluator {
let string: String = stringified.into(); let string: String = stringified.into();
Value::from_str(&string).map_err(|e| { Value::from_str(&string).map_err(|e| {
EvalError::Communication(format!("Failed to parse result - {}", e)) EvalError::Communication(format!("Failed to parse result - {}", e))
})? })
} else { } else {
return Err(EvalError::Communication( Err(EvalError::Communication(
"Failed to stringify result".into(), "Failed to stringify result".into(),
)); ))
} }
} else { } else {
return Err(EvalError::Communication( Err(EvalError::Communication(
"Failed to stringify result".into(), "Failed to stringify result".into(),
)); ))
} }
} }
Err(err) => { Err(err) => Err(EvalError::InvalidJs(
return Err(EvalError::InvalidJs( err.as_string().unwrap_or("unknown".to_string()),
err.as_string().unwrap_or("unknown".to_string()), )),
));
}
}; };
invalid.set(Box::new(Self { invalid.set(Box::new(Self {
@@ -94,7 +92,7 @@ impl WebEvaluator {
result: Some(result), result: Some(result),
}) as Box<dyn Evaluator + 'static>); }) as Box<dyn Evaluator + 'static>);
Ok(invalid) invalid
} }
} }
@@ -105,7 +103,7 @@ impl Evaluator for WebEvaluator {
_cx: &mut std::task::Context<'_>, _cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<serde_json::Value, EvalError>> { ) -> std::task::Poll<Result<serde_json::Value, EvalError>> {
if let Some(result) = self.result.take() { if let Some(result) = self.result.take() {
std::task::Poll::Ready(Ok(result)) std::task::Poll::Ready(result)
} else { } else {
std::task::Poll::Ready(Err(EvalError::Finished)) std::task::Poll::Ready(Err(EvalError::Finished))
} }