素振りのために Rust と Elm でRSSビューア作った

リポジトリは [zaneli-sandbox/rssss](https://github.com/zaneli-sandbox/rssss)。

![rssss.gif](https://qiita-image-store.s3.amazonaws.com/0/2841/b50c87c0-8229-eaff-ab2d-1a1de94106d1.gif)

RSS の URL を入力してその場で内容を表示できるだけのもので、特に面白みもないが練習用という事で。

とりあえず、以下のRSSには対応できていそう。

* Qiita: http://qiita.com/tags/rust/feed.atom
* はてなブログ: http://staff.hatenablog.com/rss, http://staff.hatenablog.com/feed
* GitHub: https://github.com/zaneli-sandbox/rssss/commits/master.atom
* medium: https://medium.com/feed/@medium
* note: https://note.mu/info/rss
* Tumblr: https://staff.tumblr.com/rss
* YouTube: https://www.youtube.com/feeds/videos.xml?channel_id=UC4QobU6STFB0P71PMvOGN5A
* ニコニコ動画: https://www.nicovideo.jp/newarrival?rss=2.0
* 私の個人ブログ: https://www.zaneli.com/blog/rss

使ったものは

- Rust
  - [actix-web](https://github.com/actix/actix-web): Rust で Web アプリを書くならデファクトスタンダードかな、と思い。まだデファクトというほどのものが確立していないのかもしれないけれど。
  - [xml-rs](https://github.com/netvl/xml-rs): XMLプルパーサ。[serde_json](https://github.com/serde-rs/json) のように `from_str()` とかで struct を作ってくれるようなやつが見つからなかったので面倒だったけど、各サービスによって微妙に要素名や構造が違っていたりしていたので取り回しが効いて良かったかもしれない。
  - [scraper](https://github.com/programble/scraper): RSSの `<description>` の値はHTMLタグを含んでいるので、そこから雑にテキストだけを取り出すために使用。
- Elm
  - [Create Elm App](https://github.com/halfzebra/create-elm-app): Elm はもとよりフロントエンド・js文化も不慣れだったので雰囲気で使って雰囲気で動かしているが助かっている。
- CSS
  - [Bulma](https://bulma.io/): [elm-bulma](https://github.com/surprisetalk/elm-bulma)というものもあり Elm 0.19 にも対応していたが、それは使わず直接 Bulma を使用した。フロントエンド力が低くてもそれなりの見栄えになってありがたい。

以下、作っていてハマった事などをつらつらと。

### [Rust] future の型合わせ

レスポンスステータスによって、RSSをパースするか否かを分ける処理をこのように書こうとしていたが、

```rust
let status = res.status();
if status.is_success() {
        res.body()
            .from_err()
            .and_then(|b| match rss::parse_rss(b) {
                Ok(r) => Ok(HttpResponse::Ok().json(r)),
                Err(e) => Ok(e.into()),
            })
} else {
    future::ok::<HttpResponse, Error>(
        HttpResponse::build(res.status()).finish(),
    )
}
```

返す型が `futures::AndThen` と `futures::FutureResult` で異なるためコンパイルエラーとなった。
このあたり、 Scala と同じような感覚で書こうとしていてなかなかコンパイルを通すのに苦戦した。
[`Box`でくるんで](https://github.com/zaneli-sandbox/rssss/blob/386fd98e207249b4cc0552eefbf8cb2c17a7eba9/backend/src/main.rs#L63-L88)何とかコンパイルを通したが、これが正攻法なのかどうかがイマイチよく分かっていない。
`Future` に対する操作がそれぞれの型を返すのは、コンパイル時にサイズが決まっていないといけないとか、その辺の兼ね合いだろうか？ :thinking:

### [Rust] エラーの型合わせ
以下の記事が参考になった。

- [Rustのエラー処理](https://qiita.com/fujitayy/items/cafe661415b6aa33d884)
- [rust のエラーライブラリは failure を使え！](https://qiita.com/legokichi/items/d76b6aa5dac2ad781bda)

…が、何とも面倒だなー、という印象が拭えない。
この辺は発展途上な部分なのかもしれないが。

### [Rust] `actix_web::client` の使い勝手

30x を返す際にリダイレクトに対応していなかった。
[reqwest](https://github.com/seanmonstar/reqwest) とかを使えばもう少し楽になったのかもしれないが、今回は練習という事で自分でリダイレクトを解決する処理を書いてみた。
(余談だが、せいぜいリダイレクト1回のみに対応すればええやろと思っていたところ、`http://qiita.com/tags/Docker/feed.atom` には2回リダイレクトが必要だったため3回まで許容するようにした。)
また、Cargo.toml の dependencies に `actix-web = { version="0.7", features=["ssl"] }` と指定しておかないと https リクエストが投げられなかったり。
この辺はまぁ、知ってしまえばそれまで系だが。

### [Elm] ステータスコードによって Json Decoder を分けたい
200なら通常の型、40x系ならエラーメッセージ用の型、のように分けたかったが、
標準のexpectJsonは20x系しか扱えないようだった。
[Twitterでも助言をもらい](https://twitter.com/miyamo_madoka/status/1083671023201935360)、[`Http.expectStringResponse` を呼んで `Http.BadStatus_`, `Http.GoodStatus_` を扱う処理を書く](https://github.com/zaneli-sandbox/rssss/blob/386fd98e207249b4cc0552eefbf8cb2c17a7eba9/frontend/src/Main.elm#L139-L157)事で対処した。

### [Elm] バージョン0.19未満の情報がヒットしたり、使えそうなライブラリが0.19対応してなかったり
悲しい。 :cry:

---

…と、色々書いてはみたが、普段使わない言語で何か動くものを書いてみるのは凄く楽しかったし、書いてみて初めて躓く事も分かり良かった。
次は、DBアクセスしたり複数画面に渡る何かを作ってみようかな。
今回は Main.elm に全部押し込んだけど、ソースファイルの分け方もまだあまり分かっていない。

Rust や Elm の強い人の目に止まって、何かしら助言などいただけると幸いです。