Notes on the process of web assembly engineering development based on rule

Keywords: Programming Webpack JSON npm AWS

Initialization project

$ npm init rust-webpack web_assembly_demo
npx: 18 Installation successful, time 3.989 second
 Rust +  WebAssembly + Webpack = ️
Installed dependencies 

Install Web dependency

$ yarn
yarn install v1.19.1
warning package.json: No license field
info No lockfile found.
warning package-lock.json found. Your project contains lock files generated by tools other than Yarn. It is advised not to mix package managers in order to avoid resolution inconsistencies caused by unsynchronized lock files. To clear this warning, remove package-lock.json.
warning rust-webpack-template@0.1.0: No license field
[1/4]   Resolving packages...
warning @wasm-tool/wasm-pack-plugin > watchpack > chokidar > fsevents@1.2.9: One of your dependencies needs to upgrade to fsevents v2: 1) Proper nodejs v10+ support 2) No more fetching binaries from AWS, smaller package size
[2/4]   Fetching packages...
[3/4]   Linking dependencies...
[4/4]   Building fresh packages...
success Saved lockfile.
  Done in 17.87s.

Modify Cargo.toml to

# You must change these to your own details.
[package]
name = "web_assembly_demo"
description = "My super awesome Rust, WebAssembly, and Webpack project!"
version = "0.1.0"
authors = ["guzhongren <guzhoongren@live.cn>"]
categories = ["wasm"]
readme = "README.md"
edition = "2018"

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

[profile.release]
# This makes the compiled code faster and smaller, but it makes compiling slower,
# so it's only enabled in release mode.
lto = true

[features]
# If you uncomment this line, it will enable `wee_alloc`:
#default = ["wee_alloc"]

[dependencies]
# The `wasm-bindgen` crate provides the bare minimum functionality needed
# to interact with JavaScript.
wasm-bindgen = "0.2.45"

# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. However, it is slower than the default
# allocator, so it's not enabled by default.
wee_alloc = { version = "0.4.2", optional = true }

# The `web-sys` crate allows you to interact with the various browser APIs,
# like the DOM.
[dependencies.web-sys]
version = "0.3.22"
features = ["console"]

# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so it's only enabled
# in debug mode.
[target."cfg(debug_assertions)".dependencies]
console_error_panic_hook = "0.1.5"

# These crates are used for running unit tests.
[dev-dependencies]
wasm-bindgen-test = "0.2.45"
futures = "0.1.27"
js-sys = "0.3.22"
wasm-bindgen-futures = "0.3.22"

Trust's dependencies are automatically installed when you start the Web program.

Startup program

$ yarn start
yarn run v1.19.1
warning package.json: No license field
$ rimraf dist pkg && webpack-dev-server --open -d
🧐  Checking for wasm-pack...

  wasm-pack is installed. 

ℹ️  Compiling your crate in development mode...

ℹ 「wds」: Project is running at http://localhost:8080/
ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from /Users/c4/Desktop/Personal/01.Project/web_assembly/web_assembly_demo/dist
[INFO]:   Checking for the Wasm target...
[INFO]:   Compiling to Wasm...
ℹ 「wdm」: wait until bundle finished: /
   Compiling proc-macro2 v1.0.6
   Compiling unicode-xid v0.2.0
...
 3 assets
Entrypoint index = index.js
[./pkg/index.js] 4.41 KiB {0} [built]
[./pkg/index_bg.wasm] 145 KiB {0} [built]
    + 33 hidden modules
ℹ 「wdm」: Compiled successfully.
ℹ️  Compiling your crate in development mode...

[INFO]:   Checking for the Wasm target...
[INFO]:   Compiling to Wasm...
   Compiling rust-webpack-template v0.1.0 (/Users/c4/Desktop/Personal/01.Project/web_assembly/web_assembly_demo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.62s
[INFO]: ⬇️  Installing wasm-bindgen...
[INFO]: Optional fields missing from Cargo.toml: 'repository', 'license'. These are not necessary, but recommended
[INFO]:    Done in 0.77s
[INFO]:    Your wasm pkg is ready to publish at ./pkg.
  Your crate has been correctly compiled

ℹ 「wdm」: Compiling...
ℹ 「wdm」: Hash: d4e8a3c57ad23f847707
Version: webpack 4.41.2
Time: 411ms
Built at: 2019-11-23 20:16:55
                           Asset     Size  Chunks                         Chunk Names
                            0.js   17 KiB       0  [emitted]              
beee557fb69dcfa0df60.module.wasm  161 KiB       0  [emitted] [immutable]  
                        index.js  897 KiB   index  [emitted]              index
Entrypoint index = index.js
[./pkg/index.js] 4.93 KiB {0} [built]
[./pkg/index_bg.wasm] 161 KiB {0} [built]
    + 33 hidden modules
ℹ 「wdm」: Compiled successfully.
ℹ 「wdm」: Compiling...
ℹ 「wdm」: Hash: 3e1681b9b4c4c940722e
Version: webpack 4.41.2
Time: 16ms
Built at: 2019-11-23 20:17:14
   Asset     Size  Chunks             Chunk Names
index.js  897 KiB   index  [emitted]  index
 + 2 hidden assets

Create parse.rs in src and write the rust code to handle markdown

struct Parser {
  pos: usize,
  input: String,
}

pub fn parse(source: String) -> String {
  Parser {
    pos: 0,
    input: source,
  }.parse_lines()
}

impl Parser {
  fn parse_lines(&mut self) -> String {
    let mut result = String::new();
    loop {
      self.consume_whitespace();
      if self.end_of_line() {
        break;
      }
      result.push_str(&self.parse_line());
    }
    result
  }

  fn parse_line(&mut self) -> String {
    match self.next_char() {
      '#' => self.parse_title(),
      '-' => {
        if char::is_whitespace(self.input[self.pos + 1..].chars().next().unwrap()) {
          self.parse_list()
        } else {
          self.parse_text()
        }
      }
      _ => self.parse_text(),
    }
  }

  fn parse_list(&mut self) -> String {
    self.consume_char();
    self.consume_whitespace();
    let text = self.parse_text();
    create_html_element("li".to_string(), text)
  }

  fn parse_title(&mut self) -> String {
    let pound = self.consume_while(|c| c == '#');
    self.consume_whitespace();
    let text = self.parse_text();

    create_html_element(format!("h{}", pound.len()), text)
  }

  fn parse_text(&mut self) -> String {
    self.consume_while(|c| !is_new_line(c))
  }

  fn end_of_line(&self) -> bool {
    self.pos >= self.input.len()
  }

  // fn starts_with(&self, s: &str) -> bool {
  //   self.input[self.pos..].starts_with(s)
  // }

  fn next_char(&self) -> char {
    self.input[self.pos..].chars().next().unwrap()
  }

  fn consume_char(&mut self) -> char {
    let mut iter = self.input[self.pos..].char_indices();
    let (_, cur_char) = iter.next().unwrap();
    let (next_pos, _) = iter.next().unwrap_or((1, ' '));
    self.pos += next_pos;
    cur_char
  }

  fn consume_while<F>(&mut self, cond: F) -> String
  where
    F: Fn(char) -> bool,
  {
    let mut result = String::new();
    while !self.end_of_line() && cond(self.next_char()) {
      result.push(self.consume_char());
    }
    result
  }

  fn consume_whitespace(&mut self) {
    self.consume_while(char::is_whitespace);
  }
}

fn create_html_element(tag_name: String, text: String) -> String {
  format!("<{}>{}</{}>", tag_name, text, tag_name)
}

fn is_new_line(c: char) -> bool {
  c == '\n'
}

Introduce parse mod into src/lib.rs, and write exposed functions

mod parser;

extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn parse(input: &str) -> String {
  let result = parser::parse(input.to_string());
  result
}

Using Web Assembly in js/index.js, the modification results are as follows

document.body.onload = addElement;
function addElement() {
  const markdown = document.createElement('textarea')
  markdown.id = 'markdown'
  markdown.style = "height: 300px; width: 400px ;"

  document.body.appendChild(markdown)
  const parseBtn = document.createElement('button')
  parseBtn.id = 'parse'
  parseBtn.innerHTML = 'analysis markdown'
  document.body.appendChild(parseBtn)

  const previewArea = document.createElement('div')
  previewArea.id = 'preview'
  document.body.appendChild(previewArea)


  const rust = import('../pkg/index.js')

  rust.then(module => {
    const btn = document.getElementById('parse')
    const previewArea = document.getElementById('preview')

    btn.addEventListener('click', () => {
      const input = document.getElementById('markdown').value
      previewArea.innerHTML = module.parse(input)
    })
  })
}


// import("../pkg/index.js").then(module => {
//   const input = '1233'
//     previewArea.innerHTML = module.parse(input)
// }).catch(console.error);

Design sketch

Follow-up

Later, we will continue to develop on this basis, and write some concept configurations in WASM as document sharing in the blog.

Warehouse address: https://github.com/AndorLab/web_assembly/tree/based_rust

Posted by gmiyano on Mon, 25 Nov 2019 10:30:48 -0800