ビルド中にERRORやWARNINGが出る度におっさんが「ｴﾗｰ」「ｳｫｰﾝ」って言ってくれるプログラムを作った

ビルド中に流れる大量のERRORやWARNINGを眺めていて「これに合わせておっさんが『ｴﾗｰｴｴｴｴｴﾗｴﾗｰ』って言ってくれたら楽しいだろうな」と思ってしまったので作りました。[Go]で。

その名も、ザ・[**ErrorWarner**](https://github.com/Bakudankun/ErrorWarner)。

デモ↓
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ビルド中にERRORやWARNINGが出る度におっさんが「ｴﾗｰ」「ｳｫｰﾝ」って言ってくれるプログラムを作りました。<a href="https://t.co/8QUvVVWimf">https://t.co/8QUvVVWimf</a> <a href="https://t.co/j3BEjFkHYg">pic.twitter.com/j3BEjFkHYg</a></p>&mdash; バクダンくん参戦!!! (@Bakudankun) <a href="https://twitter.com/Bakudankun/status/1099633393468268544?ref_src=twsrc%5Etfw">February 24, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

あまりちゃんと調べてないのでもしかしたらすでにそういうツールがあるかもしれませんが、Go言語の勉強も兼ねて。


# じゅんぼ

## 対応OS

* Windows
* macOS
* Linux
* その他[Oto]がサポートしてるOS（たぶん）


## インストール

[Go]をいい感じにインストールしてパスを通し、以下のコマンドを実行すればおっけー。

```
go get -v github.com/Bakudankun/ErrorWarner/errwarn
```

お好みで`ew`などを`errwarn`へのエイリアスに設定しておきましょ。
Goはプログラムのインストールが楽でいいですね。

Linuxとかはライブラリのインストールが必要になるかもしれません。詳しくは[Oto]のREADMEで。


## サウンドファイルの配置

今のところサウンドはついてこないのでご自分で用意してください。
というかバンドルしたとしてインストール後にそれを探し当てるのがちょい面倒……。どうするのが良いのかしら。
とりあえずは適当に`TTS download`とかでググって出てきたサイトで読み上げさせたのを使うと良いと思います。

サウンドを用意したら以下のコマンドを実行します。

```
errwarn ""
```

このコマンドは失敗しますが、Windowsなら`%APPDATA%`の中、macOSなら`$HOME/Library/Application Support`の中、Linuxなら`$HOME/.config`の中に`ErrorWarner`フォルダが作成されます。
そのフォルダの中に`error.wav`や`warn.wav`を置いたり置かなかったりしてください。

ファイル形式はWAV以外にもMP3・FLAC・Ogg Vorbisにも対応してるといいな。
ただ、自分が試した中で「From Text To Speech」というサイトのMP3はバージョンが低い？とかで[go-mp3が対応していなかった](https://github.com/hajimehoshi/go-mp3/issues/12)ので、結局WAVにデコードしておくことになったのでした。


# つかう

```
つかいかた:
  errwarn [OPTIONS] [--] <cmdline>
  <cmdline> | errwarn [OPTIONS]

OPTIONS
  -e <regexp>
        errorの行にマッチする正規表現を指定します。
  -p <preset>
        プリセットを指定します。
  -s <soundset>
        サウンドセットを指定します。
  -stdout
        標準エラー出力の代わりに標準出力を読みます。
  -w <regexp>
        warningの行にマッチする正規表現を指定します。
```


## おためし

```
errwarn -e error -w warning
```

と実行すると標準入力を待機するので、試しに`error`または`warning`と書いてエンターを押してください。
ちゃんと音声ファイルが設置されていればそれが再生されるハズです。

これだけでも楽しいですが、次に進むためCtrl-Cで終了します。


## ErrorWarnerにコマンドを渡す

```
errwarn [OPTIONS] [--] <cmdline>
```

`errwarn`が`<cmdline>`を実行し、その出力を読みます。
出力の中にオプション`-e`や`-w`で指定した[正規表現]にマッチする行が出てきたらサウンドを鳴らしてお知らせします。

`errwarn`自身が異常終了しない限り、終了ステータスは`<cmdline>`の終了ステータスを返します。

デフォルトでは標準エラー出力を読みます。ビルドログが標準出力に出てくる場合は`-stdout`を指定してください。


## ErrorWarnerにパイプする

```
<cmdline> | errwarn [OPTIONS]
```

最初に`errwarn`を書き忘れた場合はパイプさせることもできます。

ただし普通標準エラー出力はパイプされないので、ビルドログが標準エラー出力に出てくる場合はどうにかしてパイプに流し込む必要があります。

また、この場合`errwarn`は`cmdline`の終了ステータスにかかわらず基本的に正常終了することにも注意。


# プリセット

いちいち正規表現を書くのも面倒なのでプリセットを作れるようにしました。[TOML]で。
TOMLは初見ではなんか冗長だなあという印象でしたが、設定ファイルを書くという観点ではインデントなどの制約が少なくて適当に書けるので良さげ。

上で作成されたErrorWarnerフォルダに`config.toml`という名前でテキストファイルを置いておけば、`-p`オプションでプリセットを呼び出せるようになります。
プリセットの名前がコマンドの名前に一致した場合、自動でそのプリセットを選択してくれます。
デフォルト設定を弄りたくなったので、空文字列を名前としたプリセットで弄られるようにしました。

```toml:設定例
# デフォルト値
[preset.""]
stdout = true               # 標準エラー出力の代わりに標準出力を読む
errorFormat = '(?i:error)'  # errorの行にマッチする正規表現
warningFormat = '(?i:warn)' # warningの行にマッチする正規表現
soundset = 'akane'          # サウンドセット

# `errwarn -p gcc`でこの設定を呼び出せる
[preset.gcc]
stdout = false
errorFormat = '^\S+:\d+:\d+: error: '
warningFormat = '^\S+:\d+:\d+: warning: '
soundset = 'aoi'

# `errwarn go build`で自動的に選択してくれる
[preset.go]
stdout = false
errorFormat = '^.*: '
warningFormat = '' # 空の場合は使われない
```


# サウンドセット

当たり前のように喋ってくれる人を切り替えたくなったので、ErrorWarnerフォルダの下に`soundsets/*`フォルダを作ることでサウンドセットを作成することができるようにしました。

```txt:ErrorWarnerフォルダの構造
ErrorWarner
|-- config.toml
|-- error.wav
|-- warn.wav
|-- ...
|
+-- soundsets
    +-- akane
    |   |-- error.ogg
    |   |-- warn.ogg
    |   |-- ...
    |
    +-- aoi
        |-- error.flac
        |-- warn.flac
        |-- ...
```

作ったサウンドセットは`-s`オプションやコンフィグの`soundset`パラメータで指定できます。
指定しなかった場合や空文字列を指定した場合はErrorWarnerフォルダ直下のサウンドファイルが使われます。


# もっと賑やかに

どうせなのでerror時やwarning時だけでなく色々なタイミングで音声を流せるようにしました。
以下のファイル名のサウンドが使われます。

* `error.*`  
  errorが見つかったときに再生されます。
* `warn.*`  
  warningが見つかったときに再生されます。
* `start.*`  
  コマンドの開始時に再生されます。
* `finish.*`  
  errorやwarningがあったもののコマンドは正常終了した場合に再生されます。  
  パイプ渡しの場合はコマンドの終了時に常に再生されます。
* `success.*`  
  errorもwarningも無くコマンドが正常終了した場合に再生されます。パイプ渡しの場合は使われません。
* `fail.*`  
  コマンドが失敗した場合に再生されます。パイプ渡しの場合は使われません。


-----

まだ作ったばかりなので、少なくともv0.xの間はバンバン仕様が変わる可能性があります。初めてGoで作ったツールなので悪いところがあったらどしどしお便りくださいな。

Happy Erroring!!


[Oto]: https://github.com/hajimehoshi/oto
[Go]: https://golang.org/
[正規表現]: https://golang.org/pkg/regexp/syntax/
[TOML]: https://github.com/toml-lang/toml