1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OECUAdvent Calendar 2024

Day 17

TauriでRustからファイル操作をしてみよう📝

Last updated at Posted at 2024-12-16

本記事では、Tauri製アプリでRustからファイルを操作し、データを保存する方法について紹介します。
リボンのファイルからデータを読み込んできて・・・っていうのは紹介しないのであしからず。
なお、Rust・Node.jsの環境が入っていることは前提条件とします。

Tauriを使ってみよう

皆さんアプリ作ってますか?

最近は、敢えてローカル環境にインストールせずサイトとしてアプリを動作させるWebアプリが主流ですが、ここ数年で逆にWebアプリの技術を利用したネイティブアプリ用のフレームワークも出てきています。
特にElectronやReact Nativeが流行っていますが、Rustでも同様のフレームワークが出てきました。それがTauriです!

What's Tauri !?

TauriはRust製のフレームワークで、WebViewを通してUIの表示させてコマンドという形でRustの処理を呼び出すと言ったことが可能なので早い動作が実現できるフレームワークです!
特に魅力なのはReactやSvelte、Rust製のYewなんかでもUIの構築ができるそうで、かなりWebを触ったことがある人は入りやすいのではないでしょうか!?

また、クロスコンパイルや最近ではモバイルにも対応したので多用途での利用が期待されているフレームワークです(だと思いたいです)。

さらに、Pluginが存在しておりReactやSvelteなどUI部分でほとんど処理が書けるため、Rustを学ぶという遥か高い壁を乗り越えなくて済むというメリットもあります。勿論Rustで書くよなぁ!?

ちなみに私はTauriを利用してこのような掲示板を作成しました!
https://news.yahoo.co.jp/articles/f752a2040e8bc9603576f174a12e7a05ec670f84

Rustでファイル操作

いくらTSやJSで書けるとはいえ、Rust言語が好きでRustで書きたいんだ!という需要もあると思います(かく言う私がそうです)。
そして、私が個人的に作成しているアプリの中でなかなか困ったわりに日本語記事が出てこなかったRustでのファイル操作について本記事では紹介します!

今回は、アプリのデータを保存する、Windowsだと

C:Users\hoge-user\AppData\Local\hoge.app

に該当する場所へのデータ保存を取り扱っていこうと思います!

アプリの作成

では、アプリのプロジェクトを作成していきましょう!
まずtauri-cliとcreate-tauri-appをインストールします

cargo install tauri-cli create-tauri-app

それが完了したら次にプロジェクトを作成します
ここら辺は基本お好みで行きましょう~

cargo create-tauri-app

それが出来たら作成されたディレクトリに移って、UI部のパッケージをインストール!

動作確認のために実行してみましょう

cargo tauri dev

無事起動出来たら初期設定が完了です!

データの保存先を取得

データの保存先は上記の通り

C:Users\hoge-user\AppData\Local\hoge.app

のディレクトリ以下に保存したいためAppDataのLocal内にあるアプリ用ディレクトリを取得したいです。
そのために、アプリの立ち上げ時にディレクトリを取得するコードをsrc-tauri/lib.rsに書いていきます!

src-tauri/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)
}

+ fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
+     let base_dir = app.path().app_local_data_dir()?;
+     let file = "file.txt";
+     let path = format!("{}\\{}", base_dir.to_str().unwrap(), file);
+     println!("{}", &path);
+ 
+     Ok(())
+ }

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

これを書いて実行すると、コンソール上に

C:Users\hoge-user\AppData\Local\hoge.app\file.txt

が出力されているはずです!

これで、あとはfsでファイル操作をして・・・って言いたいところなんですが、このままではsetupでしかファイル操作が出来ないので次にRustで書くコマンド上でファイル操作をやってみる方法を共有します!

コマンド上でのファイル操作

コマンド上でファイルを操作する場合、setupで取得したパスをコマンドの処理でも使いたいですよね。そこで活躍するのがState
アプリ上で状態を共有するために使用するのがこのStateになります。
感のいい方はお気づきでしょうが、取得できたパスをStateで共有して使いまわすということを行います。
では、そのやり方を見ていきましょう!

まずは、何を共有したいのかを定義するためにAppState構造体を作成します!
一番上にpath変数を持つAppDataを定義しましょう。

src-tauri/lib.rs
struct AppData {
    path: String,
}

そして、ファイルが作成済みであればいいですし、なければファイルを作成する処理を記述した初期化用のnew関数をAppData構造体に定義しましょう。

src-tauri/lib.rs
impl AppData {
    fn new(path: &str) -> std::result::Result<Self, Box<dyn std::error::Error>> {
        std::fs::File::create(path)?;
        Ok(AppData {
            path: path.to_string(),
        })
    }
}

これで、パスを共有する準備は整いました!
ここからはパスを共有して実際にファイルを操作していきましょう!

まず、せっかく共有するデータを定義した構造体を作ったのでsetup関数に、アプリへ共有データを登録して、欲しい時に共有できるよう処理を書いていきましょう

src-tauri/lib.rs
+ use tauri::Manager;

fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
    let base_dir = app.path().app_local_data_dir()?;
    let file = "file.txt";
    let path = format!("{}\\{}", base_dir.to_str().unwrap(), file);
    println!("{}", &path);
+
+     app.manage(AppData::new(&path)?);

    Ok(())
}

これでデータを共有することが可能になったので、コマンドの方でデータを受け取ってファイル操作をする処理を、面倒なので生成されているgreet関数を変更して書いていきましょう!

src-tauri/lib.rs
#[tauri::command]
- fn greet(name: &str) -> String {
-     format!("Hello, {}! You've been greeted from Rust!", name)
+ fn greet(state: tauri::State<AppData>, name: &str) -> Result<String, String> {
+     let byte_data = std::fs::read(&state.path).map_err(|e| e.to_string())?;
+     let mut data = String::from_utf8(byte_data).map_err(|e| e.to_string())?;
+     data.push_str(&format!(", {}", name));
+     std::fs::write(&state.path, &data).map_err(|e| e.to_string())?;
+     Ok(data)
}

先述のStateは、greet関数で引数として受け取っているStateのことで、Stateの型引数として渡した型に合致した値を取得してくれます。
ちなみに、この書き方以外にもAppHandleを受け取ってそこからStateを取得することも可能ですがそれはまた別の話・・・

そして、あとは普段のファイル操作と同じ要領で記述していけばファイル操作が行えます!
この処理は、受け取った名前をファイルの末尾に追加していくだけの面白味のない処理ですのでお気になさらず()

動作確認

では、実際に動かして確認してみましょう!
まずは、アプリ上の動作から!

スクリーンショット 2024-12-16 041813.png

無事に出力されていますね!
追加していく動作だったのでもう一回やってみましょう。

スクリーンショット 2024-12-16 041948.png

後ろに連結されていることが確認できました!
では、今度は実際にファイルが保存されているかをエクスプローラーから覗いてみましょう!

スクリーンショット 2024-12-16 042132.png

file.txtが確認できましたね!
そしてその中身は・・・?

スクリーンショット 2024-12-16 042501.png

見事入力したデータがファイルに出力されていることが確認できました!
これでやりたいこと完了です!

まとめ

本記事では、TauriでRustからファイル操作をする方法について紹介していきました!

アプリとしてデータを保存しておくために、JSONであったりCSVであったりいろいろなニーズがあり、かく言う私もJSONを扱いたかったので今回調べて(というか記事がないのでほぼ独力で)やってみました!

短時間でやったことをまとめて書いたので、分からない点や抜けがあればご指摘いただきたいですが、少しでも参考になれば幸いです!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?