LoginSignup
3
6

More than 5 years have passed since last update.

Go で関数の引数用構造体のバリデーションと初期化をするメソッドをタグから生成するツールを作った

Posted at

はじめに

  • 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 あたりを試してみたい。
3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6