TVer Technologies TechBlog

TVer Technologies TechBlog

go generateでコードを自動生成する

はじめに

はじめまして。今年の1月にHAROiDに入社した鈴木(@yudppp)です。

いつもMacBookProにHHKBを乗せて仕事をしています。

f:id:tt-techblog:20200716115602j:plain

HAROiDでは多くのシステムでGolangを採用しています。 個人的にもGolangはSimpleで書きやすく好きな言語なのですが、簡素な言語仕様のため手で書くコードが他の言語に比べて増えてしまいがちです。

そこでコードを自動生成できるgo generateを紹介していきます。

go generateとは

Golangの1.4から追加されたコマンドでコードの自動生成を行うことができます。

こちらの公式ブログでも紹介されています。

実際にstringerを使って、go generateの使い所を見てみたいと思います。

stringerとは

定数群にstringerをかけることによって、interfaceのStringerの関数のfunc (t T) String() stringの実装が行われます。 また、Stringerは出力時の返す値を定義するものです。

使い方は簡単で下記のようにgo getしてコマンドを入手します。

$ go get golang.org/x/tools/cmd/stringer

このようにいくつかの薬が定数で定義しているとします。

package painkiller

type Pill int

const (
    Placebo Pill = iota
    Aspirin
    Ibuprofen
    Paracetamol
    Acetaminophen = Paracetamol
)

fmt.Println(Placebo)をすると、数値の0が出力されます。0だと何者なのかが分かり難いため、分かりやすい文字列にしたいです。

そこでstringerコマンドを実行すると

$ stringer -type=Pill

Stringerが実装され、再度fmt.Println(Placebo)を試してみると、今度はPlaceboという文字列が表示されました。

すごく簡単に出力を文字列に変更することができました。

しかし薬の種類が増えたときに再度コマンドを実行しなければなりません。 ひとつのときはMakefileにでも1行書いておけばよいのですが、他のものにもstringerを実行させたくなったときにその分Makefileに追加して書いていかなければいけなくなってしまいます。

そこでgo generateを使っていきます。

先ほどのfileに

package painkiller

//go:generate stringer -type=Pill
type Pill int

const (
    Placebo Pill = iota
    Aspirin
    Ibuprofen
    Paracetamol
    Acetaminophen = Paracetamol
)

と1行コメントを追加します。

そのあとに

$ go generate

を行うと、コメントを解析して先ほどのstringerコマンドをした時と同じ結果が得られるようになります。

//go:generate {コマンド}

とするだけで自動で生成されるので簡単に処理を自動化することができます。

jsonのタグを自動生成する

普段Golangを書いていて特に面倒くさいと思うことのひとつにJSONを返すAPIを作っていた時のレスポンスに使われるstructのタグを書きたくないと毎日思っています。

type User struct {
  UserID string `json:"user_id"`
  Name   string `json:"name"`
}

スネークケース返すようなAPIを普段実装しているのですが、スネークケースで返すようにしたいだけなのに1つ1つに設定が必要で、やたら手間がかかります。 理想はdefaultはスネークケースで返すようにして、何か指定したい時だけ(たとえばomitemptystringなど)タグを書くようにしていきたいです。

ということで作ってみました。

yudppp/json_snake_case

AST(抽象構文木)を初めて触ったのですがgolangでは簡単に扱うことができ、三時間ほどで動くものができました。

// go:generate json_snake_case -type=User
type User struct {
  UserID string
  Name   string
}

上記のように宣言しgo generateするとMarshalerをスネークケースで返すように勝手に実装してくれます。

使い方等細かい内容はREADME.mdに記載しています。

同じようにタグを書くのが面倒くさいと思ってる方は是非使ってみてください。 不具合等ありましたらissueやPR等いただけたらと思います。

最後に

今後も面倒くさい仕事を自動化して、自分の仕事を減らしていきたいです。