しげぽん日記

技術屋の魂は失わない

Rustと私

このブログを書くのは5年ぶりらしい。

転勤を機に自宅をスマートホーム化したりしているが何も書いていない。

twitterでRustのコマンドラインツール群の紹介をしたい気分と書いたら、いいねがついちゃったので書かざるをえないので公開する。

 

機械学習に世の中がうぇーいする前から、私は諸々のツールの開発にPythonを使っていた。

Perlがイヤになったから。

Perlの事は私は当時Write Onlyが言語と呼んでいた。

同じ処理を記述するのに自由度が高過ぎて、自分で書いたコードでも後から読んで理解できない事が多かったから。

 

で、何にしよう?って思った時にインデントでスコープを表現するというただその1点と書き方の自由度が比較的少ない(これはそのうち自由度が高くなったというか、処理系にまかせた書き方にしないと処理が遅くなるので覚えないといけない事が増えた)のでこれにしようと思ったのだった。

 

最初に自宅をスマートホーム化した4年ほど前はPythonで書いていた。

 

一方、会社では、イントラのツールがクソ過ぎてつらかったので、改善するツールも書いていた。

最初は、Chromeプラグインを書いてブラウザの中でDOMを書き換えて、本来必要な情報を補完表示するようなものだった。

その時にJavaScriptを書くようになった。

けど、当時はJSはなんとなくまどろっこしくって嫌いだった。

だからChromeプラグイン以外は使わなかった。

しかし、Chromeプラグインはストア以外での配布がうるさくなってきたのでだんだん社内での配布も難しくなってきたので、イントラ改善ツールの提供方法としては適さなくなってきた。

 

このため、Webのフロントエンドも書く事を考え始めた。

最初はPython + Djangoで書いていた。

ただ、どうしてもPythonだとasyncを書くのがいまいちなのでフロントエンドにはあまり向いていないなと感じていた。

その時にVue.jsを使ってみてアプリを書いてみた。

なるほど、Node.jsというのはなかなかよくできているなと思った。

特に非同期が書きやすいのはすぐれていると思った。

処理系も世の中はどんどん進んでるんだと思った。

ただ、Vue.jsとReactとどっちがいい?って考えた時に、どうもReactの方がたくさん使われてそうなのでReactにしちゃおうって思った。

なかざんさんのブログとか見てたので、イントラの改善ツールもReact Native for Webで書いちゃえってなって、結局React Nativeで書いている。

会社で利用している LINC Biz(リンク ビズ)| ビジネスチャット × ビデオ会議 のプレゼンス情報を座席レイアウトに並べるアプリを作って、テレワーク環境でもなんとなくみんなそこにいる気分にさせてみたりとか。

一番は労務管理ツールなんだけど。

 

で、自宅のスマートホームもNode.jsに書き直してみた。

それなりに動いた。

現在の状況をモニターして、制御するUIを用意するくらいはどうとでもなった。

ただ、その時のシステム構成があまり気に入らなかったので、ずっとシステム構成をどうすべきか悩んでいた。

 

1. フロントエンドはLINCBizみたいなチャットツールにする
2. Prometheusに時系列DBを持たせてGrafanaで可視化する
3. 自宅のサーバで動かすけれどできるだけプアな環境でも動かしたい
4. 自宅のサーバで動かすけれどポートの外部公開はしたくない
5. 本当はGraphQL的なAPIも用意したかったけれど別にそれってWebSocketでセッションはってJSONの交換すればいいだけだからGraphQLにはこだわらない

あたりかなぁ…もう忘れた。

結局この時点でフロントエンドを積極的に自分で書く必要がなくなったので、自宅システムはできるだけ軽量な環境にのっけたいと思っていた。

 

で、GoかRustかなぁと思ったのだけどGoを最初に見た時に感じたのが、最初にJavaScriptを見た時と同じ感覚で、めんどくせって感じだったので、Rustにした。少なくともネイティブのバイナリまで落としこんでしまうその潔さにほれたという感じはある。

 

ということでRustにどっぷりはまりこんでいる。

 

結局のところ現時点ではフロントエンド書くならReactでそれ以外は全部Rustという感じになっていて、もはやPython書けなくなってきていると思う。

OpenCVで手軽に書く時だけPython使うかもしれない。

Rustのsysクレートをクロスコンパイルする環境を整備するのが面倒で、ARMクロスでバイナリを作りたい時に、opencv_sysをビルドできないためだ。

RasPi上で監視カメラの画像をフレーム間差分抽出してmp4にエンコードするなんてのは、Rustで書かずにPythonで書いた方が手間が少ないのでそういう用途ではまだ使っている。

 

でも、会社でExcelを読み書きしたり、イントラ改善する際のバックエンド処理(WebSocketのサーバとか)とか、オレオレRPAツール(まぁただのクローラーだけど)とか、チャットボットとか、自宅スマートホームとか、そういうのはもはや全部Rust化してしまった。

 

あと、Rustだけどできるだけ、gnueabiではなくmusleabiを使うようにしている。

Dockerコンテナにする際にmuslにする事でイメージのサイズを極限まで小さくできるため。

DockerfileはFROM scratchで書き始めておいて、OpenSSLの証明書とtzinfoだけホストマシンのファイルをmountすれば、極小のDockerイメージが作れる。

まぁ複数のコンテナを同時に走らせる運用をしていないので、もはやDockerコンテナ化する必要もないので、最近は普通にsystemdに登録して動かしているけど、それにしてもOSに依存するバイナリの数を極力減らせるので、変な依存関係の解決のための諸々の手続を省略できる。

 

上記のRustのmuslビルド環境はWSL2上に構築していて(自宅も会社もメインはWindowsなので…)、Dockerデーモンを起動するのが面倒なので、WSL2上に↓のイメージと同じ環境をDockerfileと同じように展開して作っておいて、デフォルトのビルドターゲットをmuslにしてビルドするようにしている。

 

GitHub - emk/rust-musl-builder: Docker images for compiling static Rust binaries using musl-libc and musl-gcc, with static versions of useful C libraries. Supports openssl and diesel crates.

Linuxディストリビューションが何だろうがmuslビルドしているのでバイナリのまま配布できる。

ソースコード管理はGiteaでやってるけどGiteaのリリースにバイナリをそのまま添付しておいて、実行するサーバでバイナリをダウンロードして、実行するだけですぐにサービスが動いちゃうので、最近はイントラ改善ツールはホストして公開するのではなくて、自分でサーバとして動く単一バイナリを会社で配布している。

ただ、動かす人に対してハードルあげてるのはたしかなのでちょっと考えた方が良いかもしれないけど、WindowsLinuxで1つずつバイナリを用意しておけば、どんなディストリビューションでも基本動くサーバ構築できるので、非常にポータビリティは高いと思う。

ついでに言うと、フロントエンドで動かすReact Nativeについてもzipで圧縮して上記のサーババイナリの中にinclude_bytes!で埋め込んでおき、起動時に自分でカレントディレクトリに展開するように作っているので、Webフロントエンドつきサーバだって1バイナリで配布できてしまう。

React Nativeももはや面倒なのでExpo使っておけばビルドするのもコマンド一発なのでそこは手をかけないようにしている。

どうせ私の書くのはお遊びレベルを越えないので。

 

ということでRustaceansの端くれとなって経緯を書いてみました。

あーツールの紹介してねーなっていうので、cargo install-update -lの結果だけさらしておきます。

Windows環境での実行結果。

 

Package       Installed  Latest   Needs update
bandwhich     v0.20.0    v0.20.0  No
bat           v0.17.1    v0.17.1  No
cargo-update  v5.0.0     v5.0.0   No
du-dust       v0.5.4     v0.5.4   No
fd-find       v8.2.1     v8.2.1   No
grex          v1.1.0     v1.1.0   No
hyperfine     v1.11.0    v1.11.0  No
nu            v0.26.0    v0.26.0  No
procs         v0.11.3    v0.11.3  No
racer         v2.1.44    v2.1.44  No
ripgrep       v12.1.1    v12.1.1  No
sd            v0.7.6     v0.7.6   No
starship      v0.50.0    v0.50.0  No
streampager   v0.9.2     v0.9.2   No
tealdeer      v1.4.1     v1.4.1   No
tokei         v12.1.2    v12.1.2  No
zoxide        v0.5.0     v0.5.0   No

 

nuが肝になっていて、実はzoxideとかはnuに対応していないので、実質使えていないのだけど、とりあえず入れたままにはしている。

あと、vimの外部コマンド実行もnuは対応していないので、vim.rustとかはnu経由では使えないので、仕方ないからbash経由でvimは起動しないといけない。

まぁメインのエディタは例に漏れずVSCodeなのだけど、コマンドラインでちょいちょいっといじりたいケースもあってvimも使ってるので…。

 

まぁ入れてはみたけど使ってないものも多いかな…

よく使うのは…

  • bat
  • cargo-update
  • fd-find
  • nu
  • ripgrep
  • starship
  • streampager … Windowsだけ

かな。

byobuのデフォルトシェルをnuにしておいて、byobuを起動したらnuが起動するけど、そうじゃない場合はbashが起動するようにしておけば、byobuのtmuxが固まったりしても非byobuなシェルを用意できるのでnuのstartupに以下のようなコマンドを書いておいて、

def ssh [host] { ssh -t $host byobu }

 ssh ホスト名でリモートログインした場合はbyobu + nuが起動、^ssh ホスト名でリモートログインした場合は、bashが起動、といった形でbyobuの設定でのログイン時に自動でbyobuを起動するオプション(よくはまる)をオンにしない運用にしている。

このへんはnuの話なのでまた別途トピックを分離して書こう。

nuってドキュメントがいまいち(リリースノートがドキュメントになってる…)とかいうのもあるので癖はあるけど非常によくできているので、そのへんの事もどっかで書いてみる。

starshipはnuを入れる前にbashpowershellに導入してみたけど見た目が統一されるのも含めて非常に良い感じ。あまりにも統一され過ぎて、今、自分がWindowsのネイティブなのか、WindowsのWSLなのか、リモートのLinuxなのか、区別つかなくなってしまうくらい…。

その上シェルまでnuで全部統一したので、byobuのstatuslineでホスト名出しておかないと、さっぱりわからなくなってしまう…

なのでWindowsのネイティブ環境でも動くbyobuがあると良いなぁ…と。

まぁ当座はWindows Terminalのタブを見て推定するしかないかなと…。

Windows Terminalもいきなりnuが起動するようにしていて、かつ、デフォルト起動にしているので、シェルスクリプトの書き方で悩む事は減った。

nuのスクリプトの書き方はクセはあるけれど、トライアンドエラーで書けるし、Rust的な感じでエラーメッセージ眺めりゃなんとかなくので、WindowsネイティブでもLinuxでも同じスクリプトで同じ処理(Giteaに最新リリースを追加してビルドした成果物をリリースにアップロードする、とかそういうの)を書けるので、かなりプラットフォームを意識しないで作業できる環境が整いつつある。

 

Windowsでbat動かしてもページャーがきかなかったのはずっと悩ましいなと思っていたのだけど、streampagerを入れたら解決した。Linuxでやると、byobuとの相性が悪いのか、streampager起動後はカーソルの表示位置がおかしくなってしまうので、Linuxでは、pagerはbatを使うようにしている。

alias less = bat --paging=always --color=always -p
alias cat = bat --paging=never --color=always -p

 と書いておけば、悩まずに使えるので。

Windowsの場合はlessは

def less [file] { bat --paging=never --color=always -p $file | sp }

と書いておけば同様に使えるのでそれで良いことにしている。

WSL2側のcargo install-update -lは以下でもうちょっと入ってる。

Package          Installed  Latest   Needs update
bandwhich        v0.20.0    v0.20.0  No
bat              v0.17.1    v0.17.1  No
cargo-audit      v0.13.1    v0.13.1  No
cargo-deb        v1.29.0    v1.29.0  No
cargo-update     v5.0.0     v5.0.0   No
du-dust          v0.5.4     v0.5.4   No
exa              v0.9.0     v0.9.0   No
fd-find          v8.2.1     v8.2.1   No
gitui            v0.11.0    v0.11.0  No
grex             v1.1.0     v1.1.0   No
hyperfine        v1.11.0    v1.11.0  No
mdbook-graphviz  v0.0.2     v0.0.2   No
nu               v0.26.0    v0.26.0  No
procs            v0.11.3    v0.11.3  No
racer            v2.1.44    v2.1.44  No
ripgrep          v12.1.1    v12.1.1  No
rmesg            v1.0.13    v1.0.13  No
rustscan         v2.0.0     v2.0.0   No
sd               v0.7.6     v0.7.6   No
starship         v0.50.0    v0.50.0  No
streampager      v0.9.2     v0.9.2   No
tealdeer         v1.4.1     v1.4.1   No
tokei            v12.1.2    v12.1.2  No
ytop             v0.6.2     v0.6.2   No
zoxide           v0.5.0     v0.5.0   No

まぁ入れたけどWindowsで積極的に使ってるもの以外はあまり使ってないかも。

あとは、ARMコアのNASでも動くように、上記のソフトウェアはsrcをダウンロードして、

GitHub - messense/rust-musl-cross: Docker images for compiling static Rust binaries using musl-cross

を使ってビルドして/opt/binとかにインストールするようにしている。

NASのシェル環境を変えられたのはかなり大きい。こいつもbyobuがないのだけど。

 

だいぶ書いたのでいっぺん公開する。

また次気が向いたら書きます。

 

嫁さんに自分の居場所を正直に申告する

ひたすら自動化正確、その9。今どこにいるのか嫁さんからわかる状態にしておく事は、いろんな意味で重要であると考えている。帰ってくるのが遅いので事故でもしたんじゃないかと思っても、位置情報が会社になってると、まだ仕事してるのか、と思うだけだし、それだけで意味がある。しかし、位置情報を嫁さんにだけうまく伝える方法が最近ではあまりないような気がしている。あるのかもしれんけど知らん。だったら安心ナビもどきを自動化の中で作ってしまえと思ってしまった。と、ここまで書いて、そう言えば安心ナビって今でもサービスしてるんだったと思い出した。まぁいいや。さすがに「探される方」に40のおっさんを指定するのって、お店の人に絶対そういう意味にとられるだろうから、申込めるわけがないし。

  1. Tasker: Received Text [ Type: SMS, Sender: 嫁さん, Content: イマドコ? ] → Get Location [ Source: GPS ] + Calendar Insert [ In/For: 15minutes, Title: イマココ, Description: "", Location: %LOC ]
  2. myThings: Google Calendarで特定のイマココを含む予定の開始時刻の5分前になったら、Gmailで嫁さんにGoogle Mapへのリンク(https://maps.google.com/maps?q={{場所}})を書き込んだメールを送信する

Google Calendarを経由するあたりが異常にトリッキーだし、SMSで受けてるのにEmailで返しちゃうあたりやっぱりトリッキーだし、myThingsでイベントが発生したかはどうやら15分間隔でポーリングしているようなので(絶対にイベントが発生するようなトリガーを設定して時刻未指定にすると15分おきに発動する。かけなびさんの日記のタイトルを読み上げる自動化を設定したら、15分おきにかけなびさんの日記のタイトルをエアコンが連呼していた。)カレンダーに15分後の予定を設定してポーリングのタイミングでようやく通知されるとか、今知りたいのにめっちゃゆっくり教えてくれるとか使い勝手悪いし、そもそも嫁さんがこの方法で私の居場所がわかる事を覚えていないし、作ってはみたものの使われてはいない。まぁ無理やりにでもやればできなくはないという1つの事例という事で。私に嫁さん以外からイマドコ?ってメール投げても位置情報は返りませんので、私の電話番号を知っててもSMS投げてこないように。

車の中では通知読み上げ

ひたすら自動化生活、その8。車を運転している最中にスマホのNotificationが届くと、どうしても気になってしまう。でも運転中にスマホを注視すると道路交通法違反でつかまってしまう。じゃあどうするか。正確に理解できなくってもいいので、とりあえず音声で教えてもらう事にする。アクションが必要なら自動車を駐車してから行えばいいし、駐車するかどうかの判断くらいできれば良いかなと。

  1. Tasker: BT Connected [カーナビのBT] + Notification [ * ] → Say [ Text: %NTITLE ]

ちなみにTTSはKDDI研究所さんのN2TTSにお世話になっている

play.google.com

自動車の中で音楽を聞く時にはGoogle Play Musicを利用しているので、カーナビのオーディオは常時BTを向くようにしている。なので、Notificationが届いてスマホがしゃべり出すと、ちゃんとカーオーディオからTTS発話で通知の内容が聞こえてくる。嫁さんからメールが届いていれば買い物してこいという指示かもしれないので駐車して内容を確認するという事が、自宅の駐車場に到達する前に実現できるのである。ただ、今電話帳に入れている嫁さんの名前が非常に短い名前になっているので、「要注意!この人の連絡は無視するといけないぜ」みたいな長い名前にしておくと、車の中でも確実に嫁さんからの連絡だと気付くに違いない。しないけど。

あと、子供が一緒に乗車していてDVD見せろと言わない限りは。DVDを見ている時はあきらめるしかない。車の中でDVDを視聴しているような状況で、スマホ本体がしゃべったところでどうせ聞こえないし。

出張の日はアラームを自動的に設定する

ひたすら自動化生活、その7。出張の日はアラームを自動的に設定する。出張の日はたいてい普段出社する日よりも早起きが必要。飛行機の始発は7:30くらいなので5:30起き、新幹線の場合は大阪に9時過ぎに入ろうと思うと5:00起きになる。寝過ごすとつらいのでアラームを自動的に設定する。

  1. myThings: Google Calendarで東京出張を含む予定の開始時刻が近づいたら自分に通知する
  2. Tasker: Notification [Owner Application: myThings, Title: 東京出張] → Set Alarm [Time: 5:30, Message: 東京出張]

同様に大阪出張の場合で5:00という組み合わせもmyThingsとTaskerの両方に設定しておく。そうすると以下の自動化とのコンボで以下のような挙動となる。

Outlookに出張という分類項目の予定を追加すると、Google Calendarにタイトルと時間と場所だけコピーする - しげぽん日記

Outlookに出張というカテゴリで「東京出張」という終日スケジュールを入力すると、それが自動的にGoogle Calendarに同期される。この予定の5分前(つまり出張する前の日の23:55)くらいになると、myThingsによって端末にNotificationがとんでくる。このNotificationを受けてTaskerがアラームをセットする。

なのでOutlookでその日だけ早起きするという事を予約できるようになる。

でも、最近、この組み合わせが正しく動作していないような気がする。Taskerがうまく反応していない模様。手動でTask実行すると設定されるようなので、Profileの設定が何か間違っているのかもしれないけれどよくわからん。結局、手動でアラームを設定している。

さらに言うと本当はActivite Popと連動させてサイレントアラームが設定できるとうれしいのだけど、withingsさんにはまだそういう機能は用意されていない模様。withingsさんのAPIの拡充が望まれる。

さりげなくテザリング

ひたすら自動化生活、その6。どうしても、出先でPCをネットにつながないといけない。充電もしないといけないのでUSB接続もする。設定変えてケーブルさしてと、まあまあ手間かかる。だったら、自動化で手間を減らしちゃえと。

  1. Tasker:USB Connected [ Class:Mass Storage ] → WiFi Tether [ Set:On ] 
ただの充電でWi-Fiテザリングになると嫌なのでUSBマスストレージじゃないと反応しないようにしている。これで、ケーブルさして充電してる風に見えるけど実はテザリングしてるにできます。

ただし、充電しようと思ってPS4のコントローラの代わりにスマホをさしてもWi-Fiテザリングしちゃう。ちゃんと充電器にさそう。

特定アプリは画面自動回転

ひたすら自動化生活、その5。自動化の自動化です。寝転がりながらスマホいじってると、「縦レイアウトで見てるつもりなのに画面が回転してしまう」ってあるあるだと思います。なので、画面の自動回転を使わないこと多いんじゃないかと思います。私だけか。でも、動画見たりする時はやっぱり回転させたい。設定から変更するの面倒。そんなあなたに朗報です。

  1. Tasker: Application: マルチメディア系のアプリ → Display AutoRotate [ Set:On ] 
アプリに応じて自動回転に設定を変えてくれるので、ごろ寝も動画もどっちも対応できます。

ただし、動画見ながらごろ寝するとやっぱり「あーっ」てなります。

特定アプリは無音で動かす

ひたすら自動化生活、その4。スマホでゲームアプリをダウンロードすると、音量設定できる画面に到達する前に、派手な音楽が流れて電車の中でダウンロードしたりすると顰蹙かったりする事も経験上あったりするんじゃないかと思います。私だけか。もちろん、マナーモードにすりゃいいんですが、嫁さんからの重要な連絡とかは、マナーモードにしてると気づかなかったりするので、どっちもどっち。なので、私はアプリケーション毎に無音設定しています。

  1. Tasker:Profile: Application: 特定のアプリ → Media Volume Level:0
これをアプリの初回起動前にやってしまうと、音が鳴る心配がなくて良いです。

てか、最近、xdaでも似たような記事が増えてるけど、たまたまです。