【Day16】たうりちゃんはデータを保存したい！！【QAC24】


# 結局のところデータ保存したいですよね。

前回にタイルエディターを作ったような気がするのですが、データ保存が出来なきゃ意味がないのですよ。
Node.js に同じみな人に言うなら`fs`みたいなのが欲しいですよね。

# fs-plugin でデータを保存する

Tauri の File System という機能は、Plugin という形で提供されています。
そのため、まずはインストールする必要があります。

https://v2.tauri.app/plugin/file-system/

```bash
$ pnpm tauri add fs
```

これで、`fs`のパッケージのインストールと Rust 側のコード調整が自動で行われます。

適当にコードを書いてお試ししてみましょう。

```typescript
import { exists, BaseDirectory } from "@tauri-apps/api/fs";

console.log(await exists("test.txt", { baseDir: BaseDirectory.AppData }));
```

というようなコードが Docs で紹介されているので説明してみましょう。

今回は`exists`と`BaseDirectory`というものをインポートしています。
`exists`はファイルが存在するかどうかを確認する関数なのですが、親ディレクトリを指定する必要があります。
また、セキュリティ上の理由からパスは後述の`baseDir`からの相対パス(親ディレクトリに行くことは出来ない)で指定する必要があります。
(`path.join`というものを使うことで指定しなくてもよい)

でその`baseDir`ですが、`BaseDirectory`から生える列挙型から選ぶことが出来ます。
ここら辺は、どこのフォルダにアクセスする必要があるのかで変わってきますね。

...で、なんとなくわかったかもしれないですが、あんまり自由度が高いわけではないというか...

好きな場所の好きなファイルを操作することはできないですね。多分。
また、セキュリティ上の理由から`permission`にパスを書き込まなければならず、少しそこらへんもめんどくさいです。

## Rustで書けばいいじゃね？（原点回帰）

簡単な話、フロントエンドはセキュリティ上の理由から制限されています。
ですがバックエンドはそこまで制限されていないので、書くことが出来ます。
が、何のためにフロントエンドが制限されているのかだけは考えておく必要がありますよね...？

というわけで、Rust...よりTauri的な部分にフォーカスを当てていきましょう

# `tauri-specta`を使ってみる

`tauri-specta`は、Rustで定義した関数...特にコマンドをフロントエンド上で勝手に定義してくれて、楽に呼び出せるようにするライブラリというかツールというかです。

という訳でインストールしていきましょう。

## インストール

基本はドキュメントに従っていきます。

```bash
$ cd ./src-tauri
$ cargo add specta@=2.0.0-rc.20
$ cargo add specta-typescript@0.0.7
$ cargo add tauri-specta@=2.0.0-rc.20 --features derive,typescript
```

これで`tauri-specta`とそれに必要な`specta`、`specta-typescript`がインストールされます。

## 使ってみる

TauriのRust側のコードは`src-tauri/src/lib.rs`に書かれています。

多分何も触ってなかったらこんな感じなのかな？

```rust:src-tauri/src/lib.rs
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}! You've been greeted from Rust!", name)
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_shell::init())
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
```

この`greet`関数が、フロントエンドから呼び出されるコマンドなのは、だいぶ前に紹介しましたね。
まずはこの関数に新しいマクロとして`specta::specta`を追加してみましょう。

```rust:src-tauri/src/lib.rs
#[tauri::command]
#[specta::specta]
fn greet(name: &str) -> String {
    format!("Hello, {}! You've been greeted from Rust!", name)
}
```

そのうえで、`run`関数の中でコマンドをなんか色々する処理を書きましょう。
全部でこんな感じです。

```diff_rust:src-tauri/src/lib.rs
+ use specta_typescript::Typescript;
+ use tauri_specta;
  
  #[tauri::command]
+ #[specta::specta]
  fn greet(name: &str) -> String {
      format!("Hello, {}! You've been greeted from Rust!", name)
  }
  
  #[cfg_attr(mobile, tauri::mobile_entry_point)]
  pub fn run() {
+     let builder =
+         tauri_specta::Builder::<tauri::Wry>::new().commands(tauri_specta::collect_commands![greet]);
  
+     builder
+         .export(Typescript::default(), "../src/binding.ts")
+         .expect("failed to export typescript bindings");
  
      tauri::Builder::default()
          .plugin(tauri_plugin_shell::init())
-         .invoke_handler(tauri::generate_handler![greet])
+         .invoke_handler(builder.invoke_handler())
          .run(tauri::generate_context!())
          .expect("error while running tauri application");
}
```

これで、Tauri起動時に`../src/binding.ts`に TypeScript の型定義とかが書き出されるようになります。
(`src-tauri`からの相対パスなので、`../`がついている)

まぁまずは`pnpm tauri dev`でTauriを起動してみましょう。
起動したら閉じても大丈夫です。

そうすると`/src/binding.ts`に TypeScript の型定義が書き出されているはずです。

```typescript:/src/binding.ts
...

export const commands = {
async greet(name: string) : Promise<string> {
    return await TAURI_INVOKE("greet", { name });
}
}

...
```

大事なのはここですかね。

`commands`の中で、Rustで定義した`greet`関数があります。
中身はもちろん`invoke`で呼び出しているのですが、関数に型などもついているので、TypeScript から呼び出すときに便利ですよね。

実際にこれを使うときは、こんな感じです。

```typescript
import { commands } from "../binding.ts";

console.log(await commands.greet("Anonymouse"));
```

`commands`をimportして、`greet`関数を呼び出しています。

ちなみに、Rustには`Result`という型がありますが、これもしっかり再現出来るようになっており、
TypeScriptでは`binding.ts`内に`Result<T, E>`という型が定義されています。

`Result`の`status`というプロパティが`ok`か`error`かで、`ok`の場合は`data`にTが入り、`error`の場合は`error`にEが入ります。

というわけで、`tauri-specta`を使ってみると、Rustのコマンドを簡単にフロントエンドから呼び出すことが出来るようになります。

# コード紹介は？
しないのよ～

セキュリティ上の制限が突破された今、Rustでファイルを扱うコードについては普通に調べれば出てきちゃうので、省略します。
タイルのリストをコマンドに渡して後はそれをシリアライズしてファイルに書き込むとか、そういう感じですね。
特定のキーを押したときにそれを発動させればもう完璧ですもんね。

次回は、記事冒頭で紹介したようなプラグインと呼ばれるやつをいろいろ紹介してみたいと思います。