はじめに

今回はTauriを使ってレシピ取得アプリを作成しました。
著者はRustもReact(TypeScript)も初学者のため学習のために作成しました。

完成形のGitHubリポジトリ
https://github.com/taiyou1116/tauri-recipe-search

作るモチベ

RustとTypeScriptを一度に学べる!!
Rustの非同期処理について学べる!!

使う技術

    • Tauri

 

    • Rust

 

    React (TypeScript)

対象者

この記事は下記のような人を対象にしています。

    • Tauriでデスクトップアプリを作成してみたい方

 

    • Rust, TypeScript初学者

 

    楽天APIを使ってみたい方

完成形

recipe_img_02.png

追記(2023/10/11)

下記コードでevalメソッドを使用していますが、直接jsコードを実行してしまうので、代わりにemitメソッドを使用しましょう。emitは引数の関数をイベントに登録できます。

以前のコード

// メイン関数で受信と処理
    while let Some(recipes) = rx.recv().await {
        // 重複を見てあげる
        let mut new_recipes = Vec::new();
        for recipe in recipes {
            if seen.insert(recipe.recipe_id.clone()) {
                new_recipes.push(recipe);
            }
        }

        // ここで受信したレシピを処理
        let js_command = format!(
            "window.receiveRecipes({})",
            serde_json::to_string(&new_recipes).unwrap()
        );
        window.eval(&js_command).unwrap();
    }

次のように変更

// メイン関数で受信と処理
    while let Some(recipes) = rx.recv().await {
        // 重複を見てあげる
        let mut new_recipes = Vec::new();
        for recipe in recipes {
            if seen.insert(recipe.recipe_id.clone()) {
                new_recipes.push(recipe);
            }
        }
        window.emit("receive_recipes", &new_recipes).unwrap();
    }

その他詳細は私のGitHubや、下のコメントしてくれた方を参考にしてください。↓

コード解説

問題点

get_category_dataでレシピデータを取得しますが、楽天APIは人気のレシピデータを4つずつしか取得できません。また、APIへのリクエストは1秒ほど間隔を空けないとエラーが発生してしまいます。(負荷を抑えるため)

tokio::spawnで解決

tokio::spawnを使用して新しくタスクを作成します。その中でAPIへのリクエストをしレシピを取得します。
取得するたびにwhile let Some(recipes) = rx.recv().awaitの部分で受け取ります。
そして、window.eval(&js_command).unwrap();でフロントのwindow.receiveRecipesを実行します。

#[tauri::command(async)]
pub async fn get_category_data(category_name: String, window: Window) -> Result<(), String> {
    let (tx, mut rx) = mpsc::channel(32); // 32はバッファサイズ

    tokio::spawn(async move {
        let config = config::Config::new();
        let response: ApiResponseOfCategory = reqwest::get(&config.category_url)
            .await
            .unwrap()
            .json()
            .await
            .unwrap();

        let categories = extract_categories_from_response(&response, &category_name);

        for category in categories {
            let recipe_url = format!("{}{}", config.recipe_url, category.category_id);
            let response: ApiResponseOfRecipe = reqwest::get(&recipe_url)
                .await
                .unwrap()
                .json()
                .await
                .unwrap();

            // チャネルを通じてレシピを送信
            if let Err(_) = tx.send(response.result).await {
                return;
            }

            // 1秒待機
            sleep(Duration::from_secs(1)).await;
        }
    });

    let mut seen = HashSet::new();

    // メイン関数で受信と処理
    while let Some(recipes) = rx.recv().await {
        // 重複を見てあげる
        let mut new_recipes = Vec::new();
        for recipe in recipes {
            if seen.insert(recipe.recipe_id.clone()) {
                new_recipes.push(recipe);
            }
        }

        // ここで受信したレシピを処理
        let js_command = format!(
            "window.receiveRecipes({})",
            serde_json::to_string(&new_recipes).unwrap()
        );
        window.eval(&js_command).unwrap();
    }

    Ok(())
}

window.receiveRecipes

ここではrecipeListにRust側で取得したレシピを格納しています。

useEffect(() => {
    window.receiveRecipes = function(recipes) {
      useStore.setState((state) => ({
        recipeList: [...state.recipeList, ...recipes],
      }));
      
      onGetDataMatchingMaterial(materialName);
    };

    // クリーンアップ関数:コンポーネントのアンマウント時に実行
    return () => {
      window.receiveRecipes = null;
    };
  }, [materialName]);

window.recipeRecipes

window.recipeRecipesを使うためには、WindowというグローバルインターフェースにreceiveRecipesを追加する必要があります。

import { Recipe } from "./Recipe";

declare global {
  interface Window {
    receiveRecipes: ((recipes: Recipe[]) => void) | null;
  }
}

おわりに

Tauriで楽天レシピを取得するアプリについて、レシピ取得部分のみ解説してみました。
コード全体についてはGitHubリポジトリをご覧ください。

参考記事

楽天APIを使って人気レシピを取得してみた
Pythonで楽天レシピAPIからレシピを取得する

广告
将在 10 秒后关闭
bannerAds