├── .gitignore
├── README.md
├── package.json
├── public
├── bundle.css
├── bundle.js
├── favicon.png
├── global.css
└── index.html
├── scripts
└── wbg_to_wasi.js
├── src
├── App.svelte
├── greeting
│ ├── Cargo.toml
│ └── src
│ │ └── lib.rs
├── js-wasi
│ └── jsWasi.js
├── main.js
└── rust-wasi
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── LICENSE
│ ├── README.md
│ └── src
│ └── main.rs
├── webpack.config.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /public/bundle.*
3 | /public/wasm/
4 |
5 | /src/rust-wasi*js
6 |
7 | .DS_Store
8 |
9 | target/
10 | Cargo.lock
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Svelte Rust/WASI in Browser Example
2 |
3 | This example uses Rust Web Assembly compiled for WASI (the Web Assembly
4 | System Interface) running in the browser using [WasmerJS]("https://github.com/wasmerio/wasmer-js), and uses `wasm-bindgen` to make it easy to pass data from JavaScript to Rust and vice versa.
5 |
6 | Rust is compiled for target `wasm32-wasi` and bindings are generated using
7 | `wasm-bindgen` plus a small amount of post-processing to adapt the bindings for
8 | WASI.
9 |
10 | For a non-Rust example and Svelte + Wasmer/WASI template see [simple-svelte-wasmer-webpack](https://github.com/happybeing/simple-svelte-wasmer-webpack) which was the starting point for this project.
11 |
12 | ## Features
13 | - A Svelte WASM/WASI app with Rust subsystem (using target `wasm32-wasi`)
14 | - JavaScript and Rust both accessing the WasmerJS/wasmFS filesystem
15 | - Calling Rust from JavaScript and vice versa using `wasm-bindgen+`
16 | - Passing and returning JavaScript and Rust native types with no mucking about
17 | - Passing and returning JavaScript objects and arrays to/from Rust structs
18 |
19 | **Help!** I'd like to add more examples of passing variables using `wasm-bindgen` so if you know how to do something I'm not showing yet please open an issue or PR (see Rust [main.rs](https://github.com/happybeing/svelte-wasi-with-rust/blob/main/src/rust-wasi/src/main.rs)).
20 |
21 | Note: `wasm-bindgen+` indicates a small amount of post-processing to make the `wasm-bindgen` output suitable for use with WasmerJS in the browser.
22 | # How This Works
23 | Most of the work is done by WasmerJS and `wasm-bindgen` (CLI because `wasm-pack` does not support WASI at this time). It was tricky to work out how to do this, but the solution is straightforward.
24 |
25 | The main difficulty is that the JavaScript bindings generated by `wasm-bindgen` import the corresponding `wasm-bindgen` WASM output and this fails because the imports needed by the WASM are not available when the WASI is initialised.
26 |
27 | ## Modified Build Process
28 | A script (scripts/wbg_to_wasi.js) has been provided which creates a modified version of the JavaScript generated by `wasm-bindgen`. You don't need to invoke the script yourself because it is included in the build commands which handle everything. So the following is just to document how it works.
29 |
30 | In the following example the `wasm-bindgen` generated JavaScript is in rust-wasi_bg.js, which the script copies to a new file rust-wasi_bg_wasi.js with changes. Firstly the import of the WASM file:
31 | ```javascript
32 | import * as wasm from './rust-wasi_bg.wasm';
33 | ```
34 | is replaced by a way to provide the imports after the bindings module has loaded:
35 | ```javascript
36 | let wasm;
37 | export function setBindingsWasm(w){ wasm = w; }
38 | ```
39 | Secondly, imported JavaScript module names are changed to a relative path (because you can't specify a relative path with the `#[wasm_bindgen]` macro). So a line such as:
40 | ```javascript
41 | import { js_test_n } from 'test';
42 | ```
43 | is replaced with:
44 | ```javascript
45 | import { js_test_n } from './js-wasi/test.js';
46 | ```
47 | If necessary you can customise the path by editing the command which invokes the script (see package.json). I suspect there's a better way to handle this second modification - PR's welcome!
48 |
49 | ## Implemention in the App
50 | Although this repository implements the app using Svelte, it follows the WasmerJS example documentation closely and should be simple to apply in any web framework.
51 |
52 | IMPORTANT: your Rust `main()` function must be empty if you want to call Rust functions from JavaScript under Wasmer WASI. As in:
53 | ```rust
54 | main(){}
55 | ```
56 |
57 | To make use of this, your web app does three things (see for example [src/App.svelte](https://github.com/happybeing/svelte-wasi-with-rust/blob/main/src/App.svelte)).
58 |
59 | First import the modified bindings as `wasm`:
60 | ```javascript
61 | import * as wasm from './rust-wasi_bg_wasi.js';
62 | ```
63 |
64 | Provide modified bindings along with any other imports (in this case a `test` module, 'test.js') when initialising the Web Assembly. Note that you must use the name of the original bindings module './rust-wasi_bg.js':
65 | ```javascript
66 | imports = {test,...{'./rust-wasi_bg.js': await import('./rust-wasi_bg_wasi')},...imports};
67 |
68 | let instance = await WebAssembly.instantiate(wasmModule, {
69 | ...imports
70 | });
71 | ```
72 | Finally, provide the wasm module to the JavaScript bindings immediately after the call to `wasi.start(...)`:
73 | ```javascript
74 | wasi.start(instance);
75 | wasm.setBindingsWasm(instance.exports);
76 | ```
77 |
78 | That's it. From there on you can make calls to WASM via the bindings using the `wasm` import './rust-wasi_bg_wasi.js'. For example:
79 | ```javascript
80 | wasm.rust_print_bg_n(256); // Call Rust, print number to stdout
81 | ```
82 | # Setup
83 |
84 | ## Prerequisites
85 | As well as NodeJS and Rust you need the Rust `wasm32-wasi` target:
86 | ```bash
87 | rustup target add wasm32-wasi
88 | ```
89 | And the `wasm-bindgen` CLI:
90 | ```bash
91 | cargo install wasm-bindgen-cli
92 | ```
93 | **Note:** make sure `wasm-bindgen --version` matches the version of the `wasm-bingen` module in `Cargo.toml` (/src/rust-rust-wasi/Cargo.toml). If the versions don't match after doing `cargo install wasm-bindgen-cli && wasm-bindgen --version`, modify the version referred to in `Cargo.toml` to match the CLI.
94 |
95 | You should only need the first and second parts of the version to match, so for example `wasm-bindgen --version` of 'wasm-bindgen 0.2.69' should work fine with Cargo.toml 'wasm-bindgen = "^0.2"').
96 | ## Get the Code
97 | If you don't have `yarn` use `npm run` instead of `yarn` in the following:
98 | ```bash
99 | git clone https://github.com/happybeing/svelte-wasi-with-rust
100 | cd svelte-wasi-with-rust
101 | yarn && yarn dev-wasm-bindgen && yarn dev
102 | ```
103 | Once the development build completes you can visit the app at localhost:8080.
104 |
105 | # Builds
106 | ## Release Builds
107 | To build for release:
108 | ```bash
109 | yarn build
110 | ```
111 | To test, use `yarn serve public` and visit `localhost:5000`
112 |
113 | To deploy, upload everything in /public
114 | ## Development Builds
115 |
116 | The App code is in `src/App.svelte` with Rust and JavaScript subsystems in `src/rust-wasi` and `src/js-wasi`.
117 |
118 | To re-build the wasm and serve the result:
119 | ```bash
120 | yarn dev-wasm-bindgen
121 | yarn dev
122 | ```
123 |
124 | If you have `inotifywait` (e.g. on Linux) you can use `yarn dev` and `yarn watch-wasm-bindgen` together, and changes to any part of the app including the Rust subsystem will automatically re-build everything and reload the browser.
125 |
126 | To do this, in one terminal watch and re-build the app with:
127 | ```bash
128 | yarn dev
129 | ```
130 | Then in another terminal, watch and re-build the Rust subsystem with:
131 | ```bash
132 | yarn watch-wasm-bindgen
133 | ```
134 |
135 | If you're using VSCode, we recommend installing the offical Svelte extension as well as the offical Rust extension. If you are using other editors, your may need to install a plugin in order to get syntax highlighting and intellisense for both Svelte and Rust.
136 |
137 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "svelte-wasi-with-rust",
3 | "version": "1.1.0",
4 | "scripts": {
5 | "build": "rm -f public/wasm/* && yarn build-wasm-bindgen && yarn build-app",
6 | "build-app": "cross-env NODE_ENV=production webpack",
7 | "build-wasm-bindgen": "yarn build-wasm && wasm-bindgen --no-typescript src/rust-wasi/target/wasm32-wasi/release/rust-wasi.wasm --browser --out-dir . && yarn wbg-to-wasi && mv *.wasm public/wasm/ && mv rust-* src",
8 | "build-wasm": "cd src/rust-wasi/ && cargo build --release --target=wasm32-wasi && mkdir -p ../../public/wasm",
9 | "dev": "webpack-dev-server --watch-content-base --content-base public",
10 | "dev-wasm": "cd src/rust-wasi/ && cargo build --target=wasm32-wasi && mkdir -p ../../public/wasm",
11 | "dev-wasm-bindgen": "yarn dev-wasm && wasm-bindgen --no-typescript src/rust-wasi/target/wasm32-wasi/debug/rust-wasi.wasm --debug --keep-debug --out-dir . && yarn wbg-to-wasi && mv *.wasm public/wasm/ && mv rust-* src",
12 | "watch-wasm-bindgen": "while true ; do inotifywait -qm --event modify @src/rust-wasi/target -r --format '' src/rust-wasi/ | yarn dev-wasm-bindgen; done",
13 | "wbg-to-wasi": "./scripts/wbg_to_wasi.js --path ./js-wasi/ rust-wasi_bg.js rust-wasi_bg_wasi.js"
14 | },
15 | "devDependencies": {
16 | "@wasmer/wasi": "^0.12.0",
17 | "@wasmer/wasmfs": "^0.12.0",
18 | "cross-env": "^5.2.0",
19 | "css-loader": "^2.1.1",
20 | "mini-css-extract-plugin": "^0.6.0",
21 | "serve": "^11.0.0",
22 | "style-loader": "^0.23.1",
23 | "svelte": "^3.0.0",
24 | "svelte-loader": "2.13.3",
25 | "webpack": "^4.30.0",
26 | "webpack-cli": "^3.3.0",
27 | "webpack-dev-server": "^3.3.1",
28 | "yargs": "^16.2.0"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/public/bundle.css:
--------------------------------------------------------------------------------
1 | main.svelte-lgrwi{text-align:left;padding:1em;max-width:240px;margin:0 auto}h1.svelte-lgrwi{text-align:center;color:#ff3e00;text-transform:uppercase;font-size:4em;font-weight:100}@media(min-width: 640px){main.svelte-lgrwi{max-width:none}}
2 |
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/happybeing/svelte-wasi-with-rust/db8cc829f0087c66b208e6ebd29307d94716362a/public/favicon.png
--------------------------------------------------------------------------------
/public/global.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | position: relative;
3 | width: 100%;
4 | height: 100%;
5 | }
6 |
7 | body {
8 | color: #333;
9 | margin: 0;
10 | padding: 8px;
11 | box-sizing: border-box;
12 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
13 | }
14 |
15 | a {
16 | color: rgb(0,100,200);
17 | text-decoration: none;
18 | }
19 |
20 | a:hover {
21 | text-decoration: underline;
22 | }
23 |
24 | a:visited {
25 | color: rgb(0,80,160);
26 | }
27 |
28 | label {
29 | display: block;
30 | }
31 |
32 | input, button, select, textarea {
33 | font-family: inherit;
34 | font-size: inherit;
35 | padding: 0.4em;
36 | margin: 0 0 0.5em 0;
37 | box-sizing: border-box;
38 | border: 1px solid #ccc;
39 | border-radius: 2px;
40 | }
41 |
42 | input:disabled {
43 | color: #ccc;
44 | }
45 |
46 | input[type="range"] {
47 | height: 0;
48 | }
49 |
50 | button {
51 | background-color: #f4f4f4;
52 | outline: none;
53 | }
54 |
55 | button:active {
56 | background-color: #ddd;
57 | }
58 |
59 | button:focus {
60 | border-color: #666;
61 | }
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This example uses Rust Web Assembly compiled for WASI (the Web Assembly
191 | System Interface) running in the browser using WasmerJS.
192 |
193 |
Rust is compiled for target wasm32-wasi and bindings are generated using
194 | wasm-bindgen plus a small amount of post-processing to adapt the bindings for
195 | WASI. Source code is at
196 | svelte-wasi-with-rust.
197 |
198 |
Features demonstrated:
199 |
200 |
A Svelte WASM/WASI app with Rust subsystem (using target wasm32-wasi)
201 |
JavaScript and Rust both accessing the WasmerJS/wasmFS filesystem
202 |
Calling Rust from JavaScript and vice versa using wasm-bindgen+
203 |
Passing and returning JavaScript and Rust native types with no mucking about
204 |
Passing and returning JavaScript objects and arrays to/from Rust structs
205 |
206 |
207 |
Note: wasm-bindgen+ indicates a small amount of post-processing to make
208 | the wasm-bindgen output suitable for use with WasmerJS in the browser.
209 |
210 |
Check the browser console and the content below for test output.
211 |
Content from WASI
212 |
stdout:
213 |
{#each output as line} {line} {/each}
214 |
215 |
216 |
--------------------------------------------------------------------------------
/src/greeting/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "svelte-wasm-terminal-test-greeting"
3 | version = "0.1.0"
4 | authors = ["Mark "]
5 | edition = "2018"
6 |
7 | [lib]
8 | crate-type = ["cdylib", "rlib"]
9 |
10 | [features]
11 | default = ["console_error_panic_hook"]
12 |
13 | [dependencies]
14 | wee_alloc = "^0.4"
15 | wasm-bindgen = "^0.2"
16 | console_error_panic_hook = { version = "^0.1", optional = true }
17 |
18 | [dev-dependencies]
19 | wasm-bindgen-test = "^0.3"
20 |
21 | [profile.release]
22 | opt-level = "s"
--------------------------------------------------------------------------------
/src/greeting/src/lib.rs:
--------------------------------------------------------------------------------
1 | use wasm_bindgen::prelude::*;
2 |
3 | #[cfg(feature = "wee_alloc")]
4 | #[global_allocator]
5 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
6 |
7 | #[wasm_bindgen]
8 | pub fn greet() -> String {
9 | "Hello SVELTE from RUST".into()
10 | }
--------------------------------------------------------------------------------
/src/js-wasi/jsWasi.js:
--------------------------------------------------------------------------------
1 | export function js_test() {
2 | console.log("hello from js_test()")
3 | }
4 |
5 | export function js_test_n(n) {
6 | console.log("hello from js_test_n(): ", n)
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import App from './App.svelte';
2 |
3 | const init = async() => {
4 |
5 | const app = new App({
6 | target: document.body,
7 | props: {
8 | }
9 | });
10 | };
11 |
12 | init();
--------------------------------------------------------------------------------
/src/rust-wasi/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | /target/
4 |
5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
7 | Cargo.lock
8 |
9 | # These are backup files generated by rustfmt
10 | **/*.rs.bk
11 |
12 | rust-wasi.wasm
13 | wapm.lock
--------------------------------------------------------------------------------
/src/rust-wasi/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rust-wasi"
3 | version = "0.1.0"
4 | description = "Demo/template for Rust/WASI on WasmerJS using wasm-bindgen"
5 | authors = ["happybeing"]
6 | edition = "2018"
7 |
8 | [dependencies]
9 | js-sys = "0.3.46"
10 | log = "0.4.11"
11 | num-integer = "0.1.44"
12 | serde = { version = "1.0.120", features = ["derive"] }
13 | serde_json = "1.0.61"
14 | sha3 = "0.9.1"
15 | structopt = "0.2"
16 | wasm-bindgen = "^0.2"
17 | # web-sys = "0.3.46"
18 |
19 | # [dependencies.web-sys]
20 | # version = "0.3.6"
21 | # features = [
22 | # 'Document',
23 | # 'Element',
24 | # 'HtmlElement',
25 | # 'Node',
26 | # 'Window',
27 | # ]
--------------------------------------------------------------------------------
/src/rust-wasi/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Wasmer, Inc. and its affiliates.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/rust-wasi/README.md:
--------------------------------------------------------------------------------
1 | # Rust WASI Example
2 |
3 | A Rust/WASI example which compiles to target `wasm32-wasi` and demonstrates the use of `wasm-bindgen` with WasmerJS.
4 |
5 | It imports and can call JavaScript from src/js-wasi and can be called from the application JavaScript using bindings generated by `wasm-bindgen`.
6 |
7 | This is a component in a larger app, as demonstrated in https://github.com/happybeing/svelte-wasi-with-rust. See the main README or visit that project for more.
8 |
--------------------------------------------------------------------------------
/src/rust-wasi/src/main.rs:
--------------------------------------------------------------------------------
1 | use wasm_bindgen::prelude::*;
2 |
3 | use std::fs;
4 | use std::io::Read;
5 | use structopt::StructOpt;
6 |
7 | // Note: For WebAssembly to call Rust exports (above), main() must be empty.
8 | // You can do one or the other, but not both.
9 | fn main() {}
10 |
11 | // #[wasm_bindgen]
12 | // pub fn runtest() -> Result<(), JsValue> {
13 | // // set_panic_hook();
14 |
15 | // println!("runtest called!");
16 | // // // ...
17 | // // let p: web_sys::Node = document.create_element("p")?.into();
18 | // // p.set_text_content(Some("Hello from Rust, WebAssembly, and Webpack!"));
19 | // // // ...
20 | // Ok(())
21 | // }
22 |
23 | // -------------------------------------------------------------
24 | // Misc tests
25 |
26 | #[wasm_bindgen(module = "jsWasi")]
27 | extern "C" {
28 | fn js_test();
29 | fn js_test_n(n: u32);
30 | }
31 |
32 |
33 | #[wasm_bindgen]
34 | pub fn rust_js_test() {
35 | println!("Rust calling js_test()...");
36 | js_test();
37 |
38 | println!("Rust calling js_test_n(123)...");
39 | js_test_n(123);
40 | }
41 |
42 | #[wasm_bindgen]
43 | pub fn rust_print_bg() {
44 | println!("Hello, world BG!");
45 | }
46 |
47 | #[wasm_bindgen]
48 | pub fn rust_print_bg_n(n: u32) {
49 | println!("From rust_print_bg_n(n): {}", n);
50 | }
51 |
52 | #[wasm_bindgen]
53 | pub fn rust_say(s: String) -> String {
54 | let r = String::from("hello ");
55 | return r + &s;
56 | }
57 |
58 | // -------------------------------------------------------------
59 | // wasm-bindgen example by github.com/ibaryshnikov
60 | // (https://github.com/ibaryshnikov/rust-workshop-21-dec-2018)
61 |
62 | #[wasm_bindgen]
63 | pub fn add(a: i32, b: i32) -> i32 {
64 | a + b
65 | }
66 |
67 | #[wasm_bindgen(js_name = doubleList)]
68 | pub fn double_list(list: &[i32]) -> Vec {
69 | list.iter().map(|x| x * 2).collect()
70 | }
71 |
72 | // #[wasm_bindgen(js_name = addElement)]
73 | // pub fn add_element(name: String) -> Result<(), JsValue> {
74 | // let window = web_sys::window().expect("should have a window");
75 | // let document = window.document().expect("should have a document");
76 | // let body = document.body().expect("should have a body");
77 |
78 | // let p = document.create_element("p")?;
79 | // p.set_inner_html(&format!("New element with name: {}", name));
80 |
81 | // body.append_child(&p)?;
82 |
83 | // Ok(())
84 | // }
85 |
86 | // -------------------------------------------------------------
87 | // Examples from Getting started with Rust functions in Node.js
88 | // Article: https://www.secondstate.io/articles/getting-started-with-rust-function/
89 | // Source: https://github.com/second-state/wasm-learning/tree/master/nodejs/functions
90 | // More: https://cloud.secondstate.io/server-side-webassembly/getting-started
91 |
92 | use serde::{Serialize, Deserialize};
93 |
94 | #[derive(Serialize, Deserialize, Debug)]
95 | struct Point {
96 | x: f32,
97 | y: f32
98 | }
99 |
100 | #[derive(Serialize, Deserialize, Debug)]
101 | struct Line {
102 | points: Vec,
103 | valid: bool,
104 | length: f32,
105 | desc: String
106 | }
107 |
108 | #[wasm_bindgen]
109 | pub fn create_line (p1: &str, p2: &str, desc: &str) -> String {
110 | let point1: Point = serde_json::from_str(p1).unwrap();
111 | let point2: Point = serde_json::from_str(p2).unwrap();
112 | let length = ((point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y)).sqrt();
113 |
114 | let valid = if length == 0.0 { false } else { true };
115 | let line = Line { points: vec![point1, point2], valid: valid, length: length, desc: desc.to_string() };
116 | return serde_json::to_string(&line).unwrap();
117 | }
118 |
119 | #[wasm_bindgen]
120 | pub fn say(s: &str) -> String {
121 | let r = String::from("hello ");
122 | return r + s;
123 | }
124 |
125 | #[wasm_bindgen]
126 | pub fn obfusticate(s: String) -> String {
127 | (&s).chars().map(|c| {
128 | match c {
129 | 'A' ..= 'M' | 'a' ..= 'm' => ((c as u8) + 13) as char,
130 | 'N' ..= 'Z' | 'n' ..= 'z' => ((c as u8) - 13) as char,
131 | _ => c
132 | }
133 | }).collect()
134 | }
135 |
136 | use num_integer::lcm;
137 | use sha3::{Digest, Sha3_256, Keccak256};
138 |
139 | #[wasm_bindgen]
140 | pub fn lowest_common_denominator(a: i32, b: i32) -> i32 {
141 | let r = lcm(a, b);
142 | return r;
143 | }
144 |
145 | #[wasm_bindgen]
146 | pub fn sha3_digest(v: Vec) -> Vec {
147 | return Sha3_256::digest(&v).as_slice().to_vec();
148 | }
149 |
150 | #[wasm_bindgen]
151 | pub fn keccak_digest(s: &[u8]) -> Vec {
152 | return Keccak256::digest(s).as_slice().to_vec();
153 | }
154 |
155 | // -------------------------------------------------------------
156 | // wasm-bindgen book example
157 | // (https://rustwasm.github.io/docs/wasm-bindgen/reference/types/imported-js-types.html)
158 |
159 | #[wasm_bindgen]
160 | #[derive(Copy, Clone)]
161 | pub enum NumberEnum {
162 | Foo = 0,
163 | Bar = 1,
164 | Qux = 2,
165 | }
166 |
167 | #[wasm_bindgen]
168 | #[derive(Copy, Clone)]
169 | pub enum StringEnum {
170 | Foo = "foo",
171 | Bar = "bar",
172 | Qux = "qux",
173 | }
174 |
175 | #[wasm_bindgen]
176 | #[derive(Copy, Clone)]
177 | pub struct Struct {
178 | pub number: NumberEnum,
179 | pub string: StringEnum,
180 | }
181 |
182 | #[wasm_bindgen(module = "test")]
183 | extern "C" {
184 | pub type SomeJsType;
185 | }
186 |
187 | #[wasm_bindgen]
188 | pub fn imported_type_by_value(x: SomeJsType) {
189 | /* ... */
190 | }
191 |
192 | #[wasm_bindgen]
193 | pub fn imported_type_by_shared_ref(x: &SomeJsType) {
194 | /* ... */
195 | }
196 |
197 | #[wasm_bindgen]
198 | pub fn return_imported_type() -> SomeJsType {
199 | // let jsType = SomeJsType::new();
200 | // return jsType;
201 | unimplemented!()
202 | }
203 |
204 | #[wasm_bindgen]
205 | pub fn take_option_imported_type(x: Option) {
206 | /* ... */
207 | }
208 |
209 | #[wasm_bindgen]
210 | pub fn return_option_imported_type() -> Option {
211 | unimplemented!()
212 | }
213 |
214 | // -------------------------------------------------------------
215 | // Tests based on the WasmerJS H9Q+ Rust example
216 | //
217 |
218 | fn print_hello_world() {
219 | println!("Hello, world!");
220 | }
221 |
222 | fn print_nintey_nine_bottles_of_beer_on_the_wall() {
223 | for i in (1..100).rev() {
224 | println!("{} bottles of beer on the wall,{} bottles of beer.Take one down, pass it around,{} bottles of beer on the wall.", i, i, i - 1);
225 | }
226 | }
227 |
228 | fn h9q_run(source_code: String) {
229 | #[allow(unused_variables)]
230 | let mut accumulator = 0;
231 |
232 | for c in source_code.chars() {
233 | match c {
234 | 'H' => print_hello_world(),
235 | '9' => print_nintey_nine_bottles_of_beer_on_the_wall(),
236 | 'Q' => println!("{}", source_code),
237 | '+' => accumulator += 1,
238 | _ => (),
239 | }
240 | }
241 | }
242 |
243 | #[derive(Debug, StructOpt)]
244 | struct Opt {
245 | /// Evaluate HQ9+ source code
246 | #[structopt(short = "e", long = "eval")]
247 | source_code: Option,
248 | /// Evaluate HQ9+ source code from a file
249 | #[structopt(short = "f", long = "file")]
250 | source_file: Option,
251 | /// Force evaluation of the `H` instruction
252 | #[structopt(short = "h", long = "hello-world")]
253 | hello_world: bool,
254 | /// Force evaluation of the `9` instruction
255 | #[structopt(short = "9", long = "nintey-nine-bottles")]
256 | ninety_nine_bottles: bool,
257 | }
258 |
259 | #[wasm_bindgen]
260 | pub fn h9q_string(source_code: String) {
261 | h9q_run(source_code);
262 | }
263 |
264 | #[wasm_bindgen]
265 | pub fn h9q_file(src_file: String) {
266 | // let opt = Opt::from_string(¶ms);
267 |
268 | // if opt.hello_world {
269 | // print_hello_world();
270 | // return;
271 | // }
272 |
273 | // if opt.ninety_nine_bottles {
274 | // print_nintey_nine_bottles_of_beer_on_the_wall();
275 | // return;
276 | // }
277 |
278 | // if let Some(src) = opt.source_code {
279 | // h9q_run(src);
280 | // return;
281 | // }
282 |
283 | // if let Some(src_file) = opt.source_file {
284 | match fs::File::open(&src_file) {
285 | Ok(mut src_file_handle) => {
286 | let mut buf = String::new();
287 | src_file_handle
288 | .read_to_string(&mut buf)
289 | .expect(&format!("Failed to read data from file: {}", &src_file));
290 | h9q_run(buf);
291 | },
292 | Err(e) => {
293 | println!("Error: {}", e);
294 | eprintln!(
295 | "Error: please pass HQ9+ source code via the `-e` flag or as a file via the `-f` flag"
296 | );
297 | // ::std::process::exit(-1);
298 | }
299 | }
300 | }
301 |
302 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
2 | const path = require('path');
3 |
4 | const mode = process.env.NODE_ENV || 'development';
5 | const prod = mode === 'production';
6 |
7 | module.exports = {
8 | entry: {
9 | bundle: ['./src/main.js']
10 | },
11 | resolve: {
12 | alias: {
13 | svelte: path.resolve('node_modules', 'svelte')
14 | },
15 | extensions: ['.mjs', '.js', '.svelte'],
16 | mainFields: ['svelte', 'browser', 'module', 'main']
17 | },
18 | output: {
19 | path: __dirname + '/public',
20 | filename: '[name].js',
21 | chunkFilename: '[name].[id].js'
22 | },
23 | module: {
24 | rules: [
25 | {
26 | test: /\.svelte$/,
27 | use: {
28 | loader: 'svelte-loader',
29 | options: {
30 | emitCss: true,
31 | hotReload: true
32 | }
33 | }
34 | },
35 | {
36 | test: /\.css$/,
37 | use: [
38 | /**
39 | * MiniCssExtractPlugin doesn't support HMR.
40 | * For developing, use 'style-loader' instead.
41 | * */
42 | prod ? MiniCssExtractPlugin.loader : 'style-loader',
43 | 'css-loader'
44 | ]
45 | }
46 | ]
47 | },
48 | mode,
49 | plugins: [
50 | new MiniCssExtractPlugin({
51 | filename: '[name].css'
52 | })
53 | ],
54 | devtool: prod ? false: 'source-map'
55 | };
56 |
--------------------------------------------------------------------------------