【Rust】Tauri上でWebAssemblyを動かす

## はじめに

最近は以下の本でRustを勉強しています。

https://www.oreilly.co.jp/books/9784814400393/

RustのコードをWebAssemblyにコンパイルして、HTMLのCanvas上にゲームを描画するという内容の本です。
読み進めていくうちに「**これってTauri上で動かせるのでは?**」と思ったので実際にやってみました。

## インストール

事前に以下の2つをインストールしておきます。
* Node.js
https://nodejs.org/en
* Rust
https://www.rust-lang.org/ja/learn/get-started

rust\-webpackのテンプレートを追加します。

```
$ mkdir sample
$ cd sample
$ npm init rust-webpack
```

必要なパッケージをインストールして動かします。

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

動くことを確認したら、次にTauriをインストールします。

`package.json`の`scripts`にtauriを追加します。

```diff:package.json
  "scripts": {
    ...
+   "tauri": "tauri"
  }
```

CLIコマンドをインストールしてtauriのテンプレートを追加します。

```
$ npm install --save-dev @tauri-apps/cli
$ npm run tauri init
```

この時に色々聞かれるので答えていきます。
アプリ名ですが今回はフォルダ名と同じ`sample`にします。
```
? What is your app name? > sample
```

ウィンドウタイトルも同じく`sample`にします。

```
? What should the window title be? > sample
```

アセットの場所を指定します。
`npm run build`で各種アセットは`sample/dist`に保存されます。
ここでは`sample/src-tauri/tauri.conf.json`から相対パスを聞かれているので`../dist`と入力します。

```
? Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri/tauri.conf.json" file that will be created? › ../dist
```

開発時のURLを入力します。

```
? What is the url of your dev server? › http://localhost:8080
```

開発時に実行するコマンドを入力します。

```
? What is your frontend dev command? › npm run start
```

ビルド時に実行するコマンドを入力します。

```
? What is your frontend build command? › npm run build
```

現状のコマンドだとtauri起動時にブラウザも立ち上がるので`scripts`のコマンドを以下の様に修正します。

```diff:package.json
  "scripts": {
-    "start": "rimraf dist pkg && webpack-dev-server --open -d",
+    "start": "rimraf dist pkg && webpack-dev-server",
```

tauriを起動させます。

```
$ npm run tauri dev
```

ウィンドウが表示されます。

![sample1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2693350/6d837517-3e32-6fc8-6bfe-c721a3ca089a.png)

Node.jsのバージョンによっては`Error: error:0308010C:digital envelope routines::unsupported`となることがあります。
その場合は以下のコマンドを修正してから起動させます。

```diff:package.json
  "scripts": {
-    "start": "rimraf dist pkg && webpack-dev-server",
-    "build": "rimraf dist pkg && webpack",
+    "build": "set NODE_OPTIONS=--openssl-legacy-provider && rimraf dist pkg && webpack",
+    "start": "set NODE_OPTIONS=--openssl-legacy-provider && rimraf dist pkg && webpack-dev-server",
```

## 描画

ウィンドウ上に図形を描画します。

`Cargo.toml`にモジュールを追加します。

```diff:Cargo.toml
[dependencies.web-sys]
version = "0.3.22"
- features = ["console"]
+ features = ["console", "Window", "Document", "HtmlCanvasElement", "CanvasRenderingContext2d"]
```

Canvasを追加します。

```diff:sample/static/index.html
  <body>
    <script src="index.js"></script>
+   <canvas id="canvas" tabindex="0" width="500" height="500"></canvas>
  </body>
```

三角形を描画するコードを追加します。

```rust:src/lib.rs
// This is like the `main` function, except for JavaScript.
#[wasm_bindgen(start)]
pub fn main_js() -> Result<(), JsValue> {
    ...
    // Your code goes here!
    console::log_1(&JsValue::from_str("Hello world!"));
    let window = web_sys::window().unwrap();
    let document = window.document().unwrap();
    let canvas = document
        .get_element_by_id("canvas")
        .unwrap()
        .dyn_into::<web_sys::HtmlCanvasElement>()
        .unwrap();
    let context = canvas
        .get_context("2d")
        .unwrap()
        .unwrap()
        .dyn_into::<web_sys::CanvasRenderingContext2d>()
        .unwrap();
    context.begin_path();
    context.move_to(250.0, 0.0);
    context.line_to(0.0, 500.0);
    context.line_to(500.0, 500.0);
    context.close_path();
    context.set_fill_style(&wasm_bindgen::JsValue::from_str("rgb(0,128,255)"));
    context.fill();
    Ok(())
}
```

ウィンドウ上に三角形が描画されました。

![sample2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2693350/9757674c-d10d-3906-0612-1731ded1821e.png)


## ビルド

最後にビルドできることを確認します。
`sample/src-tauri/tauri.conf.json`の`identifier`を任意の値に変えて以下のコマンドを実行します。

```
$ npm run tauri build
```

`sample/src-tauri/target/release`に実行ファイルが生成されました。

![release.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2693350/e56ab131-c480-0e1d-b7f7-3dbcf7c84a90.png)

## 参考文献

https://rustwasm.github.io/wasm-pack/installer/

https://tauri.app/v1/api/cli/

https://stackoverflow.com/questions/75167770/error-error0308010cdigital-envelope-routinesunsupported-node-js-v19-4-0