遊びでstrangejsonというgoのstructの定義とswagger定義(のようなもの)を行ったり来たりするようなものを作り始めた

github.com

strangejsonというgoのstructの定義とswagger定義(のようなもの)との間を行ったり来たりするようなものを作り始めた。まだ完成はしていない。

何をするもの?

何をするものかと言うと

  • goのstructの定義を見てswagger形式のschema定義(まだ確定はしていない)を生成してみたり
  • swagger形式のschema定義(まだ確定はしていない)からgoのstructの定義を生成してみたり

できるような何か。

swagger docとgo codeの間を行ったり来たりするような、reversibleな形になるようにしたいという気持ちがある(この辺の気持ちはnbreversibleの時と同様)。

go code -> swagger docについて

例えば以下のようなstructの定義があったときに、requiredのタグを見てjsonからUnmarshallする時に必須かどうかをチェックするUnmarshalJSONを定義するようにしたい。この時requiredタグがないものはrequiredとして扱う。

// User : user
type User struct {
    // Name : name of user
    Name     string `json:"name" required:"true"`
    Age      int    `json:"age"` // no required option, treated as required
    NickName string `json:"nickname" required:"false"`
}

そのようなschema定義を俯瞰する何かも欲しかったのでswagger(OAS)のschema定義も生成できるようにしようかなーと思ったりしていた。 (出力の形式としてのswagger docについては特にこだわりはない)

swagger doc -> go codeについて

逆にenumなどの定義が存在するswagger docからgoのコードを生成する機能を追加しても良いかもしれないと思ったりしている。 このあたりは生成して終わりではなく関数定義をsyncするという形にできるような気もしている(面倒だったら生成して終わりにするかもしれない)。

どういう経緯?

そもそもの発端はこの辺のJSONの取扱いについて考えていた時に思いついたこと。

これはJSONのunmarshalについての記事なのだけれど。元々のモチベーションとしてはstruct中のpointer定義を省きたいというのが根底にあった。そしてその実現にはカスタムのUnmarshalJSONメソッドを自作するということなのだけれど。逐一このようなメソッドを書くのはとても面倒。なので生成するための何かがほしいなというところから作り始めた。

このあたりで以下の記事がリンクする。

go/ast経由でASTを頑張って解釈するよりgo/typesの各Object(types.Objectはinterfaceだけれど)を触った方が楽なのではという趣旨の記事。このgo/types経由でstructの情報を集めてみるという作業を試してみることもついでにやりたいということも念頭にあったりした。

現状

現状は以下の様な感じ。○は実装に取り掛かっているもののことで完成しているものではない。

○: go -> swagger doc
×: swagger doc -> go

structをparseしてそれっぽいような構造体のリストを作ることができるようになったところまで。swagger docを生成するところまではいっていない。

description(コメント)の取得が結構力技で結局ASTからたどるというようなことをしてしまっているのでもう少し簡潔に書けないか試行錯誤したいというのと。あと各struct間の依存関係を解釈できていないのでできるようにしたい。その他enum的なものの対応やoneOf的なものの対応。validation的なものをタグでどうやって付加するかなど結構抜けている。

今の所できること

以下のようなファイルがあるgithub.com/podhmo/strangejson/examples/simple00というパッケージを対象に実行してみると。

github.com/podhmo/strangejson/examples/simple00/user.go

package simple00

// User : user
type User struct {
    // Name : name of user
    Name     string `json:"name" required:"true"`
    Age      int    `json:"age"` // no required option, treated as required
    NickName string `json:"nickname" required:"false"`
}

github.com/podhmo/strangejson/examples/simple00/skill.go

package simple00

// Skill :
type Skill struct {
    Name string `json:"name"`
}

以下の様な出力が手に入るというところまで。

$ strangejson --pkg github.com/podhmo/strangejson/examples/simple00
[]strangejson.Schema{
  strangejson.Schema{
    Name:        "Skill",
    Description: "",
    Type:        "object",
    Properties:  []strangejson.Property{
      strangejson.Property{
        Name:        "name",
        Description: "",
        Type:        "string",
        Required:    true,
        XGoName:     "Name",
      },
    },
    Required: []string{
      "name",
    },
    XGoName: "Skill",
    Depends: []strangejson.Schema{},
  },
  strangejson.Schema{
    Name:        "User",
    Description: "user",
    Type:        "object",
    Properties:  []strangejson.Property{
      strangejson.Property{
        Name:        "name",
        Description: "",
        Type:        "string",
        Required:    true,
        XGoName:     "Name",
      },
      strangejson.Property{
        Name:        "age",
        Description: "",
        Type:        "integer",
        Required:    true,
        XGoName:     "Age",
      },
      strangejson.Property{
        Name:        "nickname",
        Description: "",
        Type:        "string",
        Required:    false,
        XGoName:     "NickName",
      },
    },
    Required: []string{
      "name",
      "age",
    },
    XGoName: "User",
    Depends: []strangejson.Schema{},
  },
}