Skip to content

Commit

Permalink
Added example for weather report using rust and webassembly
Browse files Browse the repository at this point in the history
Minor refactoring

Updated readme file
  • Loading branch information
Ayush Kumar Mishra committed Jun 24, 2020
1 parent 41409d2 commit ad48676
Show file tree
Hide file tree
Showing 12 changed files with 6,323 additions and 0 deletions.
23 changes: 23 additions & 0 deletions examples/weather_report/Cargo.toml
@@ -0,0 +1,23 @@
[package]
name = "rust-webassembly-weather-reports"
description = "Weather Information System- Get the mood of your city on one click using Rust and Webassembly"
version = "0.1.1"
authors = ["Ayush <ayushmishra2005@gmail.com>"]
categories = ["wasm"]
readme = "README.md"
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
chrono = "0.4.11"
reqwest = "0.10.6"
wasm-bindgen-futures = "0.4.1"
json= "*"
wasm-bindgen = "0.2.63"
gloo = "0.2.1"

[dependencies.web-sys]
version = "0.3.40"
features = ["Document", "Element", "HtmlElement", "Window"]
16 changes: 16 additions & 0 deletions examples/weather_report/README.md
@@ -0,0 +1,16 @@
# web-sys: Weather report

[View documentation for this example online][dox] or [View compiled example
online][compiled]

[compiled]: https://rustwasm.github.io/wasm-bindgen/exbuild/weather_report/
[dox]: https://rustwasm.github.io/docs/wasm-bindgen/examples/weather_report.html

You can build the example locally with:

```
$ npm install
$ npm start
```

and then visiting http://localhost:8080 in a browser should run the example!
1 change: 1 addition & 0 deletions examples/weather_report/js/index.js
@@ -0,0 +1 @@
import("../pkg/index.js").catch(console.error);
18 changes: 18 additions & 0 deletions examples/weather_report/package.json
@@ -0,0 +1,18 @@
{
"author": "Ayush <ayushmishra2005@gmail.com>",
"name": "rust-webassembly-weather-reports",
"version": "0.1.1",
"scripts": {
"build": "rimraf dist pkg && webpack",
"start": "rimraf dist pkg && webpack-dev-server --open -d",
"test": "cargo test && wasm-pack test --headless"
},
"devDependencies": {
"@wasm-tool/wasm-pack-plugin": "^1.1.0",
"copy-webpack-plugin": "^5.0.3",
"webpack": "^4.42.0",
"webpack-cli": "^3.3.3",
"webpack-dev-server": "^3.7.1",
"rimraf": "^3.0.0"
}
}
248 changes: 248 additions & 0 deletions examples/weather_report/src/lib.rs
@@ -0,0 +1,248 @@
extern crate chrono;
extern crate reqwest;

use chrono::prelude::DateTime;
use chrono::Utc;
use std::time::{Duration, UNIX_EPOCH};

use gloo::events::EventListener;
use json::JsonValue;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::spawn_local;
use web_sys::Document;
use web_sys::Element;
use web_sys::HtmlInputElement;

#[wasm_bindgen(module = "/util.js")]
extern "C" {
fn initialize(lat: f64, lon: f64);
}

// This is like the `main` function, except for JavaScript.
#[wasm_bindgen(start)]
pub fn main_js() -> Result<(), JsValue> {
let window = web_sys::window().expect("no global `window` exists");
let document = window.document().expect("should have a document on window");
let body = document.body().expect("document should have a body");

// Manufacture the element we're gonna append
let search_div = create_div(&document, "search", "ReportStyles-secondDiv col-md-12");
let input_box = create_input_box(&document);
search_div.append_child(&input_box)?;
let submit_box = create_submit_box(&document);
let temp_div = create_div(
&document,
"tempDetail",
"ReportStyles-mainContainer col-md-12 maincontainer",
);
let first_div = document.create_element("div")?;
let second_div = create_div(&document, "second_div", "col-md-6");
let third_div = create_div(&document, "third_div", "ReportStyles-innerDiv");
let fourth_div = create_div(&document, "cityName", "ReportStyles-city");

let table_div = document.create_element("table")?;
table_div.set_class_name("ReportStyles-table table-bordered table-striped");

let tbody_div = document.create_element("tbody")?;

let ftr_div = document.create_element("tr")?;

let ftd_div = document.create_element("td")?;
ftd_div.set_class_name(" ReportStyles-firstTd");

let img_div = document.create_element("div")?;
img_div.set_id("temp");

let std_div = document.create_element("td")?;
std_div.set_class_name(" ReportStyles-secondTd");

let weather_div = document.create_element("div")?;
weather_div.set_id("weather");

ftr_div.append_child(&ftd_div)?;
ftd_div.append_child(&img_div)?;
ftr_div.append_child(&std_div)?;
std_div.append_child(&weather_div)?;

let str_div = document.create_element("tr")?;
let ptd_div = document.create_element("td")?;
ptd_div.set_class_name(" ReportStyles-td");
ptd_div.set_inner_html("Pressure");
let sptd_div = document.create_element("td")?;
sptd_div.set_id("pressure");
str_div.append_child(&ptd_div)?;
str_div.append_child(&sptd_div)?;

let ttr_div = document.create_element("tr")?;
let htd_div = document.create_element("td")?;
htd_div.set_class_name(" ReportStyles-td");
htd_div.set_inner_html("Humidity");
let shtd_div = document.create_element("td")?;
shtd_div.set_id("humidity");
ttr_div.append_child(&htd_div)?;
ttr_div.append_child(&shtd_div)?;

let sunr_tr_div = document.create_element("tr")?;
let sunr_td_div = document.create_element("td")?;
sunr_td_div.set_class_name(" ReportStyles-td");
sunr_td_div.set_inner_html("Sunrise[UTC]");
let sunr_s_td_div = document.create_element("td")?;
sunr_s_td_div.set_id("sunrise");
sunr_tr_div.append_child(&sunr_td_div)?;
sunr_tr_div.append_child(&sunr_s_td_div)?;

let suns_tr_div = document.create_element("tr")?;
let suns_td_div = document.create_element("td")?;
suns_td_div.set_class_name(" ReportStyles-td");
suns_td_div.set_inner_html("Sunset[UTC]");
let suns_s_td_div = document.create_element("td")?;
suns_s_td_div.set_id("sunset");
suns_tr_div.append_child(&suns_td_div)?;
suns_tr_div.append_child(&suns_s_td_div)?;

let geo_tr_div = document.create_element("tr")?;
let geo_htd_div = document.create_element("td")?;
geo_htd_div.set_class_name(" ReportStyles-td");
geo_htd_div.set_inner_html("Geo coords");
let geo_shtd_div = document.create_element("td")?;
geo_shtd_div.set_id("geocoords");
geo_tr_div.append_child(&geo_htd_div)?;
geo_tr_div.append_child(&geo_shtd_div)?;

tbody_div.append_child(&ftr_div)?;
tbody_div.append_child(&str_div)?;
tbody_div.append_child(&ttr_div)?;
tbody_div.append_child(&sunr_tr_div)?;
tbody_div.append_child(&suns_tr_div)?;
tbody_div.append_child(&geo_tr_div)?;

table_div.append_child(&tbody_div)?;
third_div.append_child(&fourth_div)?;
third_div.append_child(&table_div)?;

let map_div = document.create_element("div")?;
map_div.set_class_name("col-md-6");
let map_canvas_div = document.create_element("div")?;
map_canvas_div.set_class_name(" ReportStyles-mapCanvas");
map_canvas_div.set_id("map_canvas");
map_div.append_child(&map_canvas_div)?;

second_div.append_child(&third_div)?;
first_div.append_child(&second_div)?;
first_div.append_child(&map_div)?;

temp_div.append_child(&first_div)?;
search_div.append_child(&submit_box)?;
body.append_child(&search_div)?;
body.append_child(&temp_div)?;

let on_click = EventListener::new(&submit_box, "click", move |_event| {
let input_value = document
.get_element_by_id("name")
.unwrap()
.dyn_into::<HtmlInputElement>()
.unwrap()
.value();
let temp_d = temp_div.clone();
let city = fourth_div.clone();
let image = img_div.clone();
let weather = weather_div.clone();
let pressure = sptd_div.clone();
let humidity = shtd_div.clone();
let sunrise = sunr_s_td_div.clone();
let sunset = suns_s_td_div.clone();
let geo = geo_shtd_div.clone();
let input_value: &'static _ = Box::leak(Box::new(input_value));
let response = get_response(&input_value);
spawn_local(async move {
let parsed = response.await;
let lon = (&parsed["coord"]["lon"]).to_owned().as_f64().unwrap();
let lat = (&parsed["coord"]["lat"]).to_owned().as_f64().unwrap();
initialize(lat, lon);
let city_name: &str = &parsed["name"].to_owned().to_string();
let country_name: &str = &parsed["sys"]["country"].to_owned().to_string();
let place = [city_name, ",", country_name].concat();
let icon = &parsed["weather"][0]["icon"].to_owned().to_string();
let src = [
"<img src='http://openweathermap.org/img/w/",
icon,
".png'>",
" ",
]
.concat();
let temp = ((&parsed["main"]["temp"]).to_owned().as_f64().unwrap() - 273.15) as i64;

let content = [src, temp.to_string()].concat();
let p: &str = &parsed["main"]["pressure"].to_owned().to_string();
let h: &str = &parsed["main"]["humidity"].to_owned().to_string();
let sun_r = ((&parsed["sys"]["sunrise"]).to_owned().as_f64().unwrap()) as u64;
let sun_s = ((&parsed["sys"]["sunset"]).to_owned().as_f64().unwrap()) as u64;
temp_d.set_attribute("style", "display: block");
city.set_inner_html(&place);
image.set_inner_html(&content);
weather.set_inner_html(&parsed["weather"][0]["main"].to_owned().to_string());
pressure.set_inner_html(&([p, " hpa"].concat()));
humidity.set_inner_html(&([h, "%"].concat()));
sunrise.set_inner_html(&get_time(sun_r));
sunset.set_inner_html(&get_time(sun_s));
geo.set_inner_html(&(["[", &lon.to_string(), ",", &lat.to_string(), "]"].concat()));
});
});

// When a Closure is dropped it will invalidate the associated JS closure.
// Here we want JS callback to be alive for the entire duration of the program.
// So we used `forget` leak this instance of Closure.
// It should be used sparingly to ensure the memory leak doesn't affect the program too much.
on_click.forget();
Ok(())
}

fn create_div(document: &Document, id: &str, class: &str) -> Element {
let div = document.create_element("div").unwrap();
div.set_id(id);
div.set_class_name(class);
div
}

fn create_submit_box(document: &Document) -> Element {
let submit_box: Element = document.create_element("input").unwrap();
submit_box.set_attribute("type", "button");
submit_box.set_attribute("value", "Search");
submit_box.set_attribute("name", "submit");
submit_box.set_id("submit");
submit_box.set_class_name(" ReportStyles-bootstrapButton btn btn-info");
submit_box
}

fn create_input_box(document: &Document) -> Element {
let input_box = document.create_element("input").unwrap();
input_box.set_attribute("name", "name");
input_box.set_attribute("value", "Delhi");
input_box.set_attribute("type", "text");
input_box.set_attribute("placeholder", "Type city name here");
input_box.set_id("name");
input_box.set_class_name("ReportStyles-search");
input_box
}

// Get response from weather api
async fn get_response(location: &str) -> JsonValue {
let url1 = "http://api.openweathermap.org/data/2.5/weather?q=";
let url2 = "&appid=<apiKey>";

let url = [url1, location, url2].concat();

let resp = reqwest::get(&url).await.unwrap().text().await.unwrap();

json::parse(&resp).unwrap()
}

// Convert millisecond into UTC date
fn get_time(millis: u64) -> String {
let d = UNIX_EPOCH + Duration::from_secs(millis);
// Create DateTime from SystemTime
let datetime = DateTime::<Utc>::from(d);
// Formats the combined date and time with the specified format string.
datetime.format("%H:%M:%S").to_string()
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit ad48676

Please sign in to comment.