せっかくなのでRust(tauri)バージョンの占いアプリのコードも公開

最近ちょっと話題になっている、バックエンドをRustで書けるElectron『Tauri』で恒例の占いアプリを作ってみました。今回はフロントエンドはsvelteを選択しています。

プロジェクトの作成についてはnpmを利用しているのですが、そのあたりはこの記事では割愛させていただきます。ネットで探せば普通に書いてあるので問題ないかと。ちなみに分けて書くのは面倒なのでファイルごとに書いていきます。

ちなみに今回この記事に登場するのは

  • src/App.svelte

  • src-tauri/src/main.rs

  • src-tauri/cargo.toml

の3つのファイルです。もしエラーが出るのであればその部分は各自で修正をお願いします。

App.svelte

<script>
	import {invoke} from "@tauri-apps/api/tauri";
	import {getName, getVersion} from "@tauri-apps/api/app";
	import {ask,message} from "@tauri-apps/api/dialog";
	import {platform} from "@tauri-apps/api/os";
	import {requestPermission,sendNotification} from "@tauri-apps/api/notification";

	//権限の確認(一度認証すると次からは自動っぽい)
	requestPermission().then(function(res){
		console.log(res);
	})
	//OSの通知機能へメッセージを送信する。
	sendNotification("tauriテストプログラムを起動しました");

	//アプリ名の取得
	getName().then(function(res){
		console.log(res);
		});
	//アプリバージョンの取得
	getVersion().then(function(res){
		console.log(res);
		})
	//実行中のプラットフォームの取得
	platform().then(function(res){
		console.log(res);
	})
	//メッセージの表示(JSのalertと同等)
	message(`メッセージです`);

	let name = "";
	let inputText = "";
	let receiveWord = "";
	let fortune = "";
	let num = 0;

	function deleteName(){
		receiveWord = "";
		name = "";
		fortune = "";
		num = 0;
	}

	function callRust(){
		ask(`${inputText}でいいですか?`)
		.then(function(res){
			if(res){
				name = inputText;
				inputText = "";
				num = 0;
				if(fortune.length != 0){
					fortune = "";
				};

				let sendto = (name.length == 0? "nothing":name);
				invoke("connect_js",{word: sendto})
				.then(word => {
					receiveWord = word;
				}).catch(word => {
					console.log(word);
				});
			}else{
				inputText = "";
			}
		})
	};

	function callFortune(){
		num++;
		invoke("return_fortune")
		.then(function(res){
			fortune = res;
		})
	}

	function onKeyPress(event){
		if(event.key == "Enter"){
			callRust();
		}
	}

</script>

<main>
	{#if name.length == 0}
	<h1>占いアプリへようこそ!</h1>
	<p>まずは名前を登録してください。</p>
	{:else}
	<h1>こんにちは,{name}さん。</h1>
	<h2>{receiveWord}</h2>
	{/if}

	<input type="text" bind:value={inputText} on:keydown="{onKeyPress}">
	<button on:click="{callRust}" disabled={inputText.length == 0}>登録</button>
	<button on:click="{deleteName}" disabled={receiveWord.length == 0}>リセット</button>

	<div>
		<button on:click="{callFortune}" disabled={name.length == 0}>占ってみる</button>
	</div>
	{#if name.length != 0 && fortune.length != 0}
	<p class="fortune-text">{name}{@html fortune}</p>
	<p>({name}さんは現在{num}回占っています)</p>
	{/if}
</main>

<style>
	main {
		text-align: center;
		padding: 1em;
		max-width: 240px;
		margin: 0 auto;
	}

	h1,h2 {
		color: #ff3e00;
		text-transform: uppercase;
		font-size: 3em;
		font-weight: 100;
	}
	h2{
		font-size: 2.5em;
	}

	.fortune-text {
		border: 1px solid;
		border-radius: 10px;
		background-color: cornsilk;
	}

	@media (min-width: 640px) {
		main {
			max-width: none;
		}
	}
</style>

ちょっとtauriのapiを試していたのでゴチャゴチャしていますが、svelteはVueなどのフレームワークと同じように一つのファイルにhtml・css・jsを書いていくスタイルです。

基本はinvokeコマンドでRust側に定義した関数を呼び出せます。window.__TAURI__.invokeの方法もありますが、こちらを使用する場合には先にtauri.conf.jsonファイルのbuildセクションに"withGlobalTauri": trueの項目を追記する必要があるようです。

main.rs

#![cfg_attr(
    all(not(debug_assertions), target_os = "windows"),
    windows_subsystem = "windows"
)]

#[tauri::command]
fn connect_js(word: &str) -> Result<String, String> {
        println!("呼び出し成功:{}",word);
        if word == "nothing" {
            Err(format!("入力されていません"))
        } else {
            Ok(format!("welcome to rust!"))
        }
    }

use rand::Rng;
#[allow(unused)]
struct Fortune {
    pub luck: String,
    pub text: String,
    pub face: String,
}
impl Fortune {
    fn new() -> Fortune {
        //運勢の一覧(リスト)
        let lucks = ["大吉", "中吉", "小吉", "吉", "凶", "大凶"];

        //運勢の一覧からランダムで選択
        let num = rand::thread_rng().gen_range(0..=5);
        let luck = lucks[num];

        let mut word = String::new();
        let mut face = String::new();
        let happy = "おめでとうございます。明日もいい日でありますように…。";
        let normal = "まずまずの一日になりそうですね。";
        let bad = "今日は大人しくしておいたほうがいいでしょう。";
        match luck {
            "大吉" | "中吉" => {
                word.push_str(happy);
                face.push_str("ヽ(=´▽`=)ノ");                
            },
            "小吉" | "吉" => {
                word.push_str(normal);
                face.push_str("(∀`*ゞ)テヘッ");
            },
            _ => {
                word.push_str(bad);
                face.push_str("(´・ω・`)ショボーン");
            },
        }
        Fortune {
            luck: luck.to_string(),
            text: word,
            face: face,
        }
    }
}

#[tauri::command]
fn return_fortune() -> String {
    let fortune = Fortune::new();
    println!("{}", fortune.luck);
    format!(
        "さんの今日の運勢は『{}』です!<br/>{}<br/>以上、今日の運勢でした{}",
        fortune.luck, fortune.text,fortune.face
    )
}

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

最初から書かれている部分を除くと書き加えたのはrandクレートを利用するためのuse文、呼び出されるメソッドとinvoke_handler、それに[tauri::comand]の注釈などの部分だけです。Rustの書き方自体は理解しやすいと思うのでここでの説明は省きます。というより自分自身で間違わずに説明できる自信がありません。

まあ、基本的にはこの2つのファイルを弄れば簡単にアプリが作成できるみたいです。

cargo.toml

こちらは単に[dependencies]以下に

rand = "0.8.5"

を付け加えるだけです。これがないとmain.rs内のrandクレートを使ったランダム選択部分である以下の部分、

let num = rand::thread_rng().gen_range(0..=5);

これが実行できません。

最後に

あとはこれでcargo tauri dev(もしくはnpm run tauri dev)実行してやればコンパイルが始まり、成功すれば起動します。ただ自分の環境ではコンパイル時にファンがなかなかの勢いで回っていたため、ちょっと処理が重いのかもしれません。

それと正直言ってRustがバックエンドだからと軽快に動くわけではないみたいです。なぜかところどころで引っかかりを感じる部分もありました。もちろんこれは環境にもよるのかもしれません。

試しに作ってみただけなのでだめな部分もありそうですが、まあ一応動きはします。あとのチューニングに関しては興味のある方におまかせしたいと思います。

ひとまず以上が占いプログラムのRustバージョンのコードになります。とは言っても大半はsvelteファイルにHTMLとCSSとJSを書いただけなのでこれをRustで、と言ってもいいのかという疑問も無いわけではないですが・・・。

この記事が気に入ったらサポートをしてみませんか?