From 54ca9b4f8351ee44e96720d7864267122fa8db32 Mon Sep 17 00:00:00 2001 From: Cameron Taggart Date: Fri, 11 Oct 2019 15:50:58 +0100 Subject: [PATCH 01/15] bump wasm-bindgen to 0.2.51 (#681) * upgrade wasm-bindgen to 0.2.51 * Run macro tests on beta and update readme * Update README.md * Update .travis.yml --- .travis.yml | 7 ++--- Cargo.toml | 4 +-- README.md | 2 +- examples/js_callback/Cargo.toml | 2 +- tests/derive_props/fail.stderr | 7 ++--- tests/derive_props_test.rs | 2 +- tests/macro/html-block-fail.stderr | 1 + .../html-component-fail-unimplemented.stderr | 1 + tests/macro/html-component-fail.stderr | 27 +++++++++++-------- tests/macro/html-iterable-fail.stderr | 1 + tests/macro/html-list-fail.stderr | 2 ++ tests/macro/html-node-fail.stderr | 7 ++--- tests/macro/html-tag-fail.stderr | 3 ++- tests/macro_test.rs | 2 +- 14 files changed, 41 insertions(+), 27 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7f0db07a1b0..0a5b0248511 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ branches: - staging - trying - master + - futures language: rust @@ -19,8 +20,8 @@ cache: - $HOME/.local/share/cargo-web/emscripten rust: - - 1.35.0 # min supported - - stable + # - 1.39.0 # min supported + # - stable - beta - nightly @@ -33,7 +34,7 @@ install: - nvm install 9 - rustup component add rustfmt - rustup target add wasm32-unknown-unknown - - cargo install --force --version 0.2.42 -- wasm-bindgen-cli + - cargo install --force --version 0.2.51 -- wasm-bindgen-cli - curl --retry 5 -LO https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.zip - unzip chromedriver_linux64.zip - ./ci/install_cargo_web.sh diff --git a/Cargo.toml b/Cargo.toml index b673baef98b..53fd21bbcce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ toml = { version = "0.4", optional = true } yew-macro = { version = "0.10.0", path = "crates/macro" } [target.'cfg(all(target_arch = "wasm32", not(cargo_web)))'.dependencies] -wasm-bindgen = "=0.2.42" +wasm-bindgen = "0.2.51" [dev-dependencies] serde_derive = "1" @@ -46,7 +46,7 @@ trybuild = "1.0" rustversion = "0.1" [target.'cfg(all(target_arch = "wasm32", not(cargo_web)))'.dev-dependencies] -wasm-bindgen-test = "0.2" +wasm-bindgen-test = "0.3.1" [features] default = [] diff --git a/README.md b/README.md index e0702532ee5..1d41dbae8fe 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@

Build Status Gitter Chat - Rustc Version 1.35+ + Rustc Version beta (1.39)+

diff --git a/examples/js_callback/Cargo.toml b/examples/js_callback/Cargo.toml index 9e7d46725d8..92c7dc246a2 100644 --- a/examples/js_callback/Cargo.toml +++ b/examples/js_callback/Cargo.toml @@ -9,4 +9,4 @@ yew = { path = "../.." } stdweb = "^0.4.20" [target.'cfg(all(target_arch = "wasm32", not(cargo_web)))'.dependencies] -wasm-bindgen = "=0.2.42" +wasm-bindgen = "0.2.51" diff --git a/tests/derive_props/fail.stderr b/tests/derive_props/fail.stderr index 9f626a97d5b..79514b472c5 100644 --- a/tests/derive_props/fail.stderr +++ b/tests/derive_props/fail.stderr @@ -19,7 +19,7 @@ error[E0599]: no method named `build` found for type `t3::PropsBuilder` error[E0599]: no method named `b` found for type `t4::PropsBuilder` in the current scope --> $DIR/fail.rs:48:26 @@ -28,7 +28,8 @@ error[E0599]: no method named `b` found for type `t4::PropsBuilder>` for `()` For more information about this error, try `rustc --explain E0277`. +error: could not compile `yew-tests`. diff --git a/tests/macro/html-component-fail-unimplemented.stderr b/tests/macro/html-component-fail-unimplemented.stderr index 185eda68a78..ecef60be81e 100644 --- a/tests/macro/html-component-fail-unimplemented.stderr +++ b/tests/macro/html-component-fail-unimplemented.stderr @@ -5,3 +5,4 @@ error[E0277]: the trait bound `std::string::String: yew::html::Component` is not | ^^^^^^ the trait `yew::html::Component` is not implemented for `std::string::String` For more information about this error, try `rustc --explain E0277`. +error: could not compile `yew-tests`. diff --git a/tests/macro/html-component-fail.stderr b/tests/macro/html-component-fail.stderr index 1a8fe309543..99fe3afac92 100644 --- a/tests/macro/html-component-fail.stderr +++ b/tests/macro/html-component-fail.stderr @@ -20,7 +20,7 @@ error: expected type, found `/` --> $DIR/html-component-fail.rs:70:14 | 70 | html! { }; - | ^ + | ^ expected type error: this open tag has no corresponding close tag --> $DIR/html-component-fail.rs:71:13 @@ -47,7 +47,7 @@ error[E0599]: no method named `build` found for type `ChildPropertiesBuilder }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `ChildPropertiesBuilder` | = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) @@ -58,7 +58,7 @@ error[E0599]: no method named `build` found for type `ChildPropertiesBuilder }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `ChildPropertiesBuilder` | = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) @@ -69,7 +69,7 @@ error[E0599]: no method named `build` found for type `ChildPropertiesBuilder }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `ChildPropertiesBuilder` | = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) @@ -80,7 +80,7 @@ error[E0599]: no method named `build` found for type `ChildPropertiesBuilder }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `ChildPropertiesBuilder` | = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) @@ -91,7 +91,7 @@ error[E0599]: no method named `build` found for type `ChildPropertiesBuilder }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `ChildPropertiesBuilder` | = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) @@ -110,7 +110,7 @@ error[E0599]: no method named `unknown` found for type `ChildPropertiesBuilder }; - | ^^^^^^^ + | ^^^^^^^ method not found in `ChildPropertiesBuilder` error[E0599]: no method named `build` found for type `ChildPropertiesBuilder` in the current scope --> $DIR/html-component-fail.rs:64:5 @@ -119,7 +119,7 @@ error[E0599]: no method named `build` found for type `ChildPropertiesBuilder }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `ChildPropertiesBuilder` | = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) @@ -161,6 +161,10 @@ error[E0308]: mismatched types | 68 | html! { }; | ^^^^ expected i32, found u32 +help: you can convert an `u32` to `i32` and panic if the converted value wouldn't fit + | +68 | html! { }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0599]: no method named `string` found for type `ChildPropertiesBuilder` in the current scope --> $DIR/html-component-fail.rs:69:20 @@ -169,7 +173,7 @@ error[E0599]: no method named `string` found for type `ChildPropertiesBuilder }; - | ^^^^^^ + | ^^^^^^ method not found in `ChildPropertiesBuilder` error[E0599]: no method named `children` found for type `ChildPropertiesBuilder` in the current scope --> $DIR/html-component-fail.rs:73:5 @@ -178,7 +182,7 @@ error[E0599]: no method named `children` found for type `ChildPropertiesBuilder< | - method `children` not found for this ... 73 | html! { { "Not allowed" } }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `ChildPropertiesBuilder` | = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) @@ -227,5 +231,6 @@ error[E0277]: the trait bound `yew::virtual_dom::vcomp::VChild>` for `yew::virtual_dom::vnode::VNode<_>` = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) -Some errors occurred: E0277, E0308, E0425, E0599, E0609. +Some errors have detailed explanations: E0277, E0308, E0425, E0599, E0609. For more information about an error, try `rustc --explain E0277`. +error: could not compile `yew-tests`. diff --git a/tests/macro/html-iterable-fail.stderr b/tests/macro/html-iterable-fail.stderr index e14d785dbf7..4536879cd4f 100644 --- a/tests/macro/html-iterable-fail.stderr +++ b/tests/macro/html-iterable-fail.stderr @@ -60,3 +60,4 @@ error[E0277]: `()` doesn't implement `std::fmt::Display` = note: required because of the requirements on the impl of `std::convert::Into>` for `&()` For more information about this error, try `rustc --explain E0277`. +error: could not compile `yew-tests`. diff --git a/tests/macro/html-list-fail.stderr b/tests/macro/html-list-fail.stderr index a618571662d..179b3e02228 100644 --- a/tests/macro/html-list-fail.stderr +++ b/tests/macro/html-list-fail.stderr @@ -39,3 +39,5 @@ error: expected valid html element | 10 | html! { <>invalid }; | ^^^^^^^ + +error: could not compile `yew-tests`. diff --git a/tests/macro/html-node-fail.stderr b/tests/macro/html-node-fail.stderr index 6cc3a67c90a..c36f67cf647 100644 --- a/tests/macro/html-node-fail.stderr +++ b/tests/macro/html-node-fail.stderr @@ -22,7 +22,7 @@ error: unsupported type 11 | html! { b"str" }; | ^^^^^^ -error: int literal is too large +error: integer literal is too large --> $DIR/html-node-fail.rs:12:14 | 12 | html! { 1111111111111111111111111111111111111111111111111111111111111111111111111111 }; @@ -40,7 +40,7 @@ error: unsupported type 14 | html! { { b"str" } }; | ^^^^^^ -error: int literal is too large +error: integer literal is too large --> $DIR/html-node-fail.rs:15:22 | 15 | html! { { 1111111111111111111111111111111111111111111111111111111111111111111111111111 } }; @@ -76,5 +76,6 @@ error[E0277]: `()` doesn't implement `std::fmt::Display` = note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode<_>` = note: required by `std::convert::From::from` -Some errors occurred: E0277, E0425. +Some errors have detailed explanations: E0277, E0425. For more information about an error, try `rustc --explain E0277`. +error: could not compile `yew-tests`. diff --git a/tests/macro/html-tag-fail.stderr b/tests/macro/html-tag-fail.stderr index 8a6afb1f719..abed9964b46 100644 --- a/tests/macro/html-tag-fail.stderr +++ b/tests/macro/html-tag-fail.stderr @@ -184,5 +184,6 @@ error[E0277]: the trait bound `yew::html::Href: std::convert::From<()>` is not s > = note: required because of the requirements on the impl of `std::convert::Into` for `()` -Some errors occurred: E0277, E0308. +Some errors have detailed explanations: E0277, E0308. For more information about an error, try `rustc --explain E0277`. +error: could not compile `yew-tests`. diff --git a/tests/macro_test.rs b/tests/macro_test.rs index 9c8f08405bd..3723004c733 100644 --- a/tests/macro_test.rs +++ b/tests/macro_test.rs @@ -1,5 +1,5 @@ #[allow(dead_code)] -#[rustversion::attr(stable(1.35.0), cfg_attr(not(feature = "web_test"), test))] +#[rustversion::attr(beta, cfg_attr(not(feature = "web_test"), test))] fn tests() { let t = trybuild::TestCases::new(); From 540ec9b182e74404c97896cfcd5811a4766a7f3f Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Fri, 25 Oct 2019 22:17:02 -0400 Subject: [PATCH 02/15] implement send_future --- Cargo.toml | 1 + src/html/mod.rs | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 53fd21bbcce..f7823ef21b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ yew-macro = { version = "0.10.0", path = "crates/macro" } [target.'cfg(all(target_arch = "wasm32", not(cargo_web)))'.dependencies] wasm-bindgen = "0.2.51" +wasm-bindgen-futures = "0.4.2" [dev-dependencies] serde_derive = "1" diff --git a/src/html/mod.rs b/src/html/mod.rs index 76f67b91bfa..acd4f604bbf 100644 --- a/src/html/mod.rs +++ b/src/html/mod.rs @@ -14,6 +14,9 @@ use crate::callback::Callback; use crate::virtual_dom::{VChild, VList, VNode}; use std::fmt; +#[cfg(all(target_arch = "wasm32", not(cargo_web)))] +use std::future::Future; + /// This type indicates that component should be rendered again. pub type ShouldRender = bool; @@ -338,6 +341,28 @@ where closure.into() } + #[cfg(all(target_arch = "wasm32", not(cargo_web)))] + /// This method processes a Future that returns a message and sends it back to the component's + /// loop. + pub fn send_future(&self, future: F) + where F: Future>> + 'static + { + use wasm_bindgen::JsValue; + use wasm_bindgen_futures::future_to_promise; + + let mut scope = self.scope.clone(); + + let js_future = async { + future.await + .and_then(move |message| { + scope.send_message(message); + Ok(JsValue::NULL) + }) + .map_err(|e| JsValue::from_str(&format!("{}", e))) + }; + future_to_promise(js_future); + } + /// This method sends a message to this component immediately. pub fn send_self(&mut self, msg: COMP::Message) { self.scope.send_message(msg); From 74fff557ba419c04d8d26e7218f7becc66b1fe28 Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Fri, 25 Oct 2019 22:17:15 -0400 Subject: [PATCH 03/15] cargo fmt --- src/html/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/html/mod.rs b/src/html/mod.rs index acd4f604bbf..c9618e38a1c 100644 --- a/src/html/mod.rs +++ b/src/html/mod.rs @@ -345,15 +345,17 @@ where /// This method processes a Future that returns a message and sends it back to the component's /// loop. pub fn send_future(&self, future: F) - where F: Future>> + 'static + where + F: Future>> + 'static, { use wasm_bindgen::JsValue; use wasm_bindgen_futures::future_to_promise; - + let mut scope = self.scope.clone(); let js_future = async { - future.await + future + .await .and_then(move |message| { scope.send_message(message); Ok(JsValue::NULL) From 993456b4882c94b8af6766f3b0c06e91a0e0cac7 Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Fri, 25 Oct 2019 22:26:09 -0400 Subject: [PATCH 04/15] add todo question --- src/html/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/html/mod.rs b/src/html/mod.rs index c9618e38a1c..e6d8316996d 100644 --- a/src/html/mod.rs +++ b/src/html/mod.rs @@ -362,7 +362,7 @@ where }) .map_err(|e| JsValue::from_str(&format!("{}", e))) }; - future_to_promise(js_future); + future_to_promise(js_future); // TODO should this return the Promise as a handle? } /// This method sends a message to this component immediately. From de952e85f3ef25c740b1872c7f7dc5a27008f932 Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Fri, 25 Oct 2019 23:01:28 -0400 Subject: [PATCH 05/15] static dispatch on Error in future --- Cargo.toml | 2 +- src/html/mod.rs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f7823ef21b7..656a13b952e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ yew-macro = { version = "0.10.0", path = "crates/macro" } [target.'cfg(all(target_arch = "wasm32", not(cargo_web)))'.dependencies] wasm-bindgen = "0.2.51" -wasm-bindgen-futures = "0.4.2" +wasm-bindgen-futures = "0.4.1" [dev-dependencies] serde_derive = "1" diff --git a/src/html/mod.rs b/src/html/mod.rs index e6d8316996d..951f9fa1ccb 100644 --- a/src/html/mod.rs +++ b/src/html/mod.rs @@ -344,9 +344,10 @@ where #[cfg(all(target_arch = "wasm32", not(cargo_web)))] /// This method processes a Future that returns a message and sends it back to the component's /// loop. - pub fn send_future(&self, future: F) + pub fn send_future(&self, future: F) where - F: Future>> + 'static, + F: Future> + 'static, + E: std::error::Error, { use wasm_bindgen::JsValue; use wasm_bindgen_futures::future_to_promise; From 1cc71b6bc33006e3553df8926d83dce525f89a42 Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Sun, 3 Nov 2019 14:50:45 -0500 Subject: [PATCH 06/15] Add framework for example using futures --- Cargo.toml | 1 + examples/futures/Cargo.toml | 14 +++++++++++++ examples/futures/README | 6 ++++++ examples/futures/index.html | 10 +++++++++ examples/futures/main.js | 6 ++++++ examples/futures/src/lib.rs | 41 +++++++++++++++++++++++++++++++++++++ 6 files changed, 78 insertions(+) create mode 100644 examples/futures/Cargo.toml create mode 100644 examples/futures/README create mode 100644 examples/futures/index.html create mode 100644 examples/futures/main.js create mode 100644 examples/futures/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 656a13b952e..5a9e207d617 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,7 @@ members = [ "examples/custom_components", "examples/dashboard", "examples/file_upload", + "examples/futures", "examples/fragments", "examples/game_of_life", "examples/inner_html", diff --git a/examples/futures/Cargo.toml b/examples/futures/Cargo.toml new file mode 100644 index 00000000000..33d80b3c982 --- /dev/null +++ b/examples/futures/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "futures" +version = "0.1.0" +authors = ["Henry Zimmerman "] +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +yew = { path = "../.." } + +[target.'cfg(all(target_arch = "wasm32", not(cargo_web)))'.dependencies] +wasm-bindgen = "0.2.51" \ No newline at end of file diff --git a/examples/futures/README b/examples/futures/README new file mode 100644 index 00000000000..89a159fe963 --- /dev/null +++ b/examples/futures/README @@ -0,0 +1,6 @@ +### How to run: +```sh +rustup run nightly $HOME/.cargo/bin/wasm-pack build --target web && rollup ./main.js --format iife --file ./pkg/bundle.js && python -m SimpleHTTPServer 8080 +``` +It is expected that you have a setup with wasm-pack, rollup and python installed. +Since wasm-pack doesn't have a nightly version at the moment, you have to hack around that by using `rustup run nightly`. diff --git a/examples/futures/index.html b/examples/futures/index.html new file mode 100644 index 00000000000..61a5d46b81f --- /dev/null +++ b/examples/futures/index.html @@ -0,0 +1,10 @@ + + + + + Yew • Futures + + + + + \ No newline at end of file diff --git a/examples/futures/main.js b/examples/futures/main.js new file mode 100644 index 00000000000..cce343c6215 --- /dev/null +++ b/examples/futures/main.js @@ -0,0 +1,6 @@ +import init, { run_app } from './pkg/futures.js'; +async function main() { + await init('./pkg/futures_bg.wasm'); + run_app(); +} +main() \ No newline at end of file diff --git a/examples/futures/src/lib.rs b/examples/futures/src/lib.rs new file mode 100644 index 00000000000..d6d972a9269 --- /dev/null +++ b/examples/futures/src/lib.rs @@ -0,0 +1,41 @@ +use yew::{html, Component, ComponentLink, Html, ShouldRender}; + +struct Model {} + +enum Msg { + DoIt, +} + +impl Component for Model { + // Some details omitted. Explore the examples to see more. + + type Message = Msg; + type Properties = (); + + fn create(_: Self::Properties, _: ComponentLink) -> Self { + Model {} + } + + fn update(&mut self, msg: Self::Message) -> ShouldRender { + match msg { + Msg::DoIt => { + // Update your model on events + true + } + } + } + + fn view(&self) -> Html { + html! { + // Render your model here + + } + } +} + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn run_app() { + yew::start_app::(); +} From 016672a79c6e8c004e5193d3e9ec955dcf5790be Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Sun, 3 Nov 2019 15:20:41 -0500 Subject: [PATCH 07/15] add a basic future to indicate that futures work --- examples/futures/Cargo.toml | 3 ++- examples/futures/src/lib.rs | 46 +++++++++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/examples/futures/Cargo.toml b/examples/futures/Cargo.toml index 33d80b3c982..1b10661a945 100644 --- a/examples/futures/Cargo.toml +++ b/examples/futures/Cargo.toml @@ -9,6 +9,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] yew = { path = "../.." } +web-sys = "0.3.30" [target.'cfg(all(target_arch = "wasm32", not(cargo_web)))'.dependencies] -wasm-bindgen = "0.2.51" \ No newline at end of file +wasm-bindgen = "0.2.51" diff --git a/examples/futures/src/lib.rs b/examples/futures/src/lib.rs index d6d972a9269..b4871944103 100644 --- a/examples/futures/src/lib.rs +++ b/examples/futures/src/lib.rs @@ -1,10 +1,25 @@ +use crate::Msg::FutureFinished; +use std::fmt::{Error, Formatter}; +use wasm_bindgen::prelude::*; use yew::{html, Component, ComponentLink, Html, ShouldRender}; -struct Model {} +struct Model { + future_data: Option, +} enum Msg { - DoIt, + FutureFinished(String), +} + +/// An error that can never happen (because an instance of this can not be created). +#[derive(Debug, Clone, PartialEq)] +pub enum InfallibleFutureError {} +impl std::fmt::Display for InfallibleFutureError { + fn fmt(&self, _f: &mut Formatter<'_>) -> Result<(), Error> { + unimplemented!() + } } +impl std::error::Error for InfallibleFutureError {} impl Component for Model { // Some details omitted. Explore the examples to see more. @@ -12,14 +27,20 @@ impl Component for Model { type Message = Msg; type Properties = (); - fn create(_: Self::Properties, _: ComponentLink) -> Self { - Model {} + fn create(_: Self::Properties, link: ComponentLink) -> Self { + let future = async { + let x: Result = + Ok(Msg::FutureFinished("Hello Future World!".to_string())); + x + }; + link.send_future(future); + Model { future_data: None } } fn update(&mut self, msg: Self::Message) -> ShouldRender { match msg { - Msg::DoIt => { - // Update your model on events + FutureFinished(resolved_future) => { + self.future_data = Some(resolved_future); true } } @@ -27,14 +48,19 @@ impl Component for Model { fn view(&self) -> Html { html! { - // Render your model here - + if let Some(future_data) = &self.future_data { + html! { + &future_data + } + } else { + html! { + "no future yet" + } + } } } } -use wasm_bindgen::prelude::*; - #[wasm_bindgen] pub fn run_app() { yew::start_app::(); From 06fb65bc94535937992140c3dd11d77f0632ddd1 Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Sun, 3 Nov 2019 16:00:12 -0500 Subject: [PATCH 08/15] change example to fetch markdown instead of just resolving a string --- examples/futures/Cargo.toml | 14 ++++++++++- examples/futures/src/lib.rs | 47 ++++++++++++++++++++++++++++++++----- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/examples/futures/Cargo.toml b/examples/futures/Cargo.toml index 1b10661a945..fe10bf96a00 100644 --- a/examples/futures/Cargo.toml +++ b/examples/futures/Cargo.toml @@ -9,7 +9,19 @@ crate-type = ["cdylib", "rlib"] [dependencies] yew = { path = "../.." } -web-sys = "0.3.30" +wasm-bindgen-futures = "0.4.3" + +[dependencies.web-sys] +version = "0.3.30" +features = [ + 'Headers', + 'Request', + 'RequestInit', + 'RequestMode', + 'Response', + 'Window', +] +#web-sys = "0.3.30" [target.'cfg(all(target_arch = "wasm32", not(cargo_web)))'.dependencies] wasm-bindgen = "0.2.51" diff --git a/examples/futures/src/lib.rs b/examples/futures/src/lib.rs index b4871944103..56b4371042c 100644 --- a/examples/futures/src/lib.rs +++ b/examples/futures/src/lib.rs @@ -1,6 +1,10 @@ use crate::Msg::FutureFinished; use std::fmt::{Error, Formatter}; +use std::future::Future; use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; +use wasm_bindgen_futures::JsFuture; +use web_sys::{Request, RequestInit, RequestMode, Response, Window}; use yew::{html, Component, ComponentLink, Html, ShouldRender}; struct Model { @@ -13,13 +17,45 @@ enum Msg { /// An error that can never happen (because an instance of this can not be created). #[derive(Debug, Clone, PartialEq)] -pub enum InfallibleFutureError {} -impl std::fmt::Display for InfallibleFutureError { +pub struct FetchError { + err: JsValue, +} +impl std::fmt::Display for FetchError { fn fmt(&self, _f: &mut Formatter<'_>) -> Result<(), Error> { unimplemented!() } } -impl std::error::Error for InfallibleFutureError {} +impl std::error::Error for FetchError {} + +impl From for FetchError { + fn from(value: JsValue) -> Self { + FetchError { err: value } + } +} + +/// Gets the markdown from yew's readme. +/// +/// Consult the following for an example of the fetch api by the team behind web_sys: +/// https://rustwasm.github.io/wasm-bindgen/examples/fetch.html +async fn get_markdown() -> Result { + let mut opts = RequestInit::new(); + opts.method("GET"); + opts.mode(RequestMode::Cors); + + let request = Request::new_with_str_and_init( + "https://raw.githubusercontent.com/yewstack/yew/master/README.md", + &opts, + )?; + + let window: Window = web_sys::window().unwrap(); + let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; + assert!(resp_value.is_instance_of::()); + + let resp: Response = resp_value.dyn_into().unwrap(); + + let text = JsFuture::from(resp.text()?).await?; + Ok(text.as_string().unwrap()) +} impl Component for Model { // Some details omitted. Explore the examples to see more. @@ -29,9 +65,8 @@ impl Component for Model { fn create(_: Self::Properties, link: ComponentLink) -> Self { let future = async { - let x: Result = - Ok(Msg::FutureFinished("Hello Future World!".to_string())); - x + let md = get_markdown().await; + md.map(Msg::FutureFinished) }; link.send_future(future); Model { future_data: None } From f1777783fec0b6eb4744f362bf9600442acc270d Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Sun, 3 Nov 2019 16:00:37 -0500 Subject: [PATCH 09/15] remove unnneded import --- examples/futures/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/futures/src/lib.rs b/examples/futures/src/lib.rs index 56b4371042c..5142c3a04ce 100644 --- a/examples/futures/src/lib.rs +++ b/examples/futures/src/lib.rs @@ -1,6 +1,5 @@ use crate::Msg::FutureFinished; use std::fmt::{Error, Formatter}; -use std::future::Future; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_futures::JsFuture; From 038b18364e47d552cf03c4dda06a60767e5c52f2 Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Sun, 3 Nov 2019 20:21:09 -0500 Subject: [PATCH 10/15] Force users to handle errors in their future --- examples/futures/README | 9 ++++++++- examples/futures/src/lib.rs | 17 ++++++++++------- src/html/mod.rs | 22 +++++++++++----------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/examples/futures/README b/examples/futures/README index 89a159fe963..1d04a4ecf2a 100644 --- a/examples/futures/README +++ b/examples/futures/README @@ -1,6 +1,13 @@ +# Futures Example +This example shows off how to make a asynchronous fetch request using web_sys and Yew's futures support. + +Because this example uses features not allowed by cargo web, it cannot be included in the showcase, and must be built with a different toolchain instead. + ### How to run: ```sh rustup run nightly $HOME/.cargo/bin/wasm-pack build --target web && rollup ./main.js --format iife --file ./pkg/bundle.js && python -m SimpleHTTPServer 8080 ``` -It is expected that you have a setup with wasm-pack, rollup and python installed. +This will compile the project using nightly rust, bundle up the compiler output and static assets, and start a http server on port 8080 so you can access the example at localhost:8080. + +It is expected that you have a setup with wasm-pack, rollup, and python installed. Since wasm-pack doesn't have a nightly version at the moment, you have to hack around that by using `rustup run nightly`. diff --git a/examples/futures/src/lib.rs b/examples/futures/src/lib.rs index 5142c3a04ce..7a098a17b40 100644 --- a/examples/futures/src/lib.rs +++ b/examples/futures/src/lib.rs @@ -1,4 +1,3 @@ -use crate::Msg::FutureFinished; use std::fmt::{Error, Formatter}; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; @@ -11,7 +10,8 @@ struct Model { } enum Msg { - FutureFinished(String), + FetchSuccess(String), + FetchFailed(FetchError) } /// An error that can never happen (because an instance of this can not be created). @@ -20,8 +20,8 @@ pub struct FetchError { err: JsValue, } impl std::fmt::Display for FetchError { - fn fmt(&self, _f: &mut Formatter<'_>) -> Result<(), Error> { - unimplemented!() + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + std::fmt::Debug::fmt(&self.err, f) } } impl std::error::Error for FetchError {} @@ -64,8 +64,10 @@ impl Component for Model { fn create(_: Self::Properties, link: ComponentLink) -> Self { let future = async { - let md = get_markdown().await; - md.map(Msg::FutureFinished) + match get_markdown().await { + Ok(md) => Msg::FetchSuccess(md), + Err(err) => Msg::FetchFailed(err) + } }; link.send_future(future); Model { future_data: None } @@ -73,10 +75,11 @@ impl Component for Model { fn update(&mut self, msg: Self::Message) -> ShouldRender { match msg { - FutureFinished(resolved_future) => { + Msg::FetchSuccess(resolved_future) => { self.future_data = Some(resolved_future); true } + Msg::FetchFailed(_) => {false} } } diff --git a/src/html/mod.rs b/src/html/mod.rs index 951f9fa1ccb..66936d5fbd1 100644 --- a/src/html/mod.rs +++ b/src/html/mod.rs @@ -344,10 +344,12 @@ where #[cfg(all(target_arch = "wasm32", not(cargo_web)))] /// This method processes a Future that returns a message and sends it back to the component's /// loop. - pub fn send_future(&self, future: F) + /// + /// # Panics + /// If the future panics, then the promise will not resolve, and will leak. + pub fn send_future(&self, future: F) where - F: Future> + 'static, - E: std::error::Error, + F: Future + 'static, { use wasm_bindgen::JsValue; use wasm_bindgen_futures::future_to_promise; @@ -355,15 +357,13 @@ where let mut scope = self.scope.clone(); let js_future = async { - future - .await - .and_then(move |message| { - scope.send_message(message); - Ok(JsValue::NULL) - }) - .map_err(|e| JsValue::from_str(&format!("{}", e))) + let message = future.await; + // Force movement of the cloned scope into the async block. + let scope_send = move || scope.send_message(message); + scope_send(); + Ok(JsValue::NULL) }; - future_to_promise(js_future); // TODO should this return the Promise as a handle? + future_to_promise(js_future); } /// This method sends a message to this component immediately. From 69e4d68c780c65a0885319d8d7b9fd717d99c157 Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Sun, 3 Nov 2019 20:21:16 -0500 Subject: [PATCH 11/15] Force users to handle errors in their future --- examples/futures/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/futures/src/lib.rs b/examples/futures/src/lib.rs index 7a098a17b40..a540dc9ab6c 100644 --- a/examples/futures/src/lib.rs +++ b/examples/futures/src/lib.rs @@ -11,7 +11,7 @@ struct Model { enum Msg { FetchSuccess(String), - FetchFailed(FetchError) + FetchFailed(FetchError), } /// An error that can never happen (because an instance of this can not be created). @@ -66,7 +66,7 @@ impl Component for Model { let future = async { match get_markdown().await { Ok(md) => Msg::FetchSuccess(md), - Err(err) => Msg::FetchFailed(err) + Err(err) => Msg::FetchFailed(err), } }; link.send_future(future); @@ -79,7 +79,7 @@ impl Component for Model { self.future_data = Some(resolved_future); true } - Msg::FetchFailed(_) => {false} + Msg::FetchFailed(_) => false, } } From 6bef04c5cb436962adb069fc807847794013713d Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Sun, 3 Nov 2019 20:53:29 -0500 Subject: [PATCH 12/15] cleanup and introduction of FetchState --- examples/futures/Cargo.toml | 4 +- examples/futures/{README => README.md} | 0 examples/futures/src/lib.rs | 72 +++++++++++++++----------- src/html/mod.rs | 2 +- 4 files changed, 46 insertions(+), 32 deletions(-) rename examples/futures/{README => README.md} (100%) diff --git a/examples/futures/Cargo.toml b/examples/futures/Cargo.toml index fe10bf96a00..84052fb3b42 100644 --- a/examples/futures/Cargo.toml +++ b/examples/futures/Cargo.toml @@ -3,6 +3,9 @@ name = "futures" version = "0.1.0" authors = ["Henry Zimmerman "] edition = "2018" +description = "web_sys fetching and futures demonstration" +license = "MIT/Apache" +repository = "https://github.com/yewstack/yew" [lib] crate-type = ["cdylib", "rlib"] @@ -21,7 +24,6 @@ features = [ 'Response', 'Window', ] -#web-sys = "0.3.30" [target.'cfg(all(target_arch = "wasm32", not(cargo_web)))'.dependencies] wasm-bindgen = "0.2.51" diff --git a/examples/futures/README b/examples/futures/README.md similarity index 100% rename from examples/futures/README rename to examples/futures/README.md diff --git a/examples/futures/src/lib.rs b/examples/futures/src/lib.rs index a540dc9ab6c..a45db60ab8d 100644 --- a/examples/futures/src/lib.rs +++ b/examples/futures/src/lib.rs @@ -1,3 +1,4 @@ +use crate::Msg::SetMarkdownFetchState; use std::fmt::{Error, Formatter}; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; @@ -5,15 +6,6 @@ use wasm_bindgen_futures::JsFuture; use web_sys::{Request, RequestInit, RequestMode, Response, Window}; use yew::{html, Component, ComponentLink, Html, ShouldRender}; -struct Model { - future_data: Option, -} - -enum Msg { - FetchSuccess(String), - FetchFailed(FetchError), -} - /// An error that can never happen (because an instance of this can not be created). #[derive(Debug, Clone, PartialEq)] pub struct FetchError { @@ -32,6 +24,14 @@ impl From for FetchError { } } +/// The possible states a fetch request can be in. +pub enum FetchState { + NotFetching, + Fetching, + Success(T), + Failed(FetchError), +} + /// Gets the markdown from yew's readme. /// /// Consult the following for an example of the fetch api by the team behind web_sys: @@ -56,6 +56,16 @@ async fn get_markdown() -> Result { Ok(text.as_string().unwrap()) } +struct Model { + markdown: FetchState, + link: ComponentLink, +} + +enum Msg { + SetMarkdownFetchState(FetchState), + GetMarkdown, +} + impl Component for Model { // Some details omitted. Explore the examples to see more. @@ -63,37 +73,39 @@ impl Component for Model { type Properties = (); fn create(_: Self::Properties, link: ComponentLink) -> Self { - let future = async { - match get_markdown().await { - Ok(md) => Msg::FetchSuccess(md), - Err(err) => Msg::FetchFailed(err), - } - }; - link.send_future(future); - Model { future_data: None } + Model { + markdown: FetchState::NotFetching, + link, + } } fn update(&mut self, msg: Self::Message) -> ShouldRender { match msg { - Msg::FetchSuccess(resolved_future) => { - self.future_data = Some(resolved_future); + Msg::SetMarkdownFetchState(fetch_state) => { + self.markdown = fetch_state; true } - Msg::FetchFailed(_) => false, + Msg::GetMarkdown => { + let future = async { + match get_markdown().await { + Ok(md) => Msg::SetMarkdownFetchState(FetchState::Success(md)), + Err(err) => Msg::SetMarkdownFetchState(FetchState::Failed(err)), + } + }; + self.link.send_future(future); + self.link + .send_self(SetMarkdownFetchState(FetchState::Fetching)); + false + } } } fn view(&self) -> Html { - html! { - if let Some(future_data) = &self.future_data { - html! { - &future_data - } - } else { - html! { - "no future yet" - } - } + match &self.markdown { + FetchState::NotFetching => html! {}, + FetchState::Fetching => html! {"Fetching"}, + FetchState::Success(data) => html! {&data}, + FetchState::Failed(err) => html! {&err}, } } } diff --git a/src/html/mod.rs b/src/html/mod.rs index 66936d5fbd1..9848aa97e44 100644 --- a/src/html/mod.rs +++ b/src/html/mod.rs @@ -357,7 +357,7 @@ where let mut scope = self.scope.clone(); let js_future = async { - let message = future.await; + let message: COMP::Message = future.await; // Force movement of the cloned scope into the async block. let scope_send = move || scope.send_message(message); scope_send(); From c350092477ae6b1eb55402ee24d3b7145910ae74 Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Sun, 10 Nov 2019 17:18:31 -0500 Subject: [PATCH 13/15] respond to feedback --- examples/futures/README.md | 7 ++++--- examples/futures/src/lib.rs | 12 +++++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/examples/futures/README.md b/examples/futures/README.md index 1d04a4ecf2a..9ef8e682c1e 100644 --- a/examples/futures/README.md +++ b/examples/futures/README.md @@ -4,10 +4,11 @@ This example shows off how to make a asynchronous fetch request using web_sys an Because this example uses features not allowed by cargo web, it cannot be included in the showcase, and must be built with a different toolchain instead. ### How to run: +This example requires rustc v1.39.0 or above to compile due to its use of async/.await syntax. + ```sh -rustup run nightly $HOME/.cargo/bin/wasm-pack build --target web && rollup ./main.js --format iife --file ./pkg/bundle.js && python -m SimpleHTTPServer 8080 +wasm-pack build --target web && rollup ./main.js --format iife --file ./pkg/bundle.js && python -m SimpleHTTPServer 8080 ``` -This will compile the project using nightly rust, bundle up the compiler output and static assets, and start a http server on port 8080 so you can access the example at localhost:8080. +This will compile the project, bundle up the compiler output and static assets, and start a http server on port 8080 so you can access the example at localhost:8080. It is expected that you have a setup with wasm-pack, rollup, and python installed. -Since wasm-pack doesn't have a nightly version at the moment, you have to hack around that by using `rustup run nightly`. diff --git a/examples/futures/src/lib.rs b/examples/futures/src/lib.rs index a45db60ab8d..6fcfcba9622 100644 --- a/examples/futures/src/lib.rs +++ b/examples/futures/src/lib.rs @@ -6,7 +6,7 @@ use wasm_bindgen_futures::JsFuture; use web_sys::{Request, RequestInit, RequestMode, Response, Window}; use yew::{html, Component, ComponentLink, Html, ShouldRender}; -/// An error that can never happen (because an instance of this can not be created). +/// Something wrong has occurred while fetching an external resource. #[derive(Debug, Clone, PartialEq)] pub struct FetchError { err: JsValue, @@ -32,11 +32,11 @@ pub enum FetchState { Failed(FetchError), } -/// Gets the markdown from yew's readme. +/// Gets the markdown from Yew's readme. /// /// Consult the following for an example of the fetch api by the team behind web_sys: /// https://rustwasm.github.io/wasm-bindgen/examples/fetch.html -async fn get_markdown() -> Result { +async fn fetch_markdown() -> Result { let mut opts = RequestInit::new(); opts.method("GET"); opts.mode(RequestMode::Cors); @@ -87,7 +87,7 @@ impl Component for Model { } Msg::GetMarkdown => { let future = async { - match get_markdown().await { + match fetch_markdown().await { Ok(md) => Msg::SetMarkdownFetchState(FetchState::Success(md)), Err(err) => Msg::SetMarkdownFetchState(FetchState::Failed(err)), } @@ -102,7 +102,9 @@ impl Component for Model { fn view(&self) -> Html { match &self.markdown { - FetchState::NotFetching => html! {}, + FetchState::NotFetching => { + html! {} + } FetchState::Fetching => html! {"Fetching"}, FetchState::Success(data) => html! {&data}, FetchState::Failed(err) => html! {&err}, From fc7cff52569e772bc8bbac3deab252d1714ae101 Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Sun, 10 Nov 2019 17:21:01 -0500 Subject: [PATCH 14/15] update comment --- examples/futures/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/futures/src/lib.rs b/examples/futures/src/lib.rs index 6fcfcba9622..5f5e22b832f 100644 --- a/examples/futures/src/lib.rs +++ b/examples/futures/src/lib.rs @@ -32,7 +32,7 @@ pub enum FetchState { Failed(FetchError), } -/// Gets the markdown from Yew's readme. +/// Fetches markdown from Yew's README.md. /// /// Consult the following for an example of the fetch api by the team behind web_sys: /// https://rustwasm.github.io/wasm-bindgen/examples/fetch.html From b8e3720cda8d6e6eadac1289d498de00d5c0698b Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Sun, 10 Nov 2019 20:42:59 -0500 Subject: [PATCH 15/15] fix cargo.toml dependency issues --- Cargo.toml | 7 ++++--- examples/Cargo.toml | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4532efc2963..b3cf4bc69a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,10 @@ yew-macro = { version = "0.10.0", path = "crates/macro" } [target.'cfg(all(target_arch = "wasm32", not(cargo_web)))'.dependencies] wasm-bindgen = "0.2.54" +wasm-bindgen-futures = "0.4.4" + +[target.'cfg(all(target_arch = "wasm32", not(cargo_web)))'.dev-dependencies] +wasm-bindgen-test = "0.3.4" [target.'cfg(target_os = "emscripten")'.dependencies] ryu = "=1.0.0" # 1.0.1 breaks emscripten @@ -48,9 +52,6 @@ serde_derive = "1" trybuild = "1.0" rustversion = "0.1" -[target.'cfg(all(target_arch = "wasm32", not(cargo_web)))'.dev-dependencies] -wasm-bindgen-test = "0.3.4" - [features] default = [] doc_test = [] diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 03ce18b1216..8d30e1c7a2d 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -7,6 +7,7 @@ members = [ "node_refs", "file_upload", "fragments", + "futures", "game_of_life", "inner_html", "js_callback",