lolcatやNeo Cowsayみたいに文字を色付けするgolorコマンドをはじめてGo言語を触りながら作った

# 前書き

[busyloop/lolcat: Rainbows and unicorns!](https://github.com/busyloop/lolcat)
に惚れ惚れしながら色々カラフルにしていたのですが、Rubyをインストールするのにちと時間がかかる。
また、短い文字列に色合いを固定してカラフルにするのは少し面倒。

[chalk/chalk: 🖍 Terminal string styling done right](https://github.com/chalk/chalk)
にもグラデーションできそうなものがあるが、モジュール式でちょいと面倒そう。

ということでバイナリだけで似たことができるものはないかなと調べたら、
[Code-Hex/Neo-cowsay: 🐮 cowsay written in Go](https://github.com/Code-Hex/Neo-cowsay)
がなかなか良さそうだった。
ただし牛は余計なので、コードを盗みつつ牛をドナドナするように頑張ってみた。

~~toiletはよくわからなかった。~~

# スクリーンショット

![image.png](https://qiita-image-store.s3.amazonaws.com/0/62354/2440563a-0611-eb04-c13e-29c683c0dfe0.png)


![image.png](https://qiita-image-store.s3.amazonaws.com/0/62354/ccb2cd0b-dd79-cb84-f132-41bb80fdae0c.png)

パイプとファイル指定が出来れば個人的によし。
ついでに引数渡しも。

# 完成品

[khsk/golor: Rainbows as lolcat](https://github.com/khsk/golor)

zip落として解凍すれば使えるのは嬉しい。
`go get -u github.com/khsk/golor`
でも取れる…はず…。


肝心なところはここぐらい

```go
func rainbow(text string, c *cli.Context) string {
	const (
		red = iota + 31
		green
		yellow
		blue
		magenta
		cyan
	)
	rainbow := []int{magenta, red, yellow, green, cyan, blue}
	buf := make([]rune, 0, len(text))

	var line int
	attribute := getVisualDistinction(c)

	i := 0
	for _, char := range text {
		if char == '\n' {
			if c.Int("deviation") != 0 {
				line++
				i = line * c.Int("deviation")
			} else {
				i = 0
			}
			buf = append(buf, char)
			continue
		}
		buf = append(buf, []rune(fmt.Sprintf("\x1b[%d%sm%c\x1b[0m", rainbow[i%6], attribute, char))...)
		i++
	}

	return string(buf)
}

func aurora(text string, c *cli.Context) string {

	buf := make([]rune, 0, len(text))
	attribute := getVisualDistinction(c)
	if c.Int64("seed") == 0 {
		rand.Seed(time.Now().UnixNano())
	} else {
		rand.Seed(c.Int64("seed"))
	}
	i := rand.Intn(256)

	freq := c.Float64("freq")
	spread := c.Float64("spread")

	for _, char := range text {
		if char == '\n' {
			buf = append(buf, char)
			continue
		}

		buf = append(buf, []rune(fmt.Sprintf("\033[38;5;%d%sm%c\033[0m", rgb(float64(i)/spread, freq), attribute, char))...)
		i++
	}

	return string(buf)

}

func rgb(i float64, freq float64) int {
	red := int(6*((math.Sin(freq*i+0)*127+128)/256)) * 36
	green := int(6*((math.Sin(freq*i+2*math.Pi/3)*127+128)/256)) * 6
	blue := int(6*((math.Sin(freq*i+4*math.Pi/3)*127+128)/256)) * 1
	return 16 + red + green + blue
}
```

(コピペミックスなので式の意味するところがあまりわかっていない)

# Goのいいところ

## 簡単

並列[^1]処理使っていないのでなぜGoと言われそうですが、単純に書きやすい。
[A Tour of Go](https://go-tour-jp.appspot.com/welcome/1)
は最初のHello Worldで面倒になってやめたんですが、
見よう見まねと
[golang チートシート - Qiita](https://qiita.com/jca02266/items/56a4fb7b07b692a6bf34)
などを見てなんとかかんとか動くレベルまで行くのは良かった。


[^1]: 並行？

## `go コマンド`便利

`go run`はまあ普通として、
`go fmt`のおかげで2スペースインデントとか自由にガシガシ書けた。

特に`go get`がよくて、簡単にgithubなどから引っ張ってこれる。
これがパッケージ管理のnpmなど別コマンドじゃなくて`go`でできるのが良かったと思った。
…しかしpackage.jsonらしきものはできないので依存パッケージは`go get`では管理不可？
開発者じゃなくて利用者として便利？なのかな？

**追記:**
[Go & Versioning(vgo)を読んで大きな変更が入ったなと思った - Qiita](https://qiita.com/lufia/items/67701e2f927c77a75d6e)
でバージョン管理できそう。
ただし、`vgo`とコマンド名が変わっちゃうのはちょっと残念？`npm`みたいな感じに落ち着くのだろうか。


## リリース環境整っている

作ったは良いけれど、環境毎のビルドが標準方法ではかなり不便そうだった。

[go buildとクロスコンパイル - Qiita](https://qiita.com/Utr/items/9469c1611abe8a0a3486)

いちいち環境変数設定するなんてうへーとなったけど、良いものがあった。

[mitchellh/gox: A dead simple, no frills Go cross compile tool](https://github.com/mitchellh/gox)
[golangでクロスコンパイルするならgoxライブラリを使うと便利！ - エンジニアはこわくない](http://tsujitaku50.hatenablog.com/entry/2017/04/18/202955)

自動で全部作ってくれるすごいやつだよ。

…いやいやでもでもGithubのリリース作るの面倒だしな…と凄い不精じみていると、

[tcnksm/ghr: Upload multiple artifacts to GitHub Release in parallel](https://github.com/tcnksm/ghr)
[高速に自作パッケージをGithubにリリースするghrというツールをつくった | SOTA](https://deeeet.com/writing/2014/07/29/ghr/)

自動でリリースしてアップロードもしてくれるすごいやつだよ。

…いやいやいやいやでもでもでも2つのツール使うなんて面倒だしー！zip化も手動でしなきゃいけないしなー！

[gox して ghr するツール ggallin 作った - Qiita](https://qiita.com/yuya_takeyama/items/ac200058f9a27a5db12f)

**すごい**

どんなに面倒くさがりでもこれならリリースできると思います。
できました。
ありがとうございます。

## CLIもテンプレがある

これもGoならではではないですが、
[urfave/cli: A simple, fast, and fun package for building command line apps in Go](https://github.com/urfave/cli)
で簡単にCLIが作れるのが良い。
面倒なヘルプも自動で作ってくれる。楽。

***

Goは面倒くさがりの利用者に優しく、
Goは面倒くさがりの開発者に優しい、
言語・環境なのかなと感じました。


# Goのわるいところ(別方法あるのかも)

しっかり触っていないのでなんともですが…
String型から一文字取り出すとRuneになってしまって、Stringを受け付ける処理に(キャストしても)渡せないことが困りました。

最初は一文字ずつ切り取って
[GoでANSIエスケープコードを扱うライブラリを作った(色付け・カーソル移動等) - Qiita](https://qiita.com/morikuni/items/ad8d900f56ddeb223101)
を使い色付けしようと考えていたのですが、それで断念。
手動でコードをつけていく方式(それでもコピペ)になりました。

## Qiitaのタグ

**Go言語の悪いところではないのですが**

[Go](https://qiita.com/tags/go)と[golang](https://qiita.com/tags/golang)の2つのタグがある…
[Go](https://qiita.com/tags/go)が良さそうですが、2つともつけている記事が多そうです。



# 参考

* [Go事始め作業ログ - Qiita](https://qiita.com/1000ch/items/e42e7c28cf7a7b798a02)
* [golang チートシート - Qiita](https://qiita.com/jca02266/items/56a4fb7b07b692a6bf34)
* [逆引きGolang (文字列)](https://ashitani.jp/golangtips/tips_string.html)
* [【Go】 go fmt でコード整形 - Qiita](https://qiita.com/taji-taji/items/6d286bf4483a4c6ceed6)

***


* [Go で Neo Cowsay を作った - Qiita](https://qiita.com/codehex/items/a5245720524aecfab666)
* [ANSIエスケープシーケンス チートシート - Qiita](https://qiita.com/PruneMazui/items/8a023347772620025ad6)
* [urfave/cli: A simple, fast, and fun package for building command line apps in Go](https://github.com/urfave/cli)
* [Go言語でサクッとコマンドラインツールをつくる | eureka tech blog](https://developers.eure.jp/tech/golang_commandlinetool/)
* [Goでコマンドラインツールを簡単に作成する方法 - Qiita](https://qiita.com/cotrpepe/items/113c938b37919d021339)  
  コマンドのヘルプは`greet [command] help`としないと表示されないので、`greet help`で全部出てほしかったり。
* [urfave/cli(旧github.com/codegangsta/cli)でのヘルプ表示で追加テンプレートパラメータを使う - Qiita](https://qiita.com/yamamoto-febc/items/1da872231f4b3a78b7bd)
* [Golangで標準入力がパイプで渡されたものか判定する - Qiita](https://qiita.com/tanksuzuki/items/e712717675faf4efb07a)
* [Goで乱数 - Qiita](https://qiita.com/cubicdaiya/items/819886c57e9d17e4b019)


* [gox して ghr するツール ggallin 作った - Qiita](https://qiita.com/yuya_takeyama/items/ac200058f9a27a5db12f)
* [mitchellh/gox: A dead simple, no frills Go cross compile tool](https://github.com/mitchellh/gox)
* [高速に自作パッケージをGithubにリリースするghrというツールをつくった | SOTA](https://deeeet.com/writing/2014/07/29/ghr/)

バイナリファイル名は恐らくディレクトリ名が使われるので注意。
ggallinは設定したバージョンのプレフィックスとして`v`が付くので、バージョンに`v`を含めると`vv1.0.0`となっちゃう。

* [GitHubのリリース(Releases)を削除する - Qiita](https://qiita.com/naoki_koreeda/items/84171cd98a8e65f79408)
* [GitHubでREADME.mdに画像を使用したい - Qiita](https://qiita.com/brainya/items/06dd096e7ed1acce4b1e)

***

* [GoでCLIツール作るのに便利そうなパッケージを集めてみました - Qiita](https://qiita.com/isaoshimizu/items/71dd2ca2a08ddb607e31#_reference-f5413c28dd6b33bba5ba)

***

* [仕事の役には立たない、Linuxにおけるジョークプログラムの話 - Qiita](https://qiita.com/Hiroki_lzh/items/8cf206d54f91e29b3912)  
* [lolcatは美しいが役に立たない - Qiita](https://qiita.com/dharry/items/49436e77ecf116ea88a6)  
  lolcat出会い。よさ。

# 課題

* lolcatのようにヘルプに色付け  
  カスタマイズしようにもそのまま出力してるっぽいので標準出力をバッファしないといけない？
* ライブラリ化  
  他のコード書くときの出力にも使えたら便利そう
* もっと楽しいカラフルさの表現

# オマケ

```bash:fish_prompt.fish
function fish_prompt
    #set_color $fish_color_cwd
    #echo -n (prompt_pwd)
    #set_color normal
    echo -n (prompt_pwd) | golor
    echo -n ' > '
end
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/62354/25fa3511-8024-8ba4-7672-55ed1242b462.png)



# 余談

Neo Cowsayという偉大なお手本があったのでGoにしましたが、Nimもなかなか良さそうでした。