ファイルシステムを監視する (notify)

Recipe ID: plugin-007

ファイルやディレクトリの変更(作成、更新、削除)を検知するには、Rust エコシステムの標準的なクレートである notify を使用するのが最も確実です。Rust 側で実装しイベントでフロントエンドに通知するパターンを解説します。

1. 依存関係の追加

# src-tauri ディレクトリで実行します
cd src-tauri
cargo add notify --features serde

2. 監視ロジックの実装

setup フックなどで監視を開始し、変更があればフロントエンドへイベントを emit します。

src-tauri/src/lib.rs:

use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
use std::sync::mpsc::channel;
use tauri::{AppHandle, Manager, Emitter};

fn setup_watcher(app: &AppHandle) {
    let app_handle = app.clone();
    
    // イベント監視はブロッキング操作を含むため、専用のスレッドで実行します
    std::thread::spawn(move || {
        let (tx, rx) = channel();

        // ウォッチャーの作成
        let mut watcher = RecommendedWatcher::new(tx, Config::default()).unwrap();

        // 監視対象のパス (例: ドキュメントフォルダ)
        // 注意: 実際のアプリでは権限やパスの存在確認を行ってください
        let path_to_watch = match app_handle.path().document_dir() {
            Ok(path) => path,
            Err(e) => {
                eprintln!("Failed to get document dir: {}", e);
                return;
            }
        };
        
        // 監視開始 (RecursiveMode::Recursive でサブディレクトリも監視)
        if let Err(e) = watcher.watch(&path_to_watch, RecursiveMode::Recursive) {
            eprintln!("Watcher error: {:?}", e);
            return;
        }

        // イベントループ (チャンネルからの受信待機でブロックする)
        for res in rx {
            match res {
                Ok(event) => {
                    println!("Change detected: {:?}", event);
                    // フロントエンドに通知 (notify の Event は serde feature でシリアライズ可能)
                    if let Err(e) = app_handle.emit("file-changed", event) {
                        eprintln!("Failed to emit event: {}", e);
                    }
                },
                Err(e) => println!("Watch error: {:?}", e),
            }
        }
    });
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .setup(|app| {
            setup_watcher(app.handle());
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

3. フロントエンドでの受信

import { listen } from '@tauri-apps/api/event';

type FileEvent = {
    kind: any; // 作成、変更、削除など
    paths: string[];
};

await listen<FileEvent>('file-changed', (event) => {
    console.log('File changed:', event.payload);
    // UIを更新など
});

注意点

  • notify は OS ごとの API (inotify, FSEvents, ReadDirectoryChangesW) を抽象化していますが、挙動に差異がある場合があります。