Open17

Tauri 触ってみる

nbstshnbstsh

Vite quickstart

frontend は Vite + React + TS で進めていく

https://tauri.app/v1/guides/getting-started/setup/vite/

nbstshnbstsh

Scaffolding

project を scaffolding

npm create tauri-app
✔ Project name · tauri-quick-start-vite
✔ Choose your package manager · npm
✔ Choose your UI template · react-ts

Please follow https://tauri.app/v1/guides/getting-started/prerequisites to install the needed prerequisites, if you haven't already.

Done, Now run:
  cd tauri-quick-start-vite
  npm install
  npm run tauri dev

生成されるものは、こんな感じ↓

├── README.md
├── index.html
├── package.json
├── public
│   ├── tauri.svg
│   └── vite.svg
├── src
│   ├── App.css
│   ├── App.tsx
│   ├── assets
│   │   └── react.svg
│   ├── main.tsx
│   ├── style.css
│   └── vite-env.d.ts
├── src-tauri
│   ├── Cargo.toml
│   ├── build.rs
│   ├── icons
│   │   ├── 128x128.png
│   │   ├── 128x128@2x.png
│   │   ├── 32x32.png
│   │   ├── Square107x107Logo.png
│   │   ├── Square142x142Logo.png
│   │   ├── Square150x150Logo.png
│   │   ├── Square284x284Logo.png
│   │   ├── Square30x30Logo.png
│   │   ├── Square310x310Logo.png
│   │   ├── Square44x44Logo.png
│   │   ├── Square71x71Logo.png
│   │   ├── Square89x89Logo.png
│   │   ├── StoreLogo.png
│   │   ├── icon.icns
│   │   ├── icon.ico
│   │   └── icon.png
│   ├── src
│   │   └── main.rs
│   └── tauri.conf.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

見た感じ、

src: お馴染みの frontend のコード
src-tauri: Rust のコード

が生成されてる

nbstshnbstsh

実行

cd tauri-quick-start-vite
npm install
npm run tauri dev

nbstshnbstsh

Invoke Commands

Tauri lets you enhance your frontend with native capabilities. We call these Commands, essentially Rust functions that you can call from your frontend JavaScript. This enables you to handle heavy processing or calls to the OS in much more performant Rust code.

Commands: OS native の機能を frontend 側で利用できるように Tauri が提供する api

例: greet Command を Rust で定義し、frontend 側で呼び出す

(Rust) greet Command 作成

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

fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

(frontend) '@tauri-apps/api を通して、greet commad を invoke

src/main.ts
import { invoke } from '@tauri-apps/api'

// now we can call our Command!
// Right-click the application background and open the developer tools.
// You will see "Hello, World!" printed in the console!
invoke('greet', { name: 'World' })
  // `invoke` returns a Promise
  .then((response) => console.log(response))
nbstshnbstsh

fs

Access the file system.

fs を利用して file system にアクセスしてみる

https://tauri.app/v1/api/js/fs

nbstshnbstsh

tauri.conf.json 更新

tauri.conf.json で fs を許可してあげる

tauri.conf.json
{
  "tauri": {
    "allowlist": {
      "fs": {
        "all": true, // enable all FS APIs
        "readFile": true,
        "writeFile": true,
        "readDir": true,
        "copyFile": true,
        "createDir": true,
        "removeDir": true,
        "removeFile": true,
        "renameFile": true,
        "exists": true
      }
    }
  }
}

https://tauri.app/v1/api/config/#allowlistconfig.fs

nbstshnbstsh

Security

This module prevents path traversal, not allowing absolute paths or parent dir components (i.e. "/usr/path/to/file" or "../path/to/file" paths are not allowed). Paths accessed with this API must be relative to one of the base directories so if you need access to arbitrary filesystem paths, you must write such logic on the core layer instead.

  • absolute path
  • parent dir

にはアクセスできないらしい
基本的に、以下の base directories 下ならOK。

https://tauri.app/v1/api/js/fs/#basedirectory

それ以外の場合は、別途 core layer (なにこれ?Rust 側のコードか?) でロジックを書けばいけるらしい。

The scope configuration is an array of glob patterns describing folder paths that are allowed. For instance, this scope configuration only allows accessing files on the databases folder of the $APP directory:

アクセス可能な scope を tauri.allowlist.fs.scope で設定。デフォでは [] なので、設定しないとアクセスできないぽい。

tauri.conf.json
{
  "tauri": {
    "allowlist": {
      "fs": {
        "scope": ["$APP/databases/*"]
      }
    }
  }
}

Notice the use of the $APP variable. The value is injected at runtime, resolving to the app directory. The available variables are: $AUDIO, $CACHE, $CONFIG, $DATA, $LOCALDATA, $DESKTOP, $DOCUMENT, $DOWNLOAD, $EXE, $FONT, $HOME, $PICTURE, $PUBLIC, $RUNTIME, $TEMPLATE, $VIDEO, $RESOURCE, $APP, $LOG, $TEMP.

scope 指定時に $APP のように runtime で値が決まる変数を使用できる。

以下が使用可能な変数↓

$AUDIO, $CACHE, $CONFIG, $DATA, $LOCALDATA, $DESKTOP, $DOCUMENT, $DOWNLOAD, $EXE, $FONT, $HOME, $PICTURE, $PUBLIC, $RUNTIME, $TEMPLATE, $VIDEO, $RESOURCE, $APP, $LOG, $TEMP
nbstshnbstsh

File 書き込みしてみる

  1. tauri.conf.json で download directory への書き込みを許可
tauri.conf.json
 "tauri": {
    "allowlist": {
      "fs": {
        "all": true, // 書き込みを含む全ての操作を許可
        "scope": ["$DOWNLOAD/*"] // download directory への書き込みを許可
      },
      "dialog": {
        "message": true // alert できるように
      }
    },
//...
  1. @tauri-apps/api/fswriteTextFile でファイル書き込み
CreateFile.txt
import { BaseDirectory, writeTextFile } from '@tauri-apps/api/fs';
import { useState } from 'react';
import './CreateFile.css';

export const CreateFile = () => {
  const [fileName, setFileName] = useState('');
  const [content, setContent] = useState('');

  return (
    <div className='create-file'>
      <input value={fileName} onChange={(e) => setFileName(e.target.value)} />
      <textarea
        rows={10}
        value={content}
        onChange={(e) => setContent(e.target.value)}
      />
      <button
        onClick={async () => {
          if (!fileName) {
            alert('Enter FileName');
            return;
          }
          if (!content) {
            alert('Enter content');
            return;
          }

          try {
            await writeTextFile(fileName, content, {
              dir: BaseDirectory.Download,
            });
            alert('Success');
          } catch (e) {
            alert(e);
          }
        }}
      >
        Create New File
      </button>
    </div>
  );
};

download directory に "sample.txt" が作成された、OK

nbstshnbstsh

alert 使おうとしたらエラー出た

Unhandled Promise Rejection: 'dialog > message' not in the allowlist (https://tauri.app/docs/api/config#tauri.allowlist)
CreateFile.tsx:31

dialog > message を allowlist に加えないといけないみたい