DRYな備忘録

Don't Repeat Yourself.

【Go言語】packageに定義されてるstruct名を取得する

問題

メタプログラミング的なことをするうえで、ある名前のpackageの中で定義されているstruct名を抽象的に取得したいことがあったので、その方法をメモ

方法

  • go/ast
  • go/parser

あたりを使う

準備

% cd
% mkdir $GOPATH/src/foo
% vi $GOPATH/src/foo/foo.go
% vi $GOPATH/src/foo/hoge.go

$GOPATH/src/foo/foo.go

package foo

type Foo struct{}
type Bar struct{}
type Buz struct{}

type myPrivateType struct{}

$GOPATH/src/foo/hoge.go

package foo

type Hoge struct{}
type Fuga struct{}
type Piyo struct{}

実装

取得側の実装

~/main.go

package main

import "fmt"
import "path/filepath"
import "os"
import "go/build"
import "go/token"
import "go/parser"
import "go/ast"

func main() {
    // get path to the target package
    gopath := build.Default.GOPATH
    pkgPath := filepath.Join(gopath, "src", "foo")

    // parse all files in this dir
    pkgs, _ := parser.ParseDir(token.NewFileSet(), pkgPath, func(f os.FileInfo) bool {
        // とりあえず全部オッケーでいいっしょ
        return true
    }, 0)

    // 一応fooパッケージしか無いディレクトリ走査したので、あるっしょ
    var pkg *ast.Package = pkgs["foo"]

    for _, f := range pkg.Files {
        // fooパッケージの全ファイルに含まれる宣言を取り出す
        for _, decl := range f.Decls {
            if genDecl, ok := decl.(*ast.GenDecl); ok {
                if genDecl.Tok != token.TYPE {
                    // This is not `type`
                    continue
                }
                spec := genDecl.Specs[0].(*ast.TypeSpec)
                fmt.Printf("%T\t%+v\tName: %s\n", spec, spec, spec.Name)
            }
        }
    }
}

結果

% go run main.go
*ast.TypeSpec   &{Doc:<nil> Name:Foo Type:0x2103a6380 Comment:<nil>}    Name: Foo
*ast.TypeSpec   &{Doc:<nil> Name:Bar Type:0x2103a63c0 Comment:<nil>}    Name: Bar
*ast.TypeSpec   &{Doc:<nil> Name:Buz Type:0x2103a6420 Comment:<nil>}    Name: Buz
*ast.TypeSpec   &{Doc:<nil> Name:myPrivateType Type:0x2103a6460 Comment:<nil>}  Name: myPrivateType
*ast.TypeSpec   &{Doc:<nil> Name:Hoge Type:0x2103a6500 Comment:<nil>}   Name: Hoge
*ast.TypeSpec   &{Doc:<nil> Name:Fuga Type:0x2103a6540 Comment:<nil>}   Name: Fuga
*ast.TypeSpec   &{Doc:<nil> Name:Piyo Type:0x2103a65a0 Comment:<nil>}   Name: Piyo
%

雑感

  • unexportedでも取れるのか。まあメタ的には取れないと変か
  • GenDeclってなんだ?Generated?
  • とりあえず取れたし、これであとreflectに渡してほげほげ

f:id:otiai10:20140725084754j:plain

DRY