use std::error::Error; use std::fmt::{self, Debug, Display, Formatter}; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_futures::JsFuture; use web_sys::{Request, RequestInit, RequestMode, Response}; use yew::{html, Component, Context, Html}; mod markdown; const MARKDOWN_URL: &str = "https://raw.githubusercontent.com/yewstack/yew/master/README.md"; const INCORRECT_URL: &str = "https://raw.githubusercontent.com/yewstack/yew/master/README.md.404"; /// Something wrong has occurred while fetching an external resource. #[derive(Debug, Clone, PartialEq)] pub struct FetchError { err: JsValue, } impl Display for FetchError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { Debug::fmt(&self.err, f) } } impl Error for FetchError {} impl From for FetchError { fn from(value: JsValue) -> Self { Self { err: value } } } /// The possible states a fetch request can be in. pub enum FetchState { NotFetching, Fetching, Success(T), Failed(FetchError), } /// Fetches markdown from Yew's README.md. /// /// Consult the following for an example of the fetch api by the team behind web_sys: /// https://wasm-bindgen.github.io/wasm-bindgen/examples/fetch.html async fn fetch_markdown(url: &'static str) -> Result { let opts = RequestInit::new(); opts.set_method("GET"); opts.set_mode(RequestMode::Cors); let request = Request::new_with_str_and_init(url, &opts)?; let window = gloo::utils::window(); let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; let resp: Response = resp_value.dyn_into().unwrap(); let text = JsFuture::from(resp.text()?).await?; Ok(text.as_string().unwrap()) } enum Msg { SetMarkdownFetchState(FetchState), GetMarkdown, GetError, } struct App { markdown: FetchState, } impl Component for App { type Message = Msg; type Properties = (); fn create(_ctx: &Context) -> Self { Self { markdown: FetchState::NotFetching, } } fn update(&mut self, ctx: &Context, msg: Self::Message) -> bool { match msg { Msg::SetMarkdownFetchState(fetch_state) => { self.markdown = fetch_state; true } Msg::GetMarkdown => { ctx.link().send_future(async { match fetch_markdown(MARKDOWN_URL).await { Ok(md) => Msg::SetMarkdownFetchState(FetchState::Success(md)), Err(err) => Msg::SetMarkdownFetchState(FetchState::Failed(err)), } }); ctx.link() .send_message(Msg::SetMarkdownFetchState(FetchState::Fetching)); false } Msg::GetError => { ctx.link().send_future(async { match fetch_markdown(INCORRECT_URL).await { Ok(md) => Msg::SetMarkdownFetchState(FetchState::Success(md)), Err(err) => Msg::SetMarkdownFetchState(FetchState::Failed(err)), } }); ctx.link() .send_message(Msg::SetMarkdownFetchState(FetchState::Fetching)); false } } } fn view(&self, ctx: &Context) -> Html { match &self.markdown { FetchState::NotFetching => html! { <> }, FetchState::Fetching => html! { "Fetching" }, FetchState::Success(data) => html! { markdown::render_markdown(data) }, FetchState::Failed(err) => html! { err }, } } } fn main() { yew::Renderer::::new().render(); }