RustとLambdaでなんか作る 後編 （リクガメの食べ物しらべるghomeアプリ作った)

[RustとLambdaでなんか作る 前編](https://qiita.com/ikegam1/items/d4b56756557dbc86d97d)
[RustとLambdaでなんか作る 中編](https://qiita.com/ikegam1/items/0537ba51ac4ddbe02a8e)

はい。後編でございます。
作るアプリ考えました。
後編とはいうものの、ここまでHello worldしかやってないのでこれ一個で完結しちゃったりもします。

## 何を作る？

アプリ名：リクガメの食べもの

リクガメにあげてもいい餌を教えてくれるアプリ。

**俺**: リクガメの食べ物につないで
**Home**: はい、リクガメの食べ物です
**俺**: ピーマンあげていい？
**Home**: はい。ピーマンはあげていい食べ物です。カルシウムは少ないです。(終了)

みたいな、シンプルなものにします。
webhook側は一度リクエストを受け取ってレスポンスを返せばOK.
問いと回答は静的に用意しておく。

## google home アプリのとこ

| 項目      |       内容|
|:-----------------|:------------------|
| アプリ名 |リクガメの食べもの |
| project name|kamefood|
| Category| Education & referrence|
| Actions| Dialogflow|
|Intents| askIntent, wellcome, fallback|
|Entities| food|

askIntentというのがあり、例えば「**小松菜**上げていい？」と問いかけます。
すると**小松菜**がfoodというEntityになってRequestに乗っかってきます。
それはqueryResult.queryTextではなく、queryResult.Parametersの中にfoodとして入ってきます。
それを受けてそれに対するワードを返すところをRustで作る。

## ソース

main.rsのみとなっていましたが、分けたらどうなるのかなと思ってfood.rsというのも作成してみました。
foods.rsは単にHashを保持して、キーに応じてvalueをリターンしてるだけ。
キーが食べ物でgoogleが投げてくるやつです。
そしてmain.rsはそれを投げる人で受け取る人でdialogflowに返す人です。
少し苦戦した箇所はあるけどそれは最後のまとめで。ひとまず動きました。

``` Rust:foods.rs

pub mod foods {
  use std::collections::HashMap;

  pub fn get(s:&str) -> String {
    let mut f = HashMap::new();
    f.insert("小松菜".to_string(), "<speak>はい。小松菜は、<prosody volume=loud>あげても大丈夫です。</prosody>カルシウムも豊富でおススメの食べ物です。ではまた。</speak>");
    f.insert("水菜".to_string(), "<speak>はい。水菜は、<prosody volume=loud>あげても大丈夫です。</prosody>カルシウムも豊富でおススメの食べ物です。ではまた。</speak>");
    f.insert("チンゲンサイ".to_string(), "<speak>はい。チンゲンサイは、<prosody volume=loud>あげても大丈夫です。</prosody>カルシウムも豊富でおススメの食べ物です。ではまた。</speak>");

...(略)...
 
    return format!("{}", f.get(s).as_ref().unwrap());
  }
}
```

``` Rust:main.rs
use std::error::Error;

use lambda_http::{lambda, Request, Response, Body};
use lambda_runtime::{error::HandlerError, Context};
use log::{self, info};
use simple_logger;

mod foods;

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;

#[derive(Debug,Deserialize,Default)]
struct Req {
  #[serde(default)]
  queryResult: QueryResult
}

#[derive(Debug,Deserialize,Default)]
struct QueryResult {
  #[serde(default)]
  queryText: String,
  #[serde(default)]
  parameters: Parameters
}

#[derive(Debug,Deserialize,Default)]
struct Parameters {
  #[serde(default)]
  food: String
}

fn main() -> Result<(), Box<dyn Error>> {
    simple_logger::init_with_level(log::Level::Info).unwrap();
    lambda!(my_handler);

    Ok(())
}

fn my_handler(e: Request, c: Context) -> Result<Response<Body>, HandlerError> {
  let query: Req = serde_json::from_slice(e.body().as_ref()).unwrap();
  info!("{:#?}", query);
  let food: String = query.queryResult.parameters.food;
  info!("{}", foods::foods::get(&food));
  Ok(
    Response::builder()
     .status(200)
     .header("Content-Type", "application/json; charset=UTF-8")
     .body(
       format!(
        r#"
          {{
            "fulfillmentText": "{}"
          }}"#,
        foods::foods::get(&food)
        ).into(),
      )
     .expect("none")
  )
}
```

## actions on google や dialog flowの設定

スクショなしで簡潔に書きますね。
基本的な手順は[前回](https://qiita.com/ikegam1/items/0537ba51ac4ddbe02a8e)と同じです。

### dialogflow
- askIntentを追加しました
 - Traning phrasesには「小松菜は大丈夫」のようなパターンを羅列しています
 - 「小松菜」といった食べ物の名前には@foodのEntitiesを設定しています
 - webhookを有効にしています
 - Set this intent of conversationを有効にしています
 - @foodはREQUIREDなパラメータにして絶対入ってくるようにする
- fullfillment
 - webhookurlをlabmda(api gateway)のURLにしています

### actions on google
- アプリ名とかもろもろは前述
- 画像は192x192と1920x1080を用意して登録しないといけないです
- プライバシーポリシーはwordpress上で一枚用意してます。たったこんだけ->http://ikegami.tokyo/2019/01/21/kamefood-pp/
- simuraterでテストしてOKだったのでOverviewからReleaseを選択し、SUBMIT FOR PRODUCTION

## まとめ

![64.png](https://qiita-image-store.s3.amazonaws.com/0/308709/c91e4d79-8912-45b6-10c8-291ff935ebd6.png)

そんなわけで申請中です。
ちなみにgoogle home、alexa、clovaがスマートスピーカーの代表的なとこですが、googleが一番審査早く、審査自体も通りやすいと思ってます。リジェクトされても指摘箇所直せばサクッと通ります。逆にalexaが一番厳しいですかね。

さて、最後にRustを味見してみた感想。
まだ10%も使えてないと思うけどそれを加味して聞いてくださいませ。

- 思ったよりは使いやすい
- スクリプト言語出身な人でも、ミュータブルな変数もあるわけだし、意外と自由に書こうと思えば書ける(それが良いとは言ってない）。特にnodejsな人は馴染みやすそう。
- 独特な記法についてはまだよくわからない
- 調べてみるとawsのsdk的なの(rusoto)があるし、今回みたいな用途で使ってもいいのかも。
- コンパイラが親切。根気よくダメ出ししてくれる。
- Optionとかunwrapとか最初よくわかんなかったけど、慣れると恩恵大きそう
- 好きか嫌いかと言えば好き

そんなわけでもうしばらく触ってみようと思います。
今回のも正常系しかほぼ想定してなくて、アカンコードですしね。まずはここを直していこう。テストコードも書いておきたい。
そしてその次は何つくろ。・・仕事でこっそりバッチ作ってみようかなぁ。

https://github.com/ikegam1/ghomeapp-lambdaonrust-kamefood

##追記
https://assistant.google.com/services/a/uid/0000008de1f59b3d?hl=ja
半日くらいで公開されました。画像適当すぎたなあ。。

そんなわけで、スマートスピーカー、めっちゃ敷居低いっすよ。なんか思いついたら是非。なんか使いたい言語使ってみるとなお良いかも。