choosenimを読む - Day2 -

# Day2 src/choosenimpkg/telemetory.nim

Day2はtelemetory.nimを読んでいく。
私は賢くないので何度もコードを読み返していくが、賢い人は流し読みで理解したら読まなくていいと思う。

## 初手

とりあえずアウトラインを眺めてみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/a34db299-1688-bd4c-870c-a3b553210d72.png)


「ん？report()が2つあるぞ？」と思って先にコードをチラ見したら宣言だった。
先に宣言することで、loadAnalyticsよりも後に記述されているreportを呼び出す事ができる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/83c4d156-37c0-871c-70c8-be4ff7229f17.png)

個人的には宣言よりも先行定義（呼び出されるよりも先に関数を定義する）のほうがメンテナンスは楽だと思う。
呼び出される関数は前(上）にあることが保証されているし、宣言と定義で2回書かなくてもいい。
ただ宣言にもメリットはあって、ありきたりな名前の`report`関数がこのモジュール内に存在することが明示的になり「これはオリジナルな関数なんだな」ということがわかって、可読性がよくなる。
まぁ用法用量を守ればだと思いますが…。

## 流し読み

さて流し読みをしていく。
どうやって読んだコードを解説したらいいか試行錯誤中なので、唐突に進め方が変わるかもしれない。ご容赦をば。
今日は先頭から処理ごとに読んで感想や気になったところを述べていくことにする。
気にならないような処理とかはスキップするかも。

### import
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/e5e74c13-a65b-561d-6f01-7626f1f62571.png)

* [疑問] よく知らないanalyticsパッケージが出てきた。
    * [補足] Google Analyticsで収集・分析しているらしい。  
    https://github.com/dom96/analytics
* [感想] `nimblepkg/cli`はエラーやINFOの標準出力のために使っている。display()でエラーの見た目が良くなるから使っているみたいだ。
* [感想] `when defined(os)`は見慣れてきた。

### 型定義
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/f2265a5d-b583-0b8e-fee8-dd5309a9107f.png)

* [感想] enum使ったりOption使ったりしてる。モダンだなぁ(小学生並みの感想)

### proc initEvent
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/412d0ec6-ecb3-7cb1-0cbc-6946b1d14ed8.png)

* [感想] 他の関数もそうだけど引数が長くなると改行入れて読みやすくしてるなぁ。
* [感想] 47行目は汎用性を高めるために、action引数が省略されればcmd、あればそれを返すようにしてる。
    * [補足] 調べてみたところ明示的にaction引数を渡しているのは以下。
        * `builder.nim` 92, 132, 146行目の`builder.build.params: CliParams`
        * `choosenim.nim` 238行目の`choosenim.performAction.params: CliParams` [^1]
        * `choosenim.nim` 273行目の`choosenim.params: CliParams`[^1]

[^1]: performActions.paramsはchoosenim.paramsが渡されるので両者は同等。

### proc initTiming
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/c06cbe42-6634-1955-40b6-d8b26650b353.png)

* [感想] コメントに仕様が書いてある通り。
    * [補足] Nimのドキュメンテーションコメントについては[jiro4989さんが良い記事](https://qiita.com/jiro4989/items/e9b13db41051161b4b5d)を書いてくれています。

### proc promptCustom
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/4443775e-3988-67a5-5d01-804558829c35.png)

* [感想]対話プロンプトを独自実装せずにnimblepkgと共通した処理で見た目とか良くしている印象。
* [疑問]正直なところelseの場合の再帰処理の意図がよくわからない。
    * [補足] 再帰ではありません。オーバーロードで`nimblepkg/cli.prompt`が呼び出されます。勝手にハマって、すごく脱線してたので余談に書きました→[ForcePrompt](###ForcePrompt)

### proc analyticsPrompt
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/be481ea8-e3c8-f09b-5096-5ea6b9114c20.png)

* [感想] デフォルトだと`~/.choosenim/analytics`にClientIDが書き出される。

エラーハンドリングは`choosenim.nim`のisMainModuleブロックで行われています。
トレースすると`choosenim(isMainModule)`→`loadAnalytics`→`analyticsPrompt`でした。

ちなみにClientIDの払い出しに使われている処理を追いかけてみたらこんな感じでした。
https://github.com/dom96/analytics/blob/master/src/analytics.nim#L129
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/7f248723-b5d3-7c9d-934f-675ade163055.png)
https://github.com/pragmagic/uuids/blob/master/uuids.nim#L76
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/dae230bc-827c-816c-6a33-26dcdad7c2a9.png)

### loadAnalytics

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/dde0188b-0e53-a31d-9508-cba3a1edb08a.png)

* [感想] インデント深くしないようにelse省略良くやる。個人的に読みやすい。
* [感想]`analyticsFile`の有無で`analyticsPrompt`を呼び出してる。
    * [補足]テレメトリーに同意しない場合はclientIDが空なので、データは送られない。
    

### proc reportAsyncError

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/36177c11-4cd3-1faa-b51e-5bb88550cebc.png)

* [感想] 非同期処理のコールバックは初めて見た。

### proc hasPendingReports

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/03a6652f-0394-701d-ee55-f8b9d7c53586.png)

* [感想] `pendingReports`があるかどうかを返しているだけ。

### waitForReport

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/8e3f7a9c-ecf7-c347-f3c9-07d28e978676.png)

* operationsとrepotsが終わるか、指定秒数を超えるまでのWait処理。

### proc report

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/9cd06fb0-ce9b-a593-2f31-d634dde4bdb9.png)

* [発見] 引数の`obj: Event | Timing | ref Exception`はobjの型が`Event`or`Timing`or`ref Exception`という書き方。ジェネリクスっぽい。
    * [感想] 共通の処理があって一部だけ個別にしたいケースで、オーバーロードより便利そう。
    * [疑問] ジェネリクスではないのは何故だろう。。。予測できない入力を受け付けないため？
* [感想] コンパイル時にこの関数に渡される型はわかるため`when obj in Evvent:`で分岐している。

## 今日のまとめ

nimblepkg/cliのdisplay()で出力の見栄えをよくしている。これは活用できる。
Google Analyticsを活用して、テレメトリーデータの収集をしていたので今後、収集するようなアプリの参考になる。
それにしてもきれいに設計されてるなーと思う。report()とか特に抽象化がうまいなぁ。

### 残っているタスク

* ~~[タスク]telemetry.loadAnalyticsを読む~~
* ~~[タスク]telemetry.waitForReportを読む。~~
* [タスク]cliparams.parseCliParamsを読む
* [タスク]choosenimpkgをすべて読んだらupdate,show,versions,chooseを読み直す

### 明日やること

* cliparams.nimを読む

## 余談
### ForcePrompt

[proc promptCustom](### proc promptCustom)で気になった処理。

```telemetry.nim
proc promptCustom(msg: string, params: CliParams): string =
  if params.nimbleOptions.forcePrompts == forcePromptYes:
    display("Prompt:", msg, Warning, HighPriority)
    display("Answer:", "Forced Yes", Warning, HighPriority)
    return "y"
  else:
    return promptCustom(msg, "")
```

このelseの再帰の意図がよくわからないので、処理を考える。
まず、`forcePrompts == forcePromptYes`が`false`になるケースを考える。

```nimble/cli.nim
   ForcePrompt* = enum
     dontForcePrompt, forcePromptYes, forcePromptNo
```
引用元：https://github.com/nim-lang/nimble/blob/master/src/nimblepkg/cli.nim

forcePromptsが`dontForcePrompt`or`forcePromptNo`の場合にfalseになる。
でもなんで再帰…**ん…？**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/a73520ec-c14c-5f89-59c4-17292d3eb449.png)

***「あっ、これ別のメソッドだ。オーバーロードじゃん！！」***

そもそも引数の型が`promptCustom(msg:string, "":string)`の時点で気づきたかった。
最近、再帰処理をよく見ていたので脳みそが勝手に勘違いしていた。

66行目でよばれる`cli.promptCustom()`は以下です。
https://github.com/nim-lang/nimble/blob/master/src/nimblepkg/cli.nim#L181
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223008/0d28b282-f565-9074-1d64-7ebe49684fd5.png)


めでたしめでたし。