はじめに
- Go では小さい構造体を関数の引数にすることで名前付き引数のようなことが実現できる。
type MyParam struct { X, Y int }
func MyFunc(m MyParam) {}
func main() {
MyFunc(MyParam{X: 1, Y: 2})
}
- この引数用の構造宣言の各フィールドのタグでバリデーションのオプションやデフォルト値を指定できると便利だと思いツールを作ってみた。
レポジトリ
使い方
- 基本的に README のとおり
構造体宣言
- 引数として扱う構造体の宣言を行う
- タグの
arg:""
部分で argumenter 用の設定をする-
default=VALUE
でデフォルト値の指定 -
min=VALUE
で最小値の指定 - ...
-
package main
type MyType struct {
I int `arg:"default=5,min=0,max=10"`
S string `arg:"default=hello"`
SI []int `arg:"required,lenmin=1,lenmax=4"`
M map[int]bool `arg:"required"`
F func() `arg:"required"`
IN interface{} `arg:"required"`
P *int `arg:"required"`
}
関数生成
-
argumenter
コマンドに対象の構造体名と記載のファイルを指定する- 出力ファイル名を指定しないと
元のファイル名_argumenter.go
が生成される
- 出力ファイル名を指定しないと
argumenter -type MyType file.go
生成される関数
// Code generated by "argumenter -type MyType"; DO NOT EDIT
package main
import "errors"
func (m *MyType) Valid() error {
if m.I == 0 {
m.I = 5
}
if m.I < 0 {
return errors.New("I must greater than or equal 0")
}
if m.I > 10 {
return errors.New("I must less than or equal 10")
}
if m.S == "" {
m.S = "hello"
}
if m.SI == nil {
return errors.New("SI must not nil")
}
if len(m.SI) < 1 {
return errors.New("SI length must greater than or equal 1")
}
if len(m.SI) > 4 {
return errors.New("SI length must less than or equal 4")
}
if m.M == nil {
return errors.New("M must not nil")
}
if m.F == nil {
return errors.New("F must not nil")
}
if m.IN == nil {
return errors.New("IN must not nil")
}
if m.P == nil {
return errors.New("P must not nil")
}
return nil
}
生成された関数の使い方
-
Valid() error
関数が生成されるので引数として受け取った直後に実行して必要ならエラー処理を行う -
Valid()
を実行するとデフォルト値が挿入される
func AnyFunc(m *MyType) {
if e := m.Valud() {
// エラー処理
}
num := m.I // デフォルト値が取得できる
}
おわりに
- Go は実装が固くて遊びが少ないという印象だったが、コード解析や生成まわりのツールがそのぶん最初から充実していて面白かった。
- 次は goyacc あたりを試してみたい。