├── .dockerignore ├── .gitignore ├── .travis.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── README_cn.md ├── client ├── client.go ├── function.go ├── operation.go ├── security.go └── utils.go ├── cmd └── gen │ ├── client │ └── client.go │ ├── crud │ └── crud.go │ ├── main.go │ ├── openapi │ └── openapi.go │ ├── route │ └── route.go │ └── run │ └── run.go ├── crud ├── crud.go └── tpl │ ├── Makefile │ ├── mgo.go.tpl │ ├── mock.go.tpl │ └── tpl.go ├── go.mod ├── go.sum ├── model ├── convert_from.go ├── convert_to.go └── model.go ├── named └── named.go ├── openapi └── openapi.go ├── parser ├── kind_mapping.go ├── parser.go └── util.go ├── route ├── call.go ├── middleware.go ├── not_found.go ├── operation.go ├── request.go ├── response.go ├── route.go ├── security.go ├── util.go └── wrapping.go ├── run └── run.go ├── spec ├── kind.go ├── kind_string.go └── spec.go ├── srcgen ├── file.go └── srcgen.go └── utils └── utils.go /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .gitignore 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | notifications: 4 | email: false 5 | jobs: 6 | include: 7 | - go: 1.13.x 8 | stage: deploy 9 | go_import_path: github.com/wzshiming/gen 10 | install: skip 11 | script: skip 12 | before_deploy: 13 | - BASENAME=gen bash -c "$(curl -fsSL https://github.com/wzshiming/my-shell/raw/master/build_all.bash)" 14 | deploy: 15 | provider: releases 16 | api_key: $CI_TOKEN 17 | file_glob: true 18 | file: release/* 19 | skip_cleanup: true 20 | on: 21 | repo: wzshiming/gen 22 | branch: master 23 | tags: true 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS builder 2 | WORKDIR /go/src/github.com/wzshiming/gen 3 | COPY . . 4 | RUN apk add -U --no-cache git 5 | RUN go install ./cmd/gen 6 | CMD gen 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 wzshiming 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | test: 3 | go test ./... 4 | 5 | generate: go-bindata 6 | make -C crud/tpl 7 | 8 | go-bindata: 9 | go get github.com/wzshiming/go-bindata/cmd/go-bindata 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gen - Tools for generating source code for microservices 2 | 3 | Just write normal functions, and Gen generates efficient routing source code and documentation for it 4 | Because the source code is generated, none of this affects runtime performance 5 | The differences caused by each change in the tool are shown directly in the generated source code 6 | generating clients is also supported 7 | 8 | [![Build Status](https://travis-ci.org/wzshiming/gen.svg?branch=master)](https://travis-ci.org/wzshiming/gen) 9 | [![Go Report Card](https://goreportcard.com/badge/github.com/wzshiming/gen)](https://goreportcard.com/report/github.com/wzshiming/gen) 10 | [![GitHub license](https://img.shields.io/github/license/wzshiming/gen.svg)](https://github.com/wzshiming/gen/blob/master/LICENSE) 11 | 12 | - [English](https://github.com/wzshiming/gen/blob/master/README.md) 13 | - [简体中文](https://github.com/wzshiming/gen/blob/master/README_cn.md) 14 | 15 | ## Examples 16 | 17 | '#' is the annotation, the annotation is the golang tag syntax, the only difference here is '#' wraps not '`'. 18 | 19 | ``` golang 20 | // ItemService #path:"/item/"# 21 | type ItemService struct {} 22 | 23 | // Create a Item #route:"POST /"# 24 | func (s *ItemService) Create(item *Item) (err error) {} 25 | 26 | // Update the Item #route:"PUT /{item_id}"# 27 | func (s *ItemService) Update(itemID int /* #name:"item_id"# */, item *Item) (err error) {} 28 | 29 | // Delete the Item #route:"DELETE /{item_id}"# 30 | func (s *ItemService) Delete(itemID int /* #name:"item_id"# */) (err error) {} 31 | 32 | // Get the Item #route:"GET /{item_id}"# 33 | func (s *ItemService) Get(itemID int /* #name:"item_id"# */) (item *ItemWithID, err error) {} 34 | 35 | // List of the Item #route:"GET /"# 36 | func (s *ItemService) List(offset, limit int) (items []*ItemWithID, err error) {} 37 | ``` 38 | 39 | 1. Install gen tool `go get -v github.com/wzshiming/gen/cmd/gen` 40 | 2. Add gen tool to $PATH 41 | 3. Start it `gen run github.com/wzshiming/gen-examples/service/...` 42 | 4. Open [http://127.0.0.1:8080/swagger/?url=./openapi.json#](http://127.0.0.1:8080/swagger/?url=./openapi.json#) with your browser 43 | 44 | [Examples](https://github.com/wzshiming/gen-examples/) 45 | 46 | Or try to quickly build services from scratch 47 | 48 | 1. Make a directory `mkdir -p $(go env GOPATH)/src/gentest` 49 | 2. Change directory `cd $(go env GOPATH)/src/gentest/` 50 | 3. Define models 51 | ``` shell 52 | cat > models.go < models.go <= 400 { 212 | err = fmt.Errorf("Undefined code %d %s", code, http.StatusText(code)) 213 | } 214 | } 215 | `) 216 | 217 | g.generateErrror(oper.Responses) 218 | return nil 219 | } 220 | 221 | func (g *GenClient) generateErrror(resps []*spec.Response) (err error) { 222 | g.buf.WriteString(` 223 | if err != nil { 224 | return `) 225 | for i, resp := range resps { 226 | if resp.Ref != "" { 227 | resp = g.api.Responses[resp.Ref] 228 | } 229 | if i != 0 { 230 | g.buf.WriteByte(',') 231 | } 232 | if i != len(resps)-1 { 233 | g.TypesZero(resp.Type) 234 | } else { 235 | g.buf.WriteString("err") 236 | } 237 | } 238 | g.buf.WriteString(` 239 | } 240 | `) 241 | return 242 | } 243 | -------------------------------------------------------------------------------- /client/operation.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/wzshiming/gen/spec" 5 | "github.com/wzshiming/gen/utils" 6 | ) 7 | 8 | func (g *GenClient) generateClient() (err error) { 9 | g.buf.AddImport("", "github.com/wzshiming/requests") 10 | g.buf.WriteFormat(` 11 | var Client = requests.NewClient().NewRequest() 12 | `) 13 | 14 | operations := g.api.Operations 15 | for _, v := range operations { 16 | resps := v.Responses 17 | if len(resps) == 0 { 18 | resps = append(resps, errResponse) 19 | } else { 20 | resp := resps[len(resps)-1] 21 | if resp.Ref != "" { 22 | resp = g.api.Responses[resp.Ref] 23 | } 24 | typ := resp.Type 25 | if typ.Ref != "" { 26 | typ = g.api.Types[typ.Ref] 27 | } 28 | if typ.Kind != spec.Error { 29 | resps = append(resps, errResponse) 30 | } 31 | } 32 | v.Responses = resps 33 | 34 | err = g.generateOperations(v) 35 | if err != nil { 36 | return err 37 | } 38 | err = g.generateFuncBody(v) 39 | if err != nil { 40 | return err 41 | } 42 | } 43 | return 44 | } 45 | 46 | func (g *GenClient) mergeMiddlewareRequests(sreq []*spec.Request) (reqs []*spec.Request, err error) { 47 | for _, req := range sreq { 48 | if req.Ref != "" { 49 | req = g.api.Requests[req.Ref] 50 | } 51 | switch req.In { 52 | case "security": 53 | // No action 54 | case "middleware": 55 | for _, v := range g.api.Middlewares { 56 | if len(v.Responses) == 0 { 57 | continue 58 | } 59 | resp := v.Responses[0] 60 | if resp.Ref != "" { 61 | resp = g.api.Responses[resp.Ref] 62 | } 63 | 64 | if req.Name == resp.Name { 65 | r, err := g.mergeMiddlewareRequests(v.Requests) 66 | if err != nil { 67 | return nil, err 68 | } 69 | reqs = append(reqs, r...) 70 | } 71 | } 72 | 73 | case "header", "path", "query", "body": 74 | reqs = append(reqs, req) 75 | } 76 | } 77 | return reqs, nil 78 | } 79 | 80 | func (g *GenClient) generateOperations(oper *spec.Operation) (err error) { 81 | g.buf.WriteFormat(` 82 | %sfunc `, utils.CommentLine(oper.Description)) 83 | 84 | if oper.Type != nil { 85 | g.buf.WriteByte('(') 86 | err = g.Types(oper.Type) 87 | if err != nil { 88 | return err 89 | } 90 | g.buf.WriteByte(')') 91 | } 92 | g.buf.WriteString(g.getFuncName(oper)) 93 | g.buf.WriteByte('(') 94 | reqs, err := g.mergeMiddlewareRequests(oper.Requests) 95 | if err != nil { 96 | return err 97 | } 98 | for i, req := range reqs { 99 | if i != 0 { 100 | g.buf.WriteByte(',') 101 | } 102 | typ := "" 103 | switch req.Content { 104 | case "file", "image": 105 | g.buf.AddImport("", "io") 106 | typ = "io.Reader" 107 | } 108 | err = g.generateParameterRequests(req, typ) 109 | if err != nil { 110 | return err 111 | } 112 | 113 | } 114 | g.buf.WriteString(")(") 115 | 116 | for i, v := range oper.Responses { 117 | if i != 0 { 118 | g.buf.WriteByte(',') 119 | } 120 | err = g.generateParameterResponses(v) 121 | if err != nil { 122 | return err 123 | } 124 | } 125 | 126 | g.buf.WriteByte(')') 127 | return 128 | } 129 | -------------------------------------------------------------------------------- /client/security.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "sort" 5 | 6 | "github.com/wzshiming/gen/spec" 7 | "github.com/wzshiming/gen/utils" 8 | ) 9 | 10 | func (g *GenClient) generateSecuritys() (err error) { 11 | secuKey := make([]string, 0, len(g.api.Securitys)) 12 | for k := range g.api.Securitys { 13 | secuKey = append(secuKey, k) 14 | } 15 | sort.Strings(secuKey) 16 | for _, k := range secuKey { 17 | secu := g.api.Securitys[k] 18 | err = g.generateSecurity(secu) 19 | if err != nil { 20 | return err 21 | } 22 | err = g.generateSecurityBody(secu) 23 | if err != nil { 24 | return err 25 | } 26 | } 27 | 28 | return nil 29 | } 30 | 31 | func (g *GenClient) generateSecurity(oper *spec.Security) (err error) { 32 | g.buf.WriteString(utils.CommentLine(oper.Description)) 33 | g.buf.WriteString("func ") 34 | 35 | if oper.Type != nil { 36 | g.buf.WriteByte('(') 37 | err = g.Types(oper.Type) 38 | if err != nil { 39 | return err 40 | } 41 | g.buf.WriteByte(')') 42 | } 43 | g.buf.WriteString(g.getSecurityName(oper)) 44 | g.buf.WriteByte('(') 45 | 46 | switch oper.Schema { 47 | case "apiKey": 48 | reqs := []*spec.Request{} 49 | for _, req := range oper.Requests { 50 | if req.Ref != "" { 51 | req = g.api.Requests[req.Ref] 52 | } 53 | switch req.In { 54 | case "security": 55 | // No action 56 | case "header", "path", "query", "body": 57 | reqs = append(reqs, req) 58 | } 59 | } 60 | 61 | for i, req := range reqs { 62 | if i != 0 { 63 | g.buf.WriteByte(',') 64 | } 65 | err = g.generateParameterRequests(req, "") 66 | if err != nil { 67 | return err 68 | } 69 | } 70 | case "basic": 71 | g.buf.WriteString("username string, password string") 72 | case "bearer": 73 | g.buf.WriteString("token string") 74 | } 75 | 76 | g.buf.WriteString(")") 77 | return 78 | } 79 | 80 | func (g *GenClient) generateSecurityBody(oper *spec.Security) (err error) { 81 | g.buf.WriteString(`{ 82 | `) 83 | for _, v := range oper.Requests { 84 | req := v 85 | if req.Ref != "" { 86 | req = g.api.Requests[req.Ref] 87 | } 88 | 89 | switch req.In { 90 | case "header", "cookie", "path", "query": 91 | name := g.getVarName(req.Name, req.Type) 92 | g.buf.WriteFormat(`var _%s string 93 | `, name) 94 | } 95 | } 96 | 97 | for _, v := range oper.Requests { 98 | req := v 99 | if req.Ref != "" { 100 | req = g.api.Requests[req.Ref] 101 | } 102 | 103 | switch req.In { 104 | case "header", "cookie", "path", "query": 105 | name := g.getVarName(req.Name, req.Type) 106 | err = g.GenModel.ConvertFrom(name, "_"+name, req.Type) 107 | if err != nil { 108 | return err 109 | } 110 | } 111 | } 112 | 113 | g.buf.WriteString(` 114 | Client = Client`) 115 | 116 | switch oper.Schema { 117 | case "apiKey": 118 | for _, req := range oper.Requests { 119 | if req.Ref != "" { 120 | req = g.api.Requests[req.Ref] 121 | } 122 | switch req.In { 123 | case "none": 124 | 125 | case "security": 126 | // No action 127 | case "header": 128 | g.buf.WriteFormat(`. 129 | SetHeader("%s", _%s)`, req.Name, g.getVarName(req.Name, req.Type)) 130 | case "cookie": 131 | // TODO 132 | case "path": 133 | g.buf.WriteFormat(`. 134 | SetPath("%s", _%s)`, req.Name, g.getVarName(req.Name, req.Type)) 135 | case "query": 136 | g.buf.WriteFormat(`. 137 | SetQuery("%s", _%s)`, req.Name, g.getVarName(req.Name, req.Type)) 138 | case "body": 139 | // No action 140 | } 141 | } 142 | case "basic": 143 | g.buf.WriteString(`. 144 | SetBasicAuth(username, password) 145 | `) 146 | case "bearer": 147 | g.buf.WriteString(`. 148 | SetAuthToken(token) 149 | `) 150 | } 151 | g.buf.WriteString(` 152 | } 153 | `) 154 | return nil 155 | } 156 | -------------------------------------------------------------------------------- /client/utils.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | "unsafe" 8 | 9 | "github.com/wzshiming/gen/spec" 10 | "github.com/wzshiming/namecase" 11 | ) 12 | 13 | var errResponse = &spec.Response{ 14 | Name: "err", 15 | Type: &spec.Type{ 16 | Kind: spec.Error, 17 | }, 18 | } 19 | 20 | func (g *GenClient) getVarName(name string, typ *spec.Type) string { 21 | if typ == nil { 22 | if name == "" { 23 | return "_" 24 | } 25 | return name 26 | } 27 | 28 | addr := strconv.FormatUint(uint64(uintptr(unsafe.Pointer(typ))), 16) 29 | 30 | if typ.Ref != "" { 31 | typ = g.api.Types[typ.Ref] 32 | } 33 | if typ.Kind == spec.Error { 34 | return "err" 35 | } 36 | for typ != nil && (name == "_" || name == "") { 37 | if typ.Ref != "" { 38 | typ = g.api.Types[typ.Ref] 39 | } 40 | name = typ.Name 41 | typ = typ.Elem 42 | } 43 | 44 | return g.named.GetSubNamed("").GetName("_"+namecase.ToLowerHumpInitialisms(fmt.Sprintf("%s", name)), addr) 45 | } 46 | 47 | func (g *GenClient) getTypeName(typ *spec.Type) string { 48 | name := typ.Name 49 | addr := strconv.FormatUint(uint64(uintptr(unsafe.Pointer(typ))), 16) 50 | return g.named.GetName(name, addr) 51 | } 52 | 53 | func (g *GenClient) getFuncName(oper *spec.Operation) string { 54 | name := namecase.ToUpperHumpInitialisms(strings.Join(append(oper.Chain, oper.Name), "_")) 55 | 56 | named := g.named 57 | if oper.Type != nil { 58 | addr := strconv.FormatUint(uint64(uintptr(unsafe.Pointer(oper.Type))), 16) 59 | named = named.GetSubNamed(addr) 60 | } 61 | 62 | addr := strconv.FormatUint(uint64(uintptr(unsafe.Pointer(oper))), 16) 63 | return named.GetName(name, addr) 64 | } 65 | 66 | func (g *GenClient) getSecurityName(secu *spec.Security) string { 67 | name := secu.Name 68 | 69 | named := g.named 70 | if secu.Type != nil { 71 | addr := strconv.FormatUint(uint64(uintptr(unsafe.Pointer(secu.Type))), 16) 72 | named = named.GetSubNamed(addr) 73 | } 74 | 75 | addr := strconv.FormatUint(uint64(uintptr(unsafe.Pointer(secu))), 16) 76 | return named.GetName(name, addr) 77 | } 78 | 79 | func (g *GenClient) getEnumName(name, value string) string { 80 | return g.named.GetName(name, value) 81 | } 82 | -------------------------------------------------------------------------------- /cmd/gen/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/spf13/cobra" 7 | "github.com/wzshiming/gen/client" 8 | "github.com/wzshiming/gen/parser" 9 | ) 10 | 11 | var ( 12 | out string 13 | pack string 14 | way string 15 | explode bool 16 | ) 17 | 18 | func init() { 19 | flag := Cmd.Flags() 20 | flag.StringVarP(&out, "out", "o", "client_gen.go", "output file") 21 | flag.StringVarP(&pack, "package", "p", "", "package name") 22 | flag.StringVarP(&way, "way", "w", "", "way to export") 23 | flag.BoolVarP(&explode, "explode", "", false, "query parameter of array type explode") 24 | } 25 | 26 | var Cmd = &cobra.Command{ 27 | Use: "client [flags] package [package...]", 28 | Short: "Generate client source code for functions", 29 | Args: func(cmd *cobra.Command, args []string) error { 30 | if len(args) == 0 { 31 | return errors.New("Miss package path") 32 | } 33 | return nil 34 | }, 35 | RunE: func(cmd *cobra.Command, args []string) error { 36 | def := parser.NewParser(nil) 37 | 38 | for _, pkg := range args { 39 | err := def.Import(pkg, way) 40 | if err != nil { 41 | return err 42 | } 43 | } 44 | d, err := client.NewGenClient(def.API()). 45 | SetExplode(explode). 46 | Generate() 47 | if err != nil { 48 | return err 49 | } 50 | 51 | return d.WithPackname(pack).WithFilename(out).Save() 52 | }, 53 | } 54 | -------------------------------------------------------------------------------- /cmd/gen/crud/crud.go: -------------------------------------------------------------------------------- 1 | package crud 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "go/build" 7 | "go/format" 8 | "io/ioutil" 9 | "strings" 10 | 11 | "github.com/spf13/cobra" 12 | "github.com/wzshiming/gen/crud" 13 | "github.com/wzshiming/namecase" 14 | ) 15 | 16 | var ( 17 | pkgname string 18 | tplname string 19 | name string 20 | out string 21 | ) 22 | 23 | func init() { 24 | flag := Cmd.Flags() 25 | flag.StringVarP(&tplname, "tpl", "t", "mock", fmt.Sprintf("tpl name (%s)", strings.Join(crud.TplNames(), ", "))) 26 | flag.StringVarP(&name, "name", "n", "", "type name") 27 | flag.StringVarP(&pkgname, "package", "p", "", "package name") 28 | flag.StringVarP(&out, "out", "o", "", "out file name") 29 | } 30 | 31 | var Cmd = &cobra.Command{ 32 | Use: "crud [flags]", 33 | Short: "Generate CRUD source code of type", 34 | Args: func(cmd *cobra.Command, args []string) error { 35 | if name == "" { 36 | return errors.New("need the type name") 37 | } 38 | return nil 39 | }, 40 | RunE: func(cmd *cobra.Command, args []string) error { 41 | 42 | if pkgname == "" { 43 | i, err := build.Import(".", ".", 0) 44 | if err == nil { 45 | pkgname = i.Name 46 | } 47 | } 48 | 49 | if pkgname == "" { 50 | pkgname = "main" 51 | } 52 | 53 | g := crud.NewGenCrud() 54 | data, err := g.Generate(tplname, pkgname, name) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | if out == "" { 60 | out = namecase.ToLowerSnake(name) + ".go" 61 | } 62 | 63 | if data0, err := format.Source(data); err == nil { 64 | data = data0 65 | } 66 | 67 | err = ioutil.WriteFile(out, data, 0666) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | return nil 73 | 74 | }, 75 | } 76 | -------------------------------------------------------------------------------- /cmd/gen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | "github.com/wzshiming/gen/cmd/gen/client" 6 | "github.com/wzshiming/gen/cmd/gen/crud" 7 | "github.com/wzshiming/gen/cmd/gen/openapi" 8 | "github.com/wzshiming/gen/cmd/gen/route" 9 | "github.com/wzshiming/gen/cmd/gen/run" 10 | ) 11 | 12 | func main() { 13 | rootCmd.AddCommand(client.Cmd, run.Cmd, route.Cmd, openapi.Cmd, crud.Cmd) 14 | rootCmd.Execute() 15 | } 16 | 17 | var rootCmd = &cobra.Command{ 18 | Use: "gen", 19 | Short: "generated source code tool for micro services", 20 | } 21 | -------------------------------------------------------------------------------- /cmd/gen/openapi/openapi.go: -------------------------------------------------------------------------------- 1 | package openapi 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | 9 | "github.com/spf13/cobra" 10 | "github.com/wzshiming/gen/openapi" 11 | "github.com/wzshiming/gen/parser" 12 | oaspec "github.com/wzshiming/openapi/spec" 13 | "github.com/wzshiming/openapi/util" 14 | ) 15 | 16 | var ( 17 | servers []string 18 | out string 19 | format string 20 | info string 21 | way string 22 | explode bool 23 | ) 24 | 25 | func init() { 26 | flag := Cmd.Flags() 27 | flag.StringSliceVarP(&servers, "servers", "s", nil, "") 28 | flag.StringVarP(&out, "out", "o", "openapi.json", "output file name") 29 | flag.StringVarP(&format, "format", "f", "json", "json or yaml") 30 | flag.StringVarP(&info, "info", "i", "", "Info") 31 | flag.StringVarP(&way, "way", "w", "", "way to export") 32 | flag.BoolVarP(&explode, "explode", "", false, "query parameter of array type explode") 33 | } 34 | 35 | var Cmd = &cobra.Command{ 36 | Use: "openapi [flags] package [package ...]", 37 | Short: "Generate openapi document for functions", 38 | Args: func(cmd *cobra.Command, args []string) error { 39 | if len(args) == 0 { 40 | return errors.New("Miss package path") 41 | } 42 | return nil 43 | }, 44 | RunE: func(cmd *cobra.Command, args []string) error { 45 | def := parser.NewParser(nil) 46 | for _, arg := range args { 47 | err := def.Import(arg, way) 48 | if err != nil { 49 | return err 50 | } 51 | } 52 | 53 | var oainfo *oaspec.Info 54 | 55 | if info != "" { 56 | fil, err := ioutil.ReadFile(info) 57 | if err != nil { 58 | return err 59 | } 60 | fil, err = util.YAML2JSON(fil) 61 | 62 | if err != nil { 63 | return err 64 | } 65 | err = json.Unmarshal(fil, &oainfo) 66 | if err != nil { 67 | return err 68 | } 69 | } 70 | 71 | api, err := openapi.NewGenOpenAPI(def.API()). 72 | WithServices(servers...). 73 | SetInfo(oainfo). 74 | SetExplode(explode). 75 | Generate() 76 | if err != nil { 77 | return err 78 | } 79 | 80 | d, err := json.MarshalIndent(api, "", " ") 81 | if err != nil { 82 | return err 83 | } 84 | 85 | switch format { 86 | case "json": 87 | case "yaml": 88 | d, err = util.JSON2YAML(d) 89 | if err != nil { 90 | return err 91 | } 92 | 93 | default: 94 | return fmt.Errorf("undefined format %s", format) 95 | } 96 | if out != "" { 97 | err := ioutil.WriteFile(out, d, 0666) 98 | if err != nil { 99 | return err 100 | } 101 | } else { 102 | fmt.Println(string(d)) 103 | } 104 | 105 | return nil 106 | 107 | }, 108 | } 109 | -------------------------------------------------------------------------------- /cmd/gen/route/route.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "io/ioutil" 7 | "path/filepath" 8 | 9 | "github.com/spf13/cobra" 10 | "github.com/wzshiming/gen/openapi" 11 | "github.com/wzshiming/gen/parser" 12 | "github.com/wzshiming/gen/route" 13 | "github.com/wzshiming/gen/utils" 14 | oaspec "github.com/wzshiming/openapi/spec" 15 | "github.com/wzshiming/openapi/util" 16 | ) 17 | 18 | var ( 19 | out string 20 | name string 21 | pack string 22 | servers []string 23 | info string 24 | openapiF bool 25 | way string 26 | explode bool 27 | ) 28 | 29 | func init() { 30 | flag := Cmd.Flags() 31 | flag.StringVarP(&out, "out", "o", "router_gen.go", "output file name") 32 | flag.StringVarP(&name, "name", "n", "Router", "routing function name") 33 | flag.StringVarP(&pack, "package", "p", "", "package name") 34 | flag.BoolVarP(&openapiF, "openapi", "", false, "with openapi") 35 | flag.StringSliceVarP(&servers, "servers", "s", nil, "") 36 | flag.StringVarP(&info, "info", "i", "", "Info") 37 | flag.StringVarP(&way, "way", "w", "", "way to export") 38 | flag.BoolVarP(&explode, "explode", "", false, "query parameter of array type explode") 39 | } 40 | 41 | var Cmd = &cobra.Command{ 42 | Use: "route [flags] package [package ...]", 43 | Short: "Generate routing source code for functions", 44 | Args: func(cmd *cobra.Command, args []string) error { 45 | if len(args) == 0 { 46 | return errors.New("Miss package path") 47 | } 48 | return nil 49 | }, 50 | RunE: func(cmd *cobra.Command, args []string) error { 51 | dir, _ := filepath.Abs(out) 52 | dir = filepath.Dir(dir) 53 | impPath := utils.GetPackagePath(dir) 54 | def := parser.NewParser(nil) 55 | for _, arg := range args { 56 | err := def.Import(arg, way) 57 | if err != nil { 58 | return err 59 | } 60 | } 61 | 62 | rg := route.NewGenRoute(def.API()). 63 | SetExplode(explode) 64 | if openapiF { 65 | 66 | var oainfo *oaspec.Info 67 | 68 | if info != "" { 69 | fil, err := ioutil.ReadFile(info) 70 | if err != nil { 71 | return err 72 | } 73 | fil, err = util.YAML2JSON(fil) 74 | 75 | if err != nil { 76 | return err 77 | } 78 | err = json.Unmarshal(fil, &oainfo) 79 | if err != nil { 80 | return err 81 | } 82 | } 83 | 84 | api, err := openapi.NewGenOpenAPI(def.API()). 85 | WithServices(servers...). 86 | SetInfo(oainfo). 87 | SetExplode(explode). 88 | Generate() 89 | if err != nil { 90 | return err 91 | } 92 | rg = rg.WithOpenAPI(api) 93 | } 94 | d, err := rg.Generate(pack, impPath, name) 95 | if err != nil { 96 | return err 97 | } 98 | 99 | return d.WithFilename(out).Save() 100 | }, 101 | } 102 | -------------------------------------------------------------------------------- /cmd/gen/run/run.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/spf13/cobra" 8 | "github.com/wzshiming/gen/run" 9 | ) 10 | 11 | var ( 12 | port uint 13 | way string 14 | explode bool 15 | ) 16 | 17 | func init() { 18 | flag := Cmd.Flags() 19 | flag.UintVarP(&port, "port", "p", 8080, "listening port") 20 | flag.StringVarP(&way, "way", "w", "", "way to export") 21 | flag.BoolVarP(&explode, "explode", "", false, "query parameter of array type explode") 22 | } 23 | 24 | var Cmd = &cobra.Command{ 25 | Use: "run [flags] package [package ...]", 26 | Short: "Run package", 27 | Args: func(cmd *cobra.Command, args []string) error { 28 | if len(args) == 0 { 29 | return errors.New("Miss package path") 30 | } 31 | return nil 32 | }, 33 | RunE: func(cmd *cobra.Command, args []string) error { 34 | return run.Run(args, fmt.Sprintf(":%d", port), way, explode) 35 | }, 36 | } 37 | -------------------------------------------------------------------------------- /crud/crud.go: -------------------------------------------------------------------------------- 1 | package crud 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | "text/template" 7 | "unsafe" 8 | 9 | "github.com/wzshiming/gen/crud/tpl" 10 | "github.com/wzshiming/namecase" 11 | ) 12 | 13 | type GenCrud struct { 14 | } 15 | 16 | func NewGenCrud() *GenCrud { 17 | return &GenCrud{} 18 | } 19 | 20 | const suf = ".go.tpl" 21 | 22 | func TplNames() []string { 23 | an := tpl.AssetNames() 24 | names := make([]string, 0, len(an)) 25 | for _, name := range an { 26 | if strings.HasSuffix(name, suf) { 27 | names = append(names, name[:len(name)-len(suf)]) 28 | } 29 | } 30 | return names 31 | } 32 | 33 | func (g *GenCrud) Generate(tplname, pkgname, typname string) ([]byte, error) { 34 | file, err := tpl.Asset(tplname + suf) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | fb := *(*string)(unsafe.Pointer(&file)) 40 | temp, err := template.New("").Delims("<", ">").Parse(fb) 41 | if err != nil { 42 | return nil, err 43 | } 44 | buf := bytes.NewBuffer(nil) 45 | err = temp.Execute(buf, map[string]string{ 46 | "Package": pkgname, 47 | "Original": typname, 48 | "UpperHump": namecase.ToUpperHump(typname), 49 | "LowerHump": namecase.ToLowerHump(typname), 50 | "LowerSnake": namecase.ToLowerSnake(typname), 51 | }) 52 | if err != nil { 53 | return nil, err 54 | } 55 | return buf.Bytes(), nil 56 | } 57 | -------------------------------------------------------------------------------- /crud/tpl/Makefile: -------------------------------------------------------------------------------- 1 | 2 | .PHONY: tpl.go 3 | 4 | tpl.go: 5 | go-bindata --nomemcopy --pkg tpl -o tpl.go ./*.tpl 6 | -------------------------------------------------------------------------------- /crud/tpl/mgo.go.tpl: -------------------------------------------------------------------------------- 1 | // Code generated; Do not regenerate the overwrite after editing. 2 | 3 | package < .Package > 4 | 5 | import ( 6 | "time" 7 | 8 | "github.com/globalsign/mgo" 9 | "github.com/globalsign/mgo/bson" 10 | ) 11 | 12 | // < .UpperHump >WithID is < .UpperHump > with ID 13 | type < .UpperHump >WithID struct { 14 | ID bson.ObjectId `bson:"_id,omitempty" json:"< .LowerSnake >_id"` 15 | < .UpperHump > `bson:",inline"` 16 | CreateTime time.Time `bson:"create_time,omitempty" json:"create_time"` 17 | UpdateTime time.Time `bson:"update_time,omitempty" json:"update_time"` 18 | } 19 | 20 | // < .UpperHump >Record is record of the < .UpperHump > 21 | type < .UpperHump >Record struct { 22 | ID bson.ObjectId `bson:"_id,omitempty" json:"< .LowerSnake >_record_id"` 23 | < .UpperHump >ID bson.ObjectId `bson:"< .LowerSnake >_id,omitempty" json:"< .LowerSnake >_id"` 24 | Recent *< .UpperHump > `bson:"recent,omitempty" json:"recent"` 25 | Current *< .UpperHump > `bson:"current,omitempty" json:"current"` 26 | RecentTime time.Time `bson:"recent_time,omitempty" json:"recent_time"` 27 | CurrentTime time.Time `bson:"current_time,omitempty" json:"current_time"` 28 | Times int `bson:"times,omitempty" json:"times"` 29 | } 30 | 31 | // < .UpperHump >Service is service of the < .UpperHump > 32 | // #path:"/< .LowerSnake >/"# 33 | type < .UpperHump >Service struct { 34 | db *mgo.Collection 35 | dbRecord *mgo.Collection 36 | } 37 | 38 | // New< .UpperHump >Service Create a new < .UpperHump >Service 39 | func New< .UpperHump >Service(db *mgo.Collection) (*< .UpperHump >Service, error) { 40 | dbRecord := db.Database.C(db.Name + "_record") 41 | dbRecord.EnsureIndex(mgo.Index{Key: []string{"< .LowerSnake >_id"}}) 42 | return &< .UpperHump >Service{ 43 | db: db, 44 | dbRecord: dbRecord, 45 | }, nil 46 | } 47 | 48 | // Create a < .UpperHump > 49 | // #route:"POST /"# 50 | func (s *< .UpperHump >Service) Create(< .LowerHump > *< .UpperHump >) (< .LowerHump >ID bson.ObjectId /* #name:"< .LowerSnake >_id"# */, err error) { 51 | < .LowerHump >ID = bson.NewObjectId() 52 | now := bson.Now() 53 | err = s.db.Insert(&< .UpperHump >WithID { 54 | ID: < .LowerHump >ID, 55 | < .Original >: *< .LowerHump >, 56 | CreateTime: now, 57 | UpdateTime: now, 58 | }) 59 | if err != nil { 60 | return "", err 61 | } 62 | return < .LowerHump >ID, nil 63 | } 64 | 65 | // Update the < .UpperHump > 66 | // #route:"PUT /{< .LowerSnake >_id}"# 67 | func (s *< .UpperHump >Service) Update(< .LowerHump >ID bson.ObjectId /* #name:"< .LowerSnake >_id"# */, < .LowerHump > *< .UpperHump >) (err error) { 68 | recent, err := s.Get(< .LowerHump >ID) 69 | if err != nil { 70 | if err == mgo.ErrNotFound { 71 | return nil 72 | } 73 | return err 74 | } 75 | 76 | err = s.db.UpdateId(< .LowerHump >ID, bson.D{{"$set", &< .UpperHump >WithID{ 77 | < .UpperHump >: *< .LowerHump >, 78 | UpdateTime: bson.Now(), 79 | }}}) 80 | if err != nil { 81 | return err 82 | } 83 | 84 | current, err := s.Get(< .LowerHump >ID) 85 | if err != nil { 86 | if err == mgo.ErrNotFound { 87 | return nil 88 | } 89 | return err 90 | } 91 | 92 | err = s.record(recent, ¤t.< .UpperHump >) 93 | if err != nil { 94 | return err 95 | } 96 | 97 | return nil 98 | } 99 | 100 | // Delete the < .UpperHump > 101 | // #route:"DELETE /{< .LowerSnake >_id}"# 102 | func (s *< .UpperHump >Service) Delete(< .LowerHump >ID bson.ObjectId /* #name:"< .LowerSnake >_id"# */) (err error) { 103 | recent, err := s.Get(< .LowerHump >ID) 104 | if err != nil { 105 | if err == mgo.ErrNotFound { 106 | return nil 107 | } 108 | return err 109 | } 110 | 111 | err = s.db.RemoveId(< .LowerHump >ID) 112 | if err != nil { 113 | return err 114 | } 115 | 116 | err = s.record(recent, nil) 117 | if err != nil { 118 | return err 119 | } 120 | 121 | return nil 122 | } 123 | 124 | // Get the < .UpperHump > 125 | // #route:"GET /{< .LowerSnake >_id}"# 126 | func (s *< .UpperHump >Service) Get(< .LowerHump >ID bson.ObjectId /* #name:"< .LowerSnake >_id"# */) (< .LowerHump > *< .UpperHump >WithID, err error) { 127 | q := s.db.FindId(< .LowerHump >ID) 128 | err = q.One(&< .LowerHump >) 129 | if err != nil { 130 | return nil, err 131 | } 132 | return < .LowerHump >, nil 133 | } 134 | 135 | // List of the < .UpperHump > 136 | // #route:"GET /"# 137 | func (s *< .UpperHump >Service) List(startTime /* #name:"start_time"# */, endTime time.Time /* #name:"end_time"# */, offset, limit int, sort string) (< .LowerHump >s []*< .UpperHump >WithID, err error) { 138 | m := bson.D{} 139 | if !startTime.IsZero() || !endTime.IsZero() { 140 | m0 := bson.D{} 141 | if !startTime.IsZero() { 142 | m0 = append(m0, bson.DocElem{"$gte", startTime}) 143 | } 144 | if !endTime.IsZero() { 145 | m0 = append(m0, bson.DocElem{"$lt", endTime}) 146 | } 147 | m = append(m, bson.DocElem{"create_time", m0}) 148 | } 149 | q := s.db.Find(m).Skip(offset).Limit(limit).Sort(sort) 150 | err = q.All(&< .LowerHump >s) 151 | if err != nil { 152 | return nil, err 153 | } 154 | return < .LowerHump >s, nil 155 | } 156 | 157 | // Count of the < .UpperHump > 158 | // #route:"GET /count"# 159 | func (s *< .UpperHump >Service) Count(startTime /* #name:"start_time"# */, endTime time.Time /* #name:"end_time"# */) (count int, err error) { 160 | m := bson.D{} 161 | if !startTime.IsZero() || !endTime.IsZero() { 162 | m0 := bson.D{} 163 | if !startTime.IsZero() { 164 | m0 = append(m0, bson.DocElem{"$gte", startTime}) 165 | } 166 | if !endTime.IsZero() { 167 | m0 = append(m0, bson.DocElem{"$lt", endTime}) 168 | } 169 | m = append(m, bson.DocElem{"create_time", m0}) 170 | } 171 | q := s.db.Find(m) 172 | return q.Count() 173 | } 174 | 175 | // RecordList of the < .UpperHump > record list 176 | // #route:"GET /{< .LowerSnake >_id}/record"# 177 | func (s *< .UpperHump >Service) RecordList(< .LowerHump >ID bson.ObjectId /* #name:"< .LowerSnake >_id"# */, offset, limit int) (< .LowerHump >Records []*< .UpperHump >Record, err error) { 178 | m := bson.D{{"< .LowerSnake >_id", < .LowerHump >ID}} 179 | q := s.dbRecord.Find(m).Skip(offset).Limit(limit) 180 | err = q.All(&< .LowerHump >Records) 181 | if err != nil { 182 | return nil, err 183 | } 184 | return < .LowerHump >Records, nil 185 | } 186 | 187 | // RecordCount of the < .UpperHump > record count 188 | // #route:"GET /{< .LowerSnake >_id}/record/count"# 189 | func (s *< .UpperHump >Service) RecordCount(< .LowerHump >ID bson.ObjectId /* #name:"< .LowerSnake >_id"# */) (count int, err error) { 190 | m := bson.D{{"< .LowerSnake >_id", < .LowerHump >ID}} 191 | q := s.dbRecord.Find(m) 192 | return q.Count() 193 | } 194 | 195 | func (s *< .UpperHump >Service) record(< .LowerHump > *< .UpperHump >WithID, current *< .UpperHump >) error { 196 | if < .LowerHump > == nil { 197 | return nil 198 | } 199 | count, err := s.dbRecord.Find(bson.D{{"< .LowerSnake >_id", < .LowerHump >.ID}}).Count() 200 | if err != nil { 201 | return err 202 | } 203 | record := &< .UpperHump >Record{ 204 | < .UpperHump >ID: < .LowerHump >.ID, 205 | Current: current, 206 | CurrentTime: bson.Now(), 207 | Times: count + 1, 208 | Recent: &< .LowerHump >.< .UpperHump >, 209 | RecentTime: < .LowerHump >.UpdateTime, 210 | } 211 | return s.dbRecord.Insert(record) 212 | } 213 | 214 | -------------------------------------------------------------------------------- /crud/tpl/mock.go.tpl: -------------------------------------------------------------------------------- 1 | // Code generated; Do not regenerate the overwrite after editing. 2 | 3 | package < .Package > 4 | 5 | import ( 6 | "errors" 7 | ) 8 | 9 | // < .UpperHump >WithID is < .UpperHump > with ID 10 | type < .UpperHump >WithID struct { 11 | ID int `json:"< .LowerSnake >_id,string"` 12 | < .Original > 13 | } 14 | 15 | // < .UpperHump >Service is service of the < .UpperHump > 16 | // #path:"/< .LowerSnake >/"# 17 | type < .UpperHump >Service struct { 18 | datas []*< .UpperHump >WithID 19 | } 20 | 21 | // New< .UpperHump >Service Create a new < .UpperHump >Service 22 | func New< .UpperHump >Service() (*< .UpperHump >Service, error) { 23 | return &< .UpperHump >Service{}, nil 24 | } 25 | 26 | // Create a < .UpperHump > 27 | // #route:"POST /"# 28 | func (s *< .UpperHump >Service) Create(< .LowerHump > *< .Original >) (err error) { 29 | < .LowerHump >ID := len(s.datas) + 1 30 | data := &< .UpperHump >WithID { 31 | ID: < .LowerHump >ID , 32 | < .Original >: *< .LowerHump >, 33 | } 34 | s.datas = append(s.datas, data) 35 | return nil 36 | } 37 | 38 | // Update the < .UpperHump > 39 | // #route:"PUT /{< .LowerSnake >_id}"# 40 | func (s *< .UpperHump >Service) Update(< .LowerHump >ID int /* #name:"< .LowerSnake >_id"# */, < .LowerHump > *< .Original >) (err error) { 41 | if 0 >= < .LowerHump >ID || < .LowerHump >ID > len(s.datas) || s.datas[< .LowerHump >ID-1] == nil { 42 | return errors.New("id does not exist") 43 | } 44 | v := s.datas[< .LowerHump >ID-1] 45 | v.< .Original > = *< .LowerHump > 46 | return nil 47 | } 48 | 49 | // Delete the < .UpperHump > 50 | // #route:"DELETE /{< .LowerSnake >_id}"# 51 | func (s *< .UpperHump >Service) Delete(< .LowerHump >ID int /* #name:"< .LowerSnake >_id"# */) (err error) { 52 | if 0 >= < .LowerHump >ID || < .LowerHump >ID > len(s.datas) || s.datas[< .LowerHump >ID-1] == nil { 53 | return errors.New("id does not exist") 54 | } 55 | s.datas[< .LowerHump >ID-1] = nil 56 | return nil 57 | } 58 | 59 | // Get the < .UpperHump > 60 | // #route:"GET /{< .LowerSnake >_id}"# 61 | func (s *< .UpperHump >Service) Get(< .LowerHump >ID int /* #name:"< .LowerSnake >_id"# */) (< .LowerHump > *< .UpperHump >WithID, err error) { 62 | if 0 >= < .LowerHump >ID || < .LowerHump >ID > len(s.datas) || s.datas[< .LowerHump >ID-1] == nil { 63 | return nil, errors.New("id does not exist") 64 | } 65 | return s.datas[< .LowerHump >ID-1], nil 66 | } 67 | 68 | // List of the < .UpperHump > 69 | // #route:"GET /"# 70 | func (s *< .UpperHump >Service) List(offset, limit int) (< .LowerHump >s []*< .UpperHump >WithID, err error) { 71 | off := 0 72 | lim := 0 73 | for _, v := range s.datas { 74 | if v != nil { 75 | if offset != 0 && off != offset { 76 | off++ 77 | continue 78 | } 79 | if limit == 0 || lim != limit{ 80 | lim++ 81 | < .LowerHump >s = append(< .LowerHump >s, v) 82 | if lim == limit { 83 | break 84 | } 85 | continue 86 | } 87 | } 88 | } 89 | return < .LowerHump >s, nil 90 | } 91 | -------------------------------------------------------------------------------- /crud/tpl/tpl.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-bindata. 2 | // sources: 3 | // mgo.go.tpl 4 | // mock.go.tpl 5 | // DO NOT EDIT! 6 | 7 | package tpl 8 | 9 | import ( 10 | "bytes" 11 | "compress/gzip" 12 | "fmt" 13 | "io" 14 | "io/ioutil" 15 | "os" 16 | "path/filepath" 17 | "strings" 18 | "time" 19 | ) 20 | 21 | func bindataRead(data, name string) ([]byte, error) { 22 | gz, err := gzip.NewReader(strings.NewReader(data)) 23 | if err != nil { 24 | return nil, fmt.Errorf("Read %q: %v", name, err) 25 | } 26 | 27 | var buf bytes.Buffer 28 | _, err = io.Copy(&buf, gz) 29 | clErr := gz.Close() 30 | 31 | if err != nil { 32 | return nil, fmt.Errorf("Read %q: %v", name, err) 33 | } 34 | if clErr != nil { 35 | return nil, err 36 | } 37 | 38 | return buf.Bytes(), nil 39 | } 40 | 41 | type asset struct { 42 | bytes []byte 43 | info os.FileInfo 44 | } 45 | 46 | type bindataFileInfo struct { 47 | name string 48 | size int64 49 | mode os.FileMode 50 | modTime time.Time 51 | } 52 | 53 | func (fi bindataFileInfo) Name() string { 54 | return fi.name 55 | } 56 | func (fi bindataFileInfo) Size() int64 { 57 | return fi.size 58 | } 59 | func (fi bindataFileInfo) Mode() os.FileMode { 60 | return fi.mode 61 | } 62 | func (fi bindataFileInfo) ModTime() time.Time { 63 | return fi.modTime 64 | } 65 | func (fi bindataFileInfo) IsDir() bool { 66 | return false 67 | } 68 | func (fi bindataFileInfo) Sys() interface{} { 69 | return nil 70 | } 71 | 72 | var _mgoGoTpl = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x58\x5b\x6f\xdb\x36\x14\x7e\xb6\x7e\xc5\xa9\x52\x04\x52\x6a\x48\xd9\xab\x56\x07\x18\x62\xb7\x33\x16\x24\x45\x93\x62\xc0\x8a\xa2\xd5\xe5\xd8\x61\x2b\x91\x2e\x45\xd7\x2b\x5c\xff\xf7\x81\x17\x59\xb4\x2e\xb6\x9b\x04\xd8\x1e\xe6\x27\x99\x97\x73\x0e\xbf\xef\x23\x0f\x0f\xc3\x10\x2e\x59\x86\x30\x47\x8a\x3c\x16\x98\xfd\x0a\x63\x06\x94\x09\xe0\x58\xb5\x81\xb8\x47\x60\xdf\x90\xaf\x38\x11\x08\xf1\x4c\x20\x07\xcc\x88\x20\x74\x1e\x38\xce\x22\x4e\xbf\xc4\x73\x84\x97\x10\xbc\x31\x9f\x17\x8e\x43\x8a\x05\xe3\x02\x3c\x67\xe0\x0a\x52\xa0\xeb\x38\x03\x77\x4e\xc4\xfd\x32\x09\x52\x56\x84\xf3\x9c\x25\x71\x5e\x92\x39\x0d\x8b\x39\x73\xf7\x75\x86\x49\xc9\xa8\xeb\xf8\x8e\x13\x86\xd2\xc9\xbb\xc5\x02\xf9\xef\xcb\x62\x01\x17\x7f\x12\x71\x3f\x1d\x03\x29\x1b\xed\xb0\x22\xe2\x1e\xa6\x63\x47\x7c\x5f\x60\xf7\x9c\x52\xf0\x65\x2a\x60\xed\x0c\xa6\x63\x90\x1e\x82\x9b\xe4\x33\xa6\x62\x9a\x81\xfc\x7d\x92\x4d\x91\xfb\x91\x64\x43\x56\x10\x81\xc5\x42\x7c\x77\xe1\xb3\x6a\x7c\x09\xc1\x15\x5b\x21\xbf\xa5\xf1\x17\x84\x8b\x8f\x24\x73\x3f\x39\x83\x46\x04\x60\x9b\x19\x12\x9a\x13\x8a\x72\xd8\x25\xc7\x58\xe0\x1d\x29\x10\x24\x2e\x81\xfa\x32\xc3\x52\xd5\xf7\x51\xb6\xb7\xbd\x5a\x9d\xd2\xce\xbb\x45\xd6\x6b\x67\xa9\xfa\x7a\xec\x58\x9d\xee\x27\x67\xd3\x81\xea\x5b\x4c\x19\xcf\x24\xaa\x5c\x7f\xb1\x99\x92\xc0\xee\xa8\x2e\x6c\xcd\xcc\x1d\x6c\x1b\xbf\x26\xd4\x3f\x03\xb4\x0e\xa7\x13\x6f\xe9\xa8\xc7\x74\x9b\xae\x63\x29\x7d\x8b\x29\x52\x61\x47\x7f\xd6\x60\xd9\xb8\xe0\x6a\x60\xdb\xac\x6e\x57\xb4\x2f\x39\xdf\xb5\xd5\x63\x2a\xd5\x03\x3b\xf8\xd7\x1d\x75\x5c\x8a\x71\xf3\xab\x25\xb0\xa3\x3b\xed\xbf\x47\x08\x56\xa7\x15\xa1\x65\xa4\xc7\xa8\x09\xa4\x4f\xa6\x56\xaf\x34\x2b\xe7\x97\x3b\x0a\x20\xbb\x90\x56\x66\xe5\x84\xb2\x6d\x4f\x35\xf7\x08\xf5\x16\xf9\x37\x92\xa2\x54\x6a\x69\x3e\xbb\xa5\x1a\x86\x70\xb2\x88\xc5\x7d\xe4\x86\x0d\xa6\x43\xf7\xa4\x4b\xc8\x95\xe5\x5a\xc9\x59\x52\xf1\x56\xcc\x59\x70\xc9\xf2\x1c\x53\x41\x18\x95\x3d\x46\xf6\xcd\x1e\x1d\xf2\x35\xae\xba\x6d\xeb\x93\x00\x62\xa0\xb8\xea\x76\xef\xcc\x96\x34\xed\x35\xe0\x65\x49\xd3\xa5\x0f\xde\x59\xe7\xd8\x21\x20\xe7\x8c\xfb\x7a\x25\x26\xde\x68\x04\x59\x12\x8c\x63\x11\x27\x71\x89\xc1\xa5\x97\x25\xc1\x75\x5c\x20\xbc\x00\xd7\xec\x35\xd7\xaf\xc7\x07\x13\x5a\x2e\x39\x4e\x69\x86\x7f\x7b\xd2\xaf\xfa\x5a\xff\x81\xdf\x23\x78\xff\xa1\x14\x9c\xd0\xf9\xba\x6b\x27\x6d\x36\xbe\x33\xe0\x28\x96\x9c\xc2\x69\x67\x78\x6b\x67\x30\xc8\x92\xc8\x20\x9c\x25\x43\xf5\x5f\xbb\x8d\xa0\xfa\x1a\x3a\x83\xcd\x10\x28\xc9\x0d\xb2\x5b\x00\x3b\xd8\xe6\x6c\x29\x30\x72\xdf\xdc\xdc\xde\x81\xe4\x58\x21\xe9\x95\xcd\x5d\x67\xfc\xfb\xc6\x96\x57\x45\x6f\xf6\x64\x63\xb4\x0f\x8d\x01\xad\xd4\x11\x9e\xc1\x09\x8d\x0b\xec\x3c\x51\x4e\xe0\x2c\x54\x44\x58\x64\xb4\xec\x8d\xb4\xc5\x6b\x5c\x55\x46\x3d\xdf\x19\x50\xb6\x92\x74\xe9\x2e\xb6\x92\x4d\xd2\xce\x08\xca\x20\x4b\x82\x29\x2d\x91\x0b\xef\xb4\x33\xd1\x49\x6c\xa7\xe3\x08\x9a\x9e\x24\xc6\x2f\x21\xb8\xe1\x64\x4e\x68\x9c\xc3\x45\xa4\x96\x6b\x8d\x91\x23\xea\x74\x15\x01\x65\x2b\xd9\x54\x67\x9e\xaa\x49\xf2\x4b\x66\x6a\x65\xcf\x46\x92\x20\xe5\xd4\x30\xee\xba\x6a\xcd\xce\x60\xb3\x15\x41\x2b\x14\x8b\x54\x6d\xbd\x6f\x13\x57\xb4\xbe\xbb\x83\x70\xdd\x86\x78\x73\x04\xd3\xda\xc1\x13\x10\x79\x50\x2b\xbb\x4c\x9b\x1c\xa1\x50\x8a\x24\x71\xaf\x51\xb4\xa2\xe8\x04\xd2\xb4\x8c\x46\x20\x77\xdd\x84\xf3\x6b\x26\x5e\xb1\x25\xcd\x54\x6f\x05\xaa\x84\x70\x20\x41\xae\x1a\x0c\xe8\x3b\x4a\xd1\x8b\x9f\x66\x2d\xc7\x43\xbd\xfe\xf1\x7a\xed\x3e\x2f\x51\xb8\xc3\xe6\x4e\xd5\x6a\x5a\x6b\xd1\x58\xed\x91\x49\x65\x0d\xdd\xd8\x22\xa9\x55\x2b\xb5\xb2\xd9\xab\x96\x6d\xd0\x55\x1e\xfc\x17\xf1\xd2\x47\xa0\x57\x11\x77\x6a\x42\x0a\x1a\x44\x1f\xb3\x1a\xcb\xa7\x96\xf9\x18\x73\x3c\x28\xf3\xf1\xe4\x6a\x72\x37\x79\xb0\xd2\xb5\x8f\x47\x2b\xfd\xbf\xa9\xe4\xb7\x58\xb0\x6f\x5d\x4a\x3e\x8a\x8f\x1e\x8a\x29\xc9\x1f\x46\xe7\x6b\x14\x07\xb8\x7c\x3d\x79\xf8\x91\xd5\x85\xef\x03\x58\xdc\x7f\x60\xe9\x0d\xde\xcc\x4f\x5f\x35\xc3\x59\x12\xbc\x22\x34\xeb\x86\x5b\x83\xf9\x35\xb8\xa1\xa8\x72\x90\xd5\xbf\x0f\x4d\x4a\xf2\x03\x99\xc1\xce\x0b\x57\xa4\x14\x7b\x6e\x77\x36\xca\x47\x20\x2a\xad\x79\xa5\x88\xb9\xbe\xef\xd6\xe8\xa9\x36\x7d\x79\x35\xe9\x9a\x66\x8d\x22\xab\x1e\x8c\x34\xb3\x87\xb2\xd9\xac\x44\x31\x84\x9c\x14\x44\xc8\x6b\xee\x10\x4a\x59\x03\xeb\xab\x51\x8b\x80\x12\xde\x7f\x38\x8a\x83\x62\x9b\xfa\xc7\xeb\x8d\x02\xf4\xd9\x36\xf4\x60\x5a\xfe\x85\x9c\x79\x3e\xfc\xf8\x01\xcf\x4c\xb0\x75\xa3\x84\xbb\x38\xdf\x9d\xdf\x67\x40\x6d\xc1\xe2\x1c\x46\x10\x2f\x16\x48\x33\xaf\x38\xaf\xb2\x02\x4b\x27\x39\x16\x6b\xf7\xf9\x5c\xa0\x3b\x84\xed\x64\x79\x94\x0f\x2a\x93\x9d\xce\x0f\x19\xcc\x65\xaa\x31\x33\xb7\xd6\x0a\x6b\x4a\x73\x86\x5d\x06\x0f\xa1\x38\x97\x93\x36\x4d\x9d\x7a\x85\x1f\xdc\x7e\x21\x0b\x4f\x53\xe2\x07\x57\x92\x12\x4f\x11\xe3\x07\xb7\x8c\x0b\x4f\x32\x63\x69\xf7\xb7\x3c\x6f\x6a\xb7\x7c\x9c\x78\xcb\x9d\xab\x2a\x5b\xd2\x63\xe5\x9b\xca\xb1\xc7\x5c\x59\xe5\xb8\x27\x16\xb1\x0f\x9e\x72\xaf\xd5\xfb\xbf\x0c\x1f\x2d\xc3\xad\x38\xbe\x06\x9a\x2f\xdf\x28\x42\x97\x34\xfd\xa7\x5a\xf5\xf6\x92\x93\x52\x1c\x95\x47\x42\x53\xb1\x1d\x16\x4e\xed\xfa\x09\x6e\xc1\xad\x43\xaf\x75\xce\x69\x77\x1d\xa7\x9d\xa9\xea\xf6\xc8\xac\xb3\x9c\x6c\xde\xbc\xa7\xe3\x8d\x8d\xbc\x29\x56\x0f\x1e\x03\x7b\xf7\xbe\x89\xf9\x71\x27\x80\x31\x62\x9f\x03\xba\x69\xcf\x69\x50\xd1\xae\x76\xe1\xcf\xf0\x7e\xf4\xb1\x61\x85\xf0\x14\x97\x8a\xa3\x8e\x8b\xc7\xf3\xd8\xbd\x8f\x0e\xad\xd5\x5c\xef\x8e\xbb\xf8\x98\xeb\x7d\xbb\x90\x53\x6b\x92\x4b\x22\xb3\x66\xd1\x37\xea\x92\x85\x52\x84\xc2\xc5\xba\x23\xef\x2e\xe8\x67\x70\x09\x24\x30\xfe\x76\xd9\x07\xaf\xa7\x03\xbe\x7d\xde\x39\xed\xda\x70\xed\x1a\xae\xfd\x3c\x10\xe8\xf7\x01\xf3\x26\x18\xd5\x8f\x75\x55\x59\x56\x77\xea\x1a\xaf\x7e\xdb\xad\x0a\x3d\xfd\xf2\x17\xd9\x2f\x7d\x5a\x2b\x2f\xe0\x17\xd9\xad\x1f\x31\xed\xfe\xc6\x1e\x6c\xd4\x59\xf5\x1c\xcb\x63\x33\xec\xba\xec\x1c\xda\xfb\xd2\x82\xdf\xbc\x95\x68\x8c\x94\x88\xfe\x09\x00\x00\xff\xff\xe3\x2f\x3a\x1b\x03\x19\x00\x00" 73 | 74 | func mgoGoTplBytes() ([]byte, error) { 75 | return bindataRead( 76 | _mgoGoTpl, 77 | "mgo.go.tpl", 78 | ) 79 | } 80 | 81 | func mgoGoTpl() (*asset, error) { 82 | bytes, err := mgoGoTplBytes() 83 | if err != nil { 84 | return nil, err 85 | } 86 | 87 | info := bindataFileInfo{name: "mgo.go.tpl", size: 6403, mode: os.FileMode(420), modTime: time.Unix(1555396366, 0)} 88 | a := &asset{bytes: bytes, info: info} 89 | return a, nil 90 | } 91 | 92 | var _mockGoTpl = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x55\xc1\x6e\xe2\x3a\x14\x5d\xc7\x5f\x71\x5e\x90\x50\x42\xf3\x48\xbb\xe5\x15\x36\x05\xf5\x21\x55\x6d\xa5\xb6\x7a\x8b\xaa\x6a\xf3\xc8\x0d\x78\x1a\x9c\xc8\x31\x30\x23\xe0\xdf\x47\x76\x42\x0b\xc4\xa5\x1d\x36\x33\xab\xd8\x37\xd7\xe7\x1e\x9f\x63\x5f\x87\x21\x2e\xb2\x98\x30\x26\x41\x32\x52\x14\xff\x83\x7e\x06\x91\x29\x48\xda\xc4\xa0\x26\x84\x6c\x4e\x72\x21\xb9\x22\x44\x89\x22\x09\x8a\xb9\xe2\x62\xdc\x66\x2c\x8f\x46\xaf\xd1\x98\x70\x8e\xf6\x6d\x35\xec\x31\xc6\xa7\x79\x26\x15\x3c\xe6\xb8\x24\x65\x26\x0b\x97\xf9\x8c\x85\xa1\x4e\x7b\xc8\x73\x92\xff\xce\xa6\x39\x7a\xff\x71\x35\x19\xf6\xc1\x8b\xbd\x38\x16\x5c\x4d\x30\xec\x33\xf5\x23\x27\xfb\x9a\x42\xc9\xd9\x48\x61\xc9\x1c\x0d\x20\x14\x5e\xbe\x15\x99\xe8\xb8\xe7\x68\x5f\x65\x0b\x92\x77\x22\x7a\x25\xf4\x9e\x79\x1c\x14\x4a\x72\x31\x76\x5f\x98\x73\x8e\xf6\x8d\xe4\x63\x2e\xa2\x14\x3d\xb6\xb6\x30\xba\x23\x39\xe7\x23\xd2\x94\x8a\x6a\x98\x25\x46\x82\xdd\x3c\xbd\xb2\x91\x47\x6a\xd2\x71\xc3\xbd\x92\xa1\xdb\xb0\xf1\xde\x20\xbf\x13\x8f\x23\x15\x15\x78\x7c\x6a\xd9\x36\x58\xb1\xbb\xa6\x85\x1d\xe6\x42\x92\x36\x27\x82\xa0\x85\xbd\x12\x4b\x66\x62\xf4\x21\x80\xe7\xc3\x6b\x59\xff\x04\x30\x96\xf9\x9a\xa2\x24\x35\x93\x02\x4d\x6b\xe2\x72\x1d\x40\xf0\xb4\x62\xfa\x46\xc8\x22\x94\xcc\x66\x8a\x3a\xee\xed\xcd\xdd\x3d\xb4\x3c\x86\x99\x57\xc0\x4e\xc0\xaf\xb0\xbc\x8d\xb0\xd5\xa1\x68\xed\xd8\xe7\xc3\x23\x29\xb7\xb8\xee\x66\x0f\xfb\xe8\x74\x91\x92\xf0\x8a\xb6\x11\xda\xc7\x09\xce\x4a\xd1\xf5\x9f\xa6\xf5\x54\x2d\x99\xe3\x0c\xfb\x1d\xd4\xb0\x02\xe6\xec\x9e\x9e\x8e\xa1\xb3\x95\x14\x30\x67\xcd\x9c\xaa\x18\xba\x88\xf2\x9c\x44\xbc\xa9\x1e\x40\x7f\xfc\x37\x45\xdf\x75\x7b\xc8\xe3\xcd\x2d\x3b\xa0\xdc\xc3\x3d\xc2\x65\xfd\x6c\xaf\xbf\x20\x66\x59\xc0\xab\x6d\x49\x5f\x9a\xb0\x85\x86\x88\xa6\x64\xbb\x37\x6e\x03\xad\x30\xc0\xaf\x99\xc0\x13\x9c\xa2\xd7\xad\x0b\xb8\x5a\xd5\x63\xbd\x5d\x7f\x56\x2b\x54\xe3\xc7\xfd\xd4\xbf\xcf\x9e\xd0\xed\x6a\xd5\x8c\x45\x95\x88\x65\x6b\x69\x5f\xd3\xc2\x73\x79\x8c\x38\xa3\xc2\x34\x2f\xfa\xce\x0b\xe5\xfa\xc6\x90\xb9\x36\xfb\x00\x2c\x73\xe6\xed\x9d\x2d\xa1\xbb\xef\xac\xc5\xb4\x3e\xa5\xf4\xa9\x69\xfd\xc1\xd5\xe0\x7e\x70\xb4\x6f\x65\x8d\x23\x7d\xfb\xf3\x8d\x39\x88\x68\xa4\xae\xcb\x7e\x49\xea\x13\xcd\x2f\x07\xc7\x5f\x94\x4b\x52\xc7\xab\x6d\xb9\x26\xb5\xf6\x62\x3a\xeb\xef\xf2\x44\xf0\x34\xf8\x92\x31\x55\xfe\x01\xf4\xed\xb6\x7f\xc5\x0b\x75\xe0\x89\xdc\xb6\xe5\x0b\x16\x68\x34\x2f\x4b\x92\x82\x54\x80\x94\x4f\xb9\xd2\x06\xd4\xe4\xfd\xf0\xd5\xdc\x57\x38\x4b\x12\x7d\xff\x4f\x99\x93\xf2\x69\x35\x4a\x32\x89\xe7\x00\xa6\x31\xc8\x48\x8c\x69\xb3\x55\xa3\x16\x4f\x30\xc7\x5f\xef\xea\xe9\x40\x49\x48\x47\x4f\xd1\x6c\x42\xcf\xf5\xa4\x0a\x9b\x2c\x5d\xe9\xe4\xc4\x8c\x46\x99\x50\x5c\xcc\x48\x4f\xd6\x15\x42\xb9\x95\xae\x06\x58\xad\xf4\x4c\xaf\x37\xc1\x72\x75\xca\xa7\xd5\xea\xfd\x8d\xbe\x3d\x24\x7b\x3f\x02\xcc\x7d\xb3\xa0\x44\xd7\xd8\x65\x91\x12\xd0\xf9\x5f\x52\xf4\x6a\x86\x6b\x2b\xab\xf5\xb6\xd7\x35\xec\xd2\xdf\x9f\x01\x00\x00\xff\xff\x49\x66\x66\x21\x1f\x0a\x00\x00" 93 | 94 | func mockGoTplBytes() ([]byte, error) { 95 | return bindataRead( 96 | _mockGoTpl, 97 | "mock.go.tpl", 98 | ) 99 | } 100 | 101 | func mockGoTpl() (*asset, error) { 102 | bytes, err := mockGoTplBytes() 103 | if err != nil { 104 | return nil, err 105 | } 106 | 107 | info := bindataFileInfo{name: "mock.go.tpl", size: 2591, mode: os.FileMode(420), modTime: time.Unix(1557020544, 0)} 108 | a := &asset{bytes: bytes, info: info} 109 | return a, nil 110 | } 111 | 112 | // Asset loads and returns the asset for the given name. 113 | // It returns an error if the asset could not be found or 114 | // could not be loaded. 115 | func Asset(name string) ([]byte, error) { 116 | cannonicalName := strings.Replace(name, "\\", "/", -1) 117 | if f, ok := _bindata[cannonicalName]; ok { 118 | a, err := f() 119 | if err != nil { 120 | return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) 121 | } 122 | return a.bytes, nil 123 | } 124 | return nil, fmt.Errorf("Asset %s not found", name) 125 | } 126 | 127 | // MustAsset is like Asset but panics when Asset would return an error. 128 | // It simplifies safe initialization of global variables. 129 | func MustAsset(name string) []byte { 130 | a, err := Asset(name) 131 | if err != nil { 132 | panic("asset: Asset(" + name + "): " + err.Error()) 133 | } 134 | 135 | return a 136 | } 137 | 138 | // AssetInfo loads and returns the asset info for the given name. 139 | // It returns an error if the asset could not be found or 140 | // could not be loaded. 141 | func AssetInfo(name string) (os.FileInfo, error) { 142 | cannonicalName := strings.Replace(name, "\\", "/", -1) 143 | if f, ok := _bindata[cannonicalName]; ok { 144 | a, err := f() 145 | if err != nil { 146 | return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) 147 | } 148 | return a.info, nil 149 | } 150 | return nil, fmt.Errorf("AssetInfo %s not found", name) 151 | } 152 | 153 | // AssetNames returns the names of the assets. 154 | func AssetNames() []string { 155 | names := make([]string, 0, len(_bindata)) 156 | for name := range _bindata { 157 | names = append(names, name) 158 | } 159 | return names 160 | } 161 | 162 | // _bindata is a table, holding each asset generator, mapped to its name. 163 | var _bindata = map[string]func() (*asset, error){ 164 | "mgo.go.tpl": mgoGoTpl, 165 | "mock.go.tpl": mockGoTpl, 166 | } 167 | 168 | // AssetDir returns the file names below a certain 169 | // directory embedded in the file by go-bindata. 170 | // For example if you run go-bindata on data/... and data contains the 171 | // following hierarchy: 172 | // data/ 173 | // foo.txt 174 | // img/ 175 | // a.png 176 | // b.png 177 | // then AssetDir("data") would return []string{"foo.txt", "img"} 178 | // AssetDir("data/img") would return []string{"a.png", "b.png"} 179 | // AssetDir("foo.txt") and AssetDir("notexist") would return an error 180 | // AssetDir("") will return []string{"data"}. 181 | func AssetDir(name string) ([]string, error) { 182 | node := _bintree 183 | if len(name) != 0 { 184 | cannonicalName := strings.Replace(name, "\\", "/", -1) 185 | pathList := strings.Split(cannonicalName, "/") 186 | for _, p := range pathList { 187 | node = node.Children[p] 188 | if node == nil { 189 | return nil, fmt.Errorf("Asset %s not found", name) 190 | } 191 | } 192 | } 193 | if node.Func != nil { 194 | return nil, fmt.Errorf("Asset %s not found", name) 195 | } 196 | rv := make([]string, 0, len(node.Children)) 197 | for childName := range node.Children { 198 | rv = append(rv, childName) 199 | } 200 | return rv, nil 201 | } 202 | 203 | type bintree struct { 204 | Func func() (*asset, error) 205 | Children map[string]*bintree 206 | } 207 | var _bintree = &bintree{nil, map[string]*bintree{ 208 | "mgo.go.tpl": &bintree{mgoGoTpl, map[string]*bintree{}}, 209 | "mock.go.tpl": &bintree{mockGoTpl, map[string]*bintree{}}, 210 | }} 211 | 212 | // RestoreAsset restores an asset under the given directory 213 | func RestoreAsset(dir, name string) error { 214 | data, err := Asset(name) 215 | if err != nil { 216 | return err 217 | } 218 | info, err := AssetInfo(name) 219 | if err != nil { 220 | return err 221 | } 222 | err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) 223 | if err != nil { 224 | return err 225 | } 226 | err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) 227 | if err != nil { 228 | return err 229 | } 230 | err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) 231 | if err != nil { 232 | return err 233 | } 234 | return nil 235 | } 236 | 237 | // RestoreAssets restores an asset under the given directory recursively 238 | func RestoreAssets(dir, name string) error { 239 | children, err := AssetDir(name) 240 | // File 241 | if err != nil { 242 | return RestoreAsset(dir, name) 243 | } 244 | // Dir 245 | for _, child := range children { 246 | err = RestoreAssets(dir, filepath.Join(name, child)) 247 | if err != nil { 248 | return err 249 | } 250 | } 251 | return nil 252 | } 253 | 254 | func _filePath(dir, name string) string { 255 | cannonicalName := strings.Replace(name, "\\", "/", -1) 256 | return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) 257 | } 258 | 259 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wzshiming/gen 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/spf13/cobra v1.0.0 7 | github.com/wzshiming/gotype v0.7.0 8 | github.com/wzshiming/namecase v0.2.0 9 | github.com/wzshiming/openapi v0.0.0-20200703170606-f8ff864b47e1 10 | golang.org/x/tools v0.0.0-20200702044944-0cc1aa72b347 11 | ) 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 4 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 5 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 6 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 7 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 8 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 9 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 10 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 11 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 12 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 13 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 14 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 15 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 16 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 17 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 18 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 19 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 20 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 21 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 22 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 23 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 24 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 25 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 26 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 27 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 28 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 29 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 30 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 31 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 32 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 33 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 34 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 35 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 36 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 37 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 38 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 39 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 40 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 41 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 42 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 43 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 44 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 45 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 46 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 47 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 48 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 49 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 50 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 51 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 52 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 53 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 54 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 55 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 56 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 57 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 58 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 59 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 60 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 61 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 62 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 63 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 64 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 65 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 66 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 67 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 68 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 69 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 70 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 71 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 72 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 73 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 74 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 75 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 76 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 77 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 78 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 79 | github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= 80 | github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= 81 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 82 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 83 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 84 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= 85 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 86 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 87 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 88 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 89 | github.com/wzshiming/gotype v0.7.0 h1:TgKgWwUy34fxU8HtrnAPO/P3p2z8eOI5px6l5IQ2Aec= 90 | github.com/wzshiming/gotype v0.7.0/go.mod h1:7YULvVsDObRVX2U6iUcF5hixYvHFavLsTSIW26c5GLM= 91 | github.com/wzshiming/namecase v0.2.0 h1:zRWDTVN8Yfzz9YfpOuM/TpYza/HRO9SrWAR6l9mMpKQ= 92 | github.com/wzshiming/namecase v0.2.0/go.mod h1:FJT9Q8a289dtinO3AJUDKqRP9UQzq9UxpWDevdDVvYA= 93 | github.com/wzshiming/openapi v0.0.0-20200703170606-f8ff864b47e1 h1:9hBPvewWhToCiZunvR7NFXjsiFjL/dXDuGRdH5yRUu0= 94 | github.com/wzshiming/openapi v0.0.0-20200703170606-f8ff864b47e1/go.mod h1:398xiAftMV/w8frjipnUzjr/WQ+E2fnGRv9yXobxyyk= 95 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 96 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 97 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 98 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 99 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 100 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 101 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 102 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 103 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 104 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 105 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 106 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 107 | golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= 108 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 109 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 110 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 111 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 112 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 113 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 114 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 115 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 116 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 117 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 118 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 119 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 120 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 121 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 122 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 123 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 124 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 125 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 126 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 127 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 128 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 129 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 130 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 131 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 132 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 133 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 134 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 135 | golang.org/x/tools v0.0.0-20200702044944-0cc1aa72b347 h1:/e4fNMHdLn7SQSxTrRZTma2xjQW6ELdxcnpqMhpo9X4= 136 | golang.org/x/tools v0.0.0-20200702044944-0cc1aa72b347/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 137 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 138 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 139 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 140 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 141 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 142 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 143 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 144 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 145 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 146 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 147 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 148 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 149 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 150 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 151 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 152 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 153 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 154 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 155 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 156 | -------------------------------------------------------------------------------- /model/convert_from.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/wzshiming/gen/spec" 5 | ) 6 | 7 | func (g *GenModel) convertFromBool(in, out string, typ *spec.Type) error { 8 | g.buf.AddImport("", "strconv") 9 | g.buf.WriteFormat(` 10 | %s = `, out) 11 | g.buf.WriteFormat(`strconv.FormatBool(bool(%s)) 12 | `, in) 13 | return nil 14 | } 15 | 16 | func (g *GenModel) convertFromString(in, out string, typ *spec.Type) error { 17 | g.buf.WriteFormat(`%s = `, out) 18 | g.buf.WriteFormat(`string(%s) 19 | `, in) 20 | return nil 21 | } 22 | 23 | func (g *GenModel) convertFromFloat64(in, out string, typ *spec.Type) error { 24 | g.buf.AddImport("", "strconv") 25 | g.buf.WriteFormat(` 26 | %s = `, out) 27 | g.buf.WriteFormat(`strconv.FormatFloat(float64(%s), 'e', -1, 64) 28 | `, in) 29 | return nil 30 | } 31 | 32 | func (g *GenModel) convertFromInt64(in, out string, typ *spec.Type) error { 33 | g.buf.AddImport("", "strconv") 34 | g.buf.WriteFormat(` 35 | %s = `, out) 36 | g.buf.WriteFormat(`strconv.FormatInt(int64(%s), 10) 37 | `, in) 38 | 39 | return nil 40 | } 41 | 42 | func (g *GenModel) convertFromUint64(in, out string, typ *spec.Type) error { 43 | g.buf.AddImport("", "strconv") 44 | g.buf.WriteFormat(` 45 | %s = `, out) 46 | g.buf.WriteFormat(`strconv.FormatUint(uint64(%s), 10) 47 | `, in) 48 | return nil 49 | } 50 | 51 | func (g *GenModel) convertFromBytes(in, out string, typ *spec.Type) error { 52 | g.buf.WriteFormat(`%s = `, out) 53 | g.buf.WriteFormat(`%s 54 | `, in) 55 | return nil 56 | } 57 | 58 | func (g *GenModel) convertFromSlice(in, out string, typ *spec.Type) error { 59 | g.buf.AddImport("", "strings") 60 | 61 | g.buf.WriteFormat(`_list_%s := make([]string`, out) 62 | g.buf.WriteFormat(`, 0, len(%s)) 63 | `, in) 64 | 65 | g.buf.WriteFormat(` 66 | for _, _%s := range %s { 67 | var _%s string 68 | `, in, in, out) 69 | 70 | err := g.convertFrom("_"+in, "_"+out, typ) 71 | if err != nil { 72 | return err 73 | } 74 | g.buf.WriteFormat(` 75 | _list_%s = append(_list_%s, _%s) 76 | } 77 | 78 | %s = strings.Join(_list_%s, ",") 79 | `, out, out, out, out, out) 80 | 81 | return nil 82 | } 83 | 84 | func (g *GenModel) convertFrom(in, out string, typ *spec.Type) error { 85 | if typ.Ref != "" { 86 | typ = g.api.Types[typ.Ref] 87 | } 88 | 89 | if typ.Attr.Has(spec.AttrTextMarshaler) { 90 | g.buf.AddImport("", "unsafe") 91 | g.buf.AddImport("", "net/http") 92 | g.buf.WriteFormat(` 93 | var _%s []byte 94 | _%s, err = %s.MarshalText() 95 | if err != nil { 96 | return 97 | } 98 | %s = *(*string)(unsafe.Pointer(&_%s)) 99 | 100 | `, out, out, in, out, out) 101 | return nil 102 | } 103 | 104 | if typ.Name == "" && typ.Kind == spec.Ptr { 105 | out0 := out 106 | out = "_" + out 107 | typ = typ.Elem 108 | g.buf.WriteFormat(` 109 | var %s `, out) 110 | g.Types(typ) 111 | defer func() { 112 | g.buf.WriteFormat(` 113 | %s = &%s 114 | `, out0, out) 115 | }() 116 | } 117 | 118 | switch typ.Kind { 119 | case spec.Bool: 120 | return g.convertFromBool(in, out, typ) 121 | case spec.String: 122 | return g.convertFromString(in, out, typ) 123 | case spec.Int8, spec.Int16, spec.Int32, spec.Int64, spec.Int: 124 | return g.convertFromInt64(in, out, typ) 125 | case spec.Uint8, spec.Uint16, spec.Uint32, spec.Uint64, spec.Uint: 126 | return g.convertFromUint64(in, out, typ) 127 | case spec.Float32, spec.Float64: 128 | return g.convertFromFloat64(in, out, typ) 129 | case spec.Slice: 130 | switch typ.Elem.Kind { 131 | case spec.Byte, spec.Rune: 132 | return g.convertFromBytes(in, out, typ) 133 | } 134 | return g.convertFromSlice(in, out, typ.Elem) 135 | case spec.Array: 136 | return g.convertFromSlice(in, out, typ.Elem) 137 | } 138 | 139 | g.buf.WriteFormat("// Conversion of string from ") 140 | g.Types(typ) 141 | g.buf.WriteString(" is not supported.") 142 | 143 | return nil 144 | } 145 | 146 | func (g *GenModel) ConvertFrom(in, out string, typ *spec.Type) error { 147 | return g.convertFrom(in, out, typ) 148 | } 149 | 150 | func (g *GenModel) convertFromMulti(in, out string, typ *spec.Type, explode bool) error { 151 | if typ.Ref != "" { 152 | typ = g.api.Types[typ.Ref] 153 | } 154 | g.buf.WriteFormat(` 155 | if len(%s) == 0 { 156 | return 157 | } 158 | `, in) 159 | 160 | if !explode || (typ.Kind != spec.Slice && typ.Kind != spec.Array) { 161 | return g.convertFrom(in, out, typ) 162 | } 163 | 164 | g.buf.WriteFormat(`%s = make([]string, 0, len(%s)) 165 | `, out, in) 166 | 167 | g.buf.WriteFormat(` 168 | for _, _%s := range %s { 169 | var _%s string 170 | `, in, in, out) 171 | 172 | err := g.convertFrom("_"+in, "_"+out, typ.Elem) 173 | if err != nil { 174 | return err 175 | } 176 | g.buf.WriteFormat(` 177 | if err != nil { 178 | break 179 | } 180 | %s = append(%s, _%s) 181 | } 182 | `, out, out, out) 183 | 184 | return nil 185 | } 186 | 187 | func (g *GenModel) ConvertFromMulti(in, out string, typ *spec.Type, explode bool) error { 188 | return g.convertFromMulti(in, out, typ, explode) 189 | } 190 | -------------------------------------------------------------------------------- /model/convert_to.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/wzshiming/gen/spec" 5 | ) 6 | 7 | func (g *GenModel) convertToBool(in, out string, typ *spec.Type) error { 8 | g.buf.AddImport("", "strconv") 9 | g.buf.WriteFormat(` 10 | if _%s, err := strconv.ParseBool(%s); err == nil { 11 | %s = `, out, in, out) 12 | g.Types(typ) 13 | g.buf.WriteFormat(`(_%s) 14 | } 15 | `, out) 16 | return nil 17 | } 18 | 19 | func (g *GenModel) convertToString(in, out string, typ *spec.Type) error { 20 | g.buf.WriteFormat(`%s = `, out) 21 | g.Types(typ) 22 | g.buf.WriteFormat(`(%s)`, in) 23 | return nil 24 | } 25 | 26 | func (g *GenModel) convertToFloat64(in, out string, typ *spec.Type) error { 27 | g.buf.AddImport("", "strconv") 28 | g.buf.WriteFormat(` 29 | if _%s, err := strconv.ParseFloat(%s, 0); err == nil { 30 | %s = `, out, in, out) 31 | g.Types(typ) 32 | g.buf.WriteFormat(`(_%s) 33 | } 34 | `, out) 35 | return nil 36 | } 37 | 38 | func (g *GenModel) convertToInt64(in, out string, typ *spec.Type) error { 39 | g.buf.AddImport("", "strconv") 40 | g.buf.WriteFormat(` 41 | if _%s, err := strconv.ParseInt(%s, 0, 0); err == nil { 42 | %s = `, out, in, out) 43 | g.Types(typ) 44 | g.buf.WriteFormat(`(_%s) 45 | } 46 | `, out) 47 | return nil 48 | } 49 | 50 | func (g *GenModel) convertToUint64(in, out string, typ *spec.Type) error { 51 | g.buf.AddImport("", "strconv") 52 | g.buf.WriteFormat(` 53 | if _%s, err := strconv.ParseUint(%s, 0, 0); err == nil { 54 | %s = `, out, in, out) 55 | g.Types(typ) 56 | g.buf.WriteFormat(`(_%s) 57 | } 58 | `, out) 59 | return nil 60 | } 61 | 62 | func (g *GenModel) convertToBytes(in, out string, typ *spec.Type) error { 63 | g.buf.WriteFormat(`%s := `, out) 64 | g.Types(typ) 65 | g.buf.WriteFormat(`(%s) 66 | `, in) 67 | return nil 68 | } 69 | 70 | func (g *GenModel) convertToSlice(in, out string, typ *spec.Type) error { 71 | g.buf.AddImport("", "strconv") 72 | g.buf.AddImport("", "strings") 73 | g.buf.WriteFormat(` 74 | _list_%s := strings.Split(%s, ",") 75 | `, in, in) 76 | g.buf.WriteFormat(`%s = make([]`, out) 77 | g.Types(typ) 78 | g.buf.WriteFormat(`, 0, len(_list_%s)) 79 | `, in) 80 | 81 | g.buf.WriteFormat(` 82 | for _, _%s := range _list_%s { 83 | var _%s `, in, in, out) 84 | g.Types(typ) 85 | g.buf.WriteFormat(` 86 | `) 87 | err := g.convertTo("_"+in, "_"+out, typ) 88 | if err != nil { 89 | return err 90 | } 91 | g.buf.WriteFormat(` 92 | if err != nil { 93 | break 94 | } 95 | %s = append(%s, _%s) 96 | } 97 | `, out, out, out) 98 | 99 | return nil 100 | } 101 | 102 | func (g *GenModel) convertTo(in, out string, typ *spec.Type) error { 103 | if typ.Ref != "" { 104 | typ = g.api.Types[typ.Ref] 105 | } 106 | 107 | if typ.Attr.Has(spec.AttrTextUnmarshaler) { 108 | g.buf.AddImport("", "unsafe") 109 | g.buf.AddImport("", "net/http") 110 | g.buf.WriteFormat(` 111 | if %s != "" { 112 | err = %s.UnmarshalText(*(*[]byte)(unsafe.Pointer(&%s))) 113 | } 114 | `, in, out, in) 115 | return nil 116 | } 117 | 118 | if typ.Name == "" && typ.Kind == spec.Ptr { 119 | out0 := out 120 | out = "_" + out 121 | typ = typ.Elem 122 | g.buf.WriteFormat(` 123 | var %s `, out) 124 | g.Types(typ) 125 | defer func() { 126 | g.buf.WriteFormat(` 127 | %s = &%s 128 | `, out0, out) 129 | }() 130 | } 131 | 132 | switch typ.Kind { 133 | case spec.Bool: 134 | return g.convertToBool(in, out, typ) 135 | case spec.String: 136 | return g.convertToString(in, out, typ) 137 | case spec.Int8, spec.Int16, spec.Int32, spec.Int64, spec.Int: 138 | return g.convertToInt64(in, out, typ) 139 | case spec.Uint8, spec.Uint16, spec.Uint32, spec.Uint64, spec.Uint: 140 | return g.convertToUint64(in, out, typ) 141 | case spec.Float32, spec.Float64: 142 | return g.convertToFloat64(in, out, typ) 143 | case spec.Slice: 144 | switch typ.Elem.Kind { 145 | case spec.Byte, spec.Rune: 146 | return g.convertToBytes(in, out, typ) 147 | } 148 | return g.convertToSlice(in, out, typ.Elem) 149 | case spec.Array: 150 | return g.convertToSlice(in, out, typ.Elem) 151 | } 152 | 153 | g.buf.WriteFormat("// Conversion of string to ") 154 | g.Types(typ) 155 | g.buf.WriteString(" is not supported.") 156 | 157 | return nil 158 | } 159 | 160 | func (g *GenModel) ConvertTo(in, out string, typ *spec.Type) error { 161 | return g.convertTo(in, out, typ) 162 | } 163 | 164 | func (g *GenModel) convertToMulti(in, out string, typ *spec.Type, explode bool) error { 165 | if typ.Ref != "" { 166 | typ = g.api.Types[typ.Ref] 167 | } 168 | g.buf.WriteFormat(` 169 | if len(%s) == 0 { 170 | return 171 | } 172 | `, in) 173 | 174 | if !explode || (typ.Kind != spec.Slice && typ.Kind != spec.Array) { 175 | g.buf.WriteFormat(` 176 | _%s_0 := %s[0] 177 | `, in, in) 178 | return g.convertTo("_"+in+"_0", out, typ) 179 | } 180 | 181 | g.buf.WriteFormat(`%s = make(`, out) 182 | g.Types(typ) 183 | g.buf.WriteFormat(`, 0, len(%s)) 184 | `, in) 185 | 186 | g.buf.WriteFormat(` 187 | for _, _%s := range %s { 188 | var _%s `, in, in, out) 189 | g.Types(typ.Elem) 190 | g.buf.WriteFormat(` 191 | `) 192 | err := g.convertTo("_"+in, "_"+out, typ.Elem) 193 | if err != nil { 194 | return err 195 | } 196 | g.buf.WriteFormat(` 197 | if err != nil { 198 | break 199 | } 200 | %s = append(%s, _%s) 201 | } 202 | `, out, out, out) 203 | 204 | return nil 205 | } 206 | 207 | func (g *GenModel) ConvertToMulti(in, out string, typ *spec.Type, explode bool) error { 208 | return g.convertToMulti(in, out, typ, explode) 209 | } 210 | -------------------------------------------------------------------------------- /model/model.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "path" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/wzshiming/gen/spec" 9 | "github.com/wzshiming/gen/srcgen" 10 | "github.com/wzshiming/gen/utils" 11 | "github.com/wzshiming/namecase" 12 | ) 13 | 14 | // GenModel is the generating generating 15 | type GenModel struct { 16 | ps map[string]bool 17 | api *spec.API 18 | buf *srcgen.File 19 | } 20 | 21 | func NewGenModel(api *spec.API, buf *srcgen.File, pkgpath []string) *GenModel { 22 | ps := map[string]bool{} 23 | for _, p := range pkgpath { 24 | ps[p] = true 25 | } 26 | return &GenModel{ 27 | api: api, 28 | buf: buf, 29 | ps: ps, 30 | } 31 | } 32 | 33 | func (g *GenModel) TypesZero(typ *spec.Type) (err error) { 34 | typ0 := typ 35 | if typ.Ref != "" { 36 | typ = g.api.Types[typ.Ref] 37 | } 38 | switch typ.Kind { 39 | case spec.Ptr, spec.Slice, spec.Map, spec.Error, spec.Chan, spec.Interface: 40 | g.buf.WriteString("nil") 41 | case spec.Array: 42 | g.buf.WriteString("(") 43 | err = g.Types(typ0) 44 | if err != nil { 45 | return err 46 | } 47 | g.buf.WriteString("{})") 48 | case spec.String: 49 | g.buf.WriteString("\"\"") 50 | case spec.Struct: 51 | g.buf.WriteString("(") 52 | err = g.Types(typ0) 53 | if err != nil { 54 | return err 55 | } 56 | g.buf.WriteString("{})") 57 | case spec.Int, spec.Int8, spec.Int16, spec.Int32, spec.Int64, 58 | spec.Uint, spec.Uint8, spec.Uint16, spec.Uint32, spec.Uint64, 59 | spec.Float32, spec.Float64, spec.Complex64, spec.Complex128, 60 | spec.Byte, spec.Rune: 61 | g.buf.WriteString("0") 62 | case spec.Bool: 63 | g.buf.WriteString("false") 64 | default: 65 | g.buf.WriteString(strings.ToLower(typ.Kind.String())) 66 | } 67 | return nil 68 | } 69 | 70 | func (g *GenModel) GetPkgPath(path string) (string, bool) { 71 | const ( 72 | vendor = "/vendor/" 73 | ) 74 | 75 | if i := strings.LastIndex(path, vendor); i != -1 { 76 | path = path[i+len(vendor):] 77 | } 78 | 79 | if len(g.ps) == 0 { 80 | return path, false 81 | } 82 | 83 | if g.ps[path] { 84 | return path, false 85 | } 86 | 87 | return path, true 88 | } 89 | 90 | func (g *GenModel) PkgPath(path string) bool { 91 | path, ok := g.GetPkgPath(path) 92 | if !ok { 93 | return false 94 | } 95 | 96 | pkgname := namecase.ToCamel(path) 97 | g.buf.AddImport(pkgname, path) 98 | g.buf.WriteFormat("%s.", pkgname) 99 | return true 100 | } 101 | 102 | func (g *GenModel) Paths(typ *spec.Type) bool { 103 | if typ.Ref != "" { 104 | typ = g.api.Types[typ.Ref] 105 | } 106 | return g.PkgPath(typ.PkgPath) 107 | } 108 | 109 | func (g *GenModel) Ptr(typ *spec.Type) bool { 110 | if typ.Ref != "" { 111 | typ = g.api.Types[typ.Ref] 112 | } 113 | return typ.Kind != spec.Interface 114 | } 115 | 116 | func (g *GenModel) PtrTypes(typ *spec.Type) (err error) { 117 | if g.Ptr(typ) { 118 | g.buf.WriteString("*") 119 | } 120 | return g.Types(typ) 121 | } 122 | 123 | func (g *GenModel) Types(typ *spec.Type) (err error) { 124 | 125 | if g.typeRoot(typ) { 126 | return 127 | } 128 | 129 | if typ.Ref != "" { 130 | g.Paths(typ) 131 | g.buf.WriteString(getName(typ.Ref)) 132 | return nil 133 | } 134 | 135 | if typ.Name != "" && strings.ToLower(typ.Kind.String()) != typ.Name { 136 | g.Paths(typ) 137 | g.buf.WriteString(typ.Name) 138 | return nil 139 | } 140 | 141 | return g.TypesDefine(typ) 142 | } 143 | 144 | func (g *GenModel) typeRoot(typ *spec.Type) bool { 145 | if typ.Ref != "" { 146 | typ = g.api.Types[typ.Ref] 147 | } 148 | 149 | if typ.Attr.Has(spec.AttrRoot) { 150 | g.buf.AddImport("", typ.PkgPath) 151 | _, pkgname := path.Split(typ.PkgPath) 152 | g.buf.WriteFormat("%s.%s", pkgname, typ.Name) 153 | return true 154 | } 155 | return false 156 | } 157 | 158 | func (g *GenModel) TypesDefine(typ *spec.Type) (err error) { 159 | if typ.Ref != "" { 160 | typ = g.api.Types[typ.Ref] 161 | } 162 | 163 | if g.typeRoot(typ) { 164 | return 165 | } 166 | 167 | switch typ.Kind { 168 | case spec.Ptr: 169 | g.buf.WriteByte('*') 170 | err := g.Types(typ.Elem) 171 | if err != nil { 172 | return err 173 | } 174 | case spec.Slice: 175 | g.buf.WriteString("[]") 176 | err := g.Types(typ.Elem) 177 | if err != nil { 178 | return err 179 | } 180 | case spec.Array: 181 | g.buf.WriteByte('[') 182 | g.buf.WriteString(strconv.Itoa(typ.Len)) 183 | g.buf.WriteByte(']') 184 | err := g.Types(typ.Elem) 185 | if err != nil { 186 | return err 187 | } 188 | case spec.Map: 189 | g.buf.WriteString("map[") 190 | err := g.Types(typ.Key) 191 | if err != nil { 192 | return err 193 | } 194 | g.buf.WriteByte(']') 195 | err = g.Types(typ.Elem) 196 | if err != nil { 197 | return err 198 | } 199 | case spec.Struct: 200 | g.buf.WriteString("struct {") 201 | if len(typ.Fields) != 0 { 202 | g.buf.WriteByte('\n') 203 | } 204 | for _, v := range typ.Fields { 205 | if v.Type.Kind == spec.Func { 206 | continue 207 | } 208 | if !v.Anonymous { 209 | g.buf.WriteString(v.Name) 210 | g.buf.WriteByte(' ') 211 | } 212 | err := g.Types(v.Type) 213 | if err != nil { 214 | return err 215 | } 216 | if v.Tag != "" { 217 | g.buf.WriteByte(' ') 218 | g.buf.WriteByte('`') 219 | g.buf.WriteString(string(v.Tag)) 220 | g.buf.WriteByte('`') 221 | } 222 | g.buf.WriteString("// ") 223 | g.buf.WriteString(utils.MergeLine(v.Description)) 224 | g.buf.WriteByte('\n') 225 | } 226 | g.buf.WriteByte('}') 227 | case spec.Interface: 228 | g.buf.WriteString("interface{}") 229 | default: 230 | g.buf.WriteString(strings.ToLower(typ.Kind.String())) 231 | } 232 | return 233 | } 234 | 235 | func getName(name string) string { 236 | i := strings.Index(name, ".") 237 | if i == -1 { 238 | return name 239 | } 240 | return name[:i] 241 | } 242 | -------------------------------------------------------------------------------- /named/named.go: -------------------------------------------------------------------------------- 1 | package named 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Named struct { 8 | set map[string]map[string]string 9 | seg string 10 | sub map[string]*Named 11 | } 12 | 13 | func NewNamed(seg string) *Named { 14 | return &Named{ 15 | set: map[string]map[string]string{}, 16 | seg: seg, 17 | sub: map[string]*Named{}, 18 | } 19 | } 20 | 21 | func (n *Named) GetName(name string, addr string) string { 22 | d, ok := n.set[name] 23 | if !ok { 24 | d = map[string]string{} 25 | n.set[name] = d 26 | } 27 | 28 | if name, ok := d[addr]; ok { 29 | return name 30 | } 31 | 32 | if len(d) != 0 { 33 | name = fmt.Sprintf("%s%s%d", name, n.seg, len(d)) 34 | } 35 | d[addr] = name 36 | return name 37 | } 38 | 39 | func (n *Named) GetSubNamed(addr string) *Named { 40 | name, ok := n.sub[addr] 41 | if ok { 42 | return name 43 | } 44 | name = NewNamed(n.seg) 45 | n.sub[addr] = name 46 | return name 47 | } 48 | -------------------------------------------------------------------------------- /openapi/openapi.go: -------------------------------------------------------------------------------- 1 | package openapi 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "sort" 7 | "strings" 8 | 9 | "github.com/wzshiming/gen/spec" 10 | "github.com/wzshiming/gen/utils" 11 | oaspec "github.com/wzshiming/openapi/spec" 12 | ) 13 | 14 | type GenOpenAPI struct { 15 | api *spec.API 16 | openapi *oaspec.OpenAPI 17 | servers []string 18 | explode bool 19 | } 20 | 21 | func NewGenOpenAPI(api *spec.API) *GenOpenAPI { 22 | return &GenOpenAPI{ 23 | api: api, 24 | openapi: &oaspec.OpenAPI{ 25 | OpenAPI: "3.0.1", 26 | Components: oaspec.NewComponents(), 27 | Paths: oaspec.Paths{}, 28 | }, 29 | } 30 | } 31 | 32 | func (g *GenOpenAPI) SetExplode(b bool) *GenOpenAPI { 33 | g.explode = b 34 | return g 35 | } 36 | 37 | func (g *GenOpenAPI) SetInfo(info *oaspec.Info) *GenOpenAPI { 38 | g.openapi.Info = info 39 | return g 40 | } 41 | 42 | func (g *GenOpenAPI) WithServices(servers ...string) *GenOpenAPI { 43 | if len(servers) != 0 { 44 | g.servers = servers 45 | } 46 | return g 47 | } 48 | 49 | func (g *GenOpenAPI) Generate() (*oaspec.OpenAPI, error) { 50 | err := g.generateComponents() 51 | if err != nil { 52 | return nil, err 53 | } 54 | servers, err := oaspec.NewServers(g.servers...) 55 | if err != nil { 56 | return nil, err 57 | } 58 | g.openapi.Servers = servers 59 | 60 | if g.openapi.Info == nil { 61 | g.openapi.Info = &oaspec.Info{ 62 | Title: "OpenAPI Demo", 63 | Description: "Automatically generated", 64 | Version: "0.0.1", 65 | Contact: &oaspec.Contact{ 66 | Name: "wzshiming", 67 | URL: "https://github.com/wzshiming/gen", 68 | }, 69 | } 70 | } 71 | 72 | return g.openapi, nil 73 | } 74 | 75 | func (g *GenOpenAPI) generateComponents() (err error) { 76 | 77 | secuKey := make([]string, 0, len(g.api.Securitys)) 78 | for k := range g.api.Securitys { 79 | secuKey = append(secuKey, k) 80 | } 81 | sort.Strings(secuKey) 82 | for _, k := range secuKey { 83 | secu := g.api.Securitys[k] 84 | err := g.generateSecurityScheme(secu) 85 | if err != nil { 86 | return err 87 | } 88 | } 89 | 90 | for k, v := range g.api.Requests { 91 | switch v.In { 92 | case "security": 93 | // No action 94 | case "body": 95 | body, err := g.generateRequestBody(v) 96 | if err != nil { 97 | return err 98 | } 99 | g.openapi.Components.RequestBodies[k] = body 100 | case "header", "cookie", "path", "query": 101 | par, err := g.generateParameters(v) 102 | if err != nil { 103 | return err 104 | } 105 | g.openapi.Components.Parameters[k] = par 106 | default: 107 | // No action 108 | } 109 | } 110 | 111 | for k, v := range g.api.Responses { 112 | switch v.In { 113 | case "cookie": 114 | // No action 115 | case "header": 116 | // No action 117 | case "body": 118 | _, resp, err := g.generateResponsesBody(v) 119 | if err != nil { 120 | return err 121 | } 122 | g.openapi.Components.Responses[k] = resp 123 | default: 124 | // No action 125 | } 126 | } 127 | 128 | tmpTags := map[*spec.Type]bool{} 129 | for _, v := range g.api.Operations { 130 | err := g.generateOperations(v) 131 | if err != nil { 132 | return err 133 | } 134 | if v.Type != nil && !tmpTags[v.Type] { 135 | err := g.generateTags(v) 136 | if err != nil { 137 | return err 138 | } 139 | tmpTags[v.Type] = true 140 | } 141 | } 142 | 143 | return nil 144 | } 145 | 146 | func (g *GenOpenAPI) generateTags(ope *spec.Operation) (err error) { 147 | typ := ope.Type 148 | typ = g.api.Types[typ.Ref] 149 | sch, err := g.generateSchemas(typ) 150 | if err != nil { 151 | return err 152 | } 153 | if sch.Ref != "" { 154 | sch = g.openapi.Components.Schemas[sch.Ref] 155 | } 156 | description := sch.Description 157 | for _, v := range ope.Tags { 158 | g.openapi.Tags = append(g.openapi.Tags, &oaspec.Tag{ 159 | Name: v, 160 | Description: description, 161 | }) 162 | } 163 | return nil 164 | } 165 | 166 | func (g *GenOpenAPI) generateRequests(oper *oaspec.Operation, reqs []*spec.Request) (err error) { 167 | 168 | for _, v := range reqs { 169 | req := v 170 | if v.Ref != "" { 171 | req = g.api.Requests[v.Ref] 172 | } 173 | switch req.In { 174 | case "middleware": 175 | for _, v := range g.api.Middlewares { 176 | if len(v.Responses) == 0 { 177 | continue 178 | } 179 | resp := v.Responses[0] 180 | if resp.Ref != "" { 181 | resp = g.api.Responses[resp.Ref] 182 | } 183 | 184 | if req.Name == resp.Name { 185 | err := g.generateRequests(oper, v.Requests) 186 | if err != nil { 187 | return err 188 | } 189 | } 190 | } 191 | case "security": 192 | secuKey := make([]string, 0, len(g.api.Securitys)) 193 | for k := range g.api.Securitys { 194 | secuKey = append(secuKey, k) 195 | } 196 | sort.Strings(secuKey) 197 | for _, k := range secuKey { 198 | v := g.api.Securitys[k] 199 | if len(v.Responses) == 0 { 200 | continue 201 | } 202 | resp := v.Responses[0] 203 | if resp.Ref != "" { 204 | resp = g.api.Responses[resp.Ref] 205 | } 206 | 207 | if req.Name == resp.Name { 208 | oper.Security = append(oper.Security, map[string]oaspec.SecurityRequirement{ 209 | v.Name: oaspec.SecurityRequirement{}, 210 | }) 211 | } 212 | } 213 | case "body": 214 | body, err := g.generateRequestBody(v) 215 | if err != nil { 216 | return err 217 | } 218 | oper.RequestBody = body 219 | case "header", "cookie", "path", "query": 220 | par, err := g.generateParameters(v) 221 | if err != nil { 222 | return err 223 | } 224 | oper.Parameters = append(oper.Parameters, par) 225 | default: 226 | // No action 227 | } 228 | } 229 | 230 | return nil 231 | } 232 | 233 | func (g *GenOpenAPI) generateResponses(oper *oaspec.Operation, resps []*spec.Response) (err error) { 234 | oper.Responses = map[string]*oaspec.Response{} 235 | headers := map[string]*oaspec.Header{} 236 | for _, resp := range resps { 237 | if resp.Ref != "" { 238 | resp = g.api.Responses[resp.Ref] 239 | } 240 | switch resp.In { 241 | case "cookie": 242 | // TODO: Process the returned cookie 243 | case "header": 244 | name, head, err := g.generateResponsesHeader(resp) 245 | if err != nil { 246 | return err 247 | } 248 | headers[name] = head 249 | case "body": 250 | code, resp, err := g.generateResponsesBody(resp) 251 | if err != nil { 252 | return err 253 | } 254 | 255 | if len(headers) != 0 { 256 | resp.Headers = headers 257 | } 258 | 259 | oper.Responses[code] = resp 260 | default: 261 | // No action 262 | } 263 | } 264 | 265 | return nil 266 | } 267 | 268 | func (g *GenOpenAPI) generateOperations(ope *spec.Operation) (err error) { 269 | oper := &oaspec.Operation{} 270 | 271 | err = g.generateRequests(oper, ope.Requests) 272 | if err != nil { 273 | return err 274 | } 275 | 276 | err = g.generateResponses(oper, ope.Responses) 277 | if err != nil { 278 | return err 279 | } 280 | 281 | oper.Description = ope.Description 282 | oper.Summary = ope.Summary 283 | oper.Deprecated = ope.Deprecated 284 | 285 | if g.openapi.Paths[ope.Path] == nil { 286 | g.openapi.Paths[ope.Path] = &oaspec.PathItem{} 287 | } 288 | item := g.openapi.Paths[ope.Path] 289 | 290 | for _, method := range strings.Split(ope.Method, ",") { 291 | switch strings.ToLower(method) { 292 | case "get": 293 | item.Get = oper 294 | case "put": 295 | item.Put = oper 296 | case "post": 297 | item.Post = oper 298 | case "delete": 299 | item.Delete = oper 300 | case "options": 301 | item.Options = oper 302 | case "head": 303 | item.Head = oper 304 | case "patch": 305 | item.Patch = oper 306 | case "trace": 307 | item.Trace = oper 308 | } 309 | } 310 | 311 | for _, v := range ope.Tags { 312 | oper.Tags = append(oper.Tags, v) 313 | } 314 | 315 | return nil 316 | } 317 | 318 | func (g *GenOpenAPI) generateResponsesHeader(res *spec.Response) (name string, head *oaspec.Header, err error) { 319 | sch, err := g.generateSchemas(res.Type) 320 | if err != nil { 321 | return "", nil, err 322 | } 323 | head = &oaspec.Header{} 324 | head.Schema = sch 325 | return res.Name, head, nil 326 | } 327 | 328 | func (g *GenOpenAPI) generateResponsesBody(res *spec.Response) (code string, resp *oaspec.Response, err error) { 329 | if res.Ref != "" { 330 | return g.api.Responses[res.Ref].Code, oaspec.RefResponse(res.Ref), nil 331 | } 332 | 333 | sch, err := g.generateSchemas(res.Type) 334 | if err != nil { 335 | return "", nil, err 336 | } 337 | 338 | resp = &oaspec.Response{} 339 | resp.Content, err = g.generateContents(res.Name, res.Content, sch) 340 | if err != nil { 341 | return "", nil, err 342 | } 343 | resp.Description = res.Description 344 | if resp.Description == "" { 345 | resp.Description = "Response code is " + res.Code 346 | } 347 | code = res.Code 348 | return 349 | } 350 | 351 | func (g *GenOpenAPI) generateParameters(req *spec.Request) (par *oaspec.Parameter, err error) { 352 | if req.Ref != "" { 353 | return oaspec.RefParameter(req.Ref), nil 354 | } 355 | sch, err := g.generateSchemas(req.Type) 356 | if err != nil { 357 | return nil, err 358 | } 359 | switch req.In { 360 | case "header": 361 | par = oaspec.HeaderParam(req.Name, sch) 362 | case "cookie": 363 | par = oaspec.CookieParam(req.Name, sch) 364 | case "path": 365 | par = oaspec.PathParam(req.Name, sch) 366 | case "query": 367 | par = oaspec.QueryParam(req.Name, sch) 368 | par.Explode = g.explode 369 | default: 370 | return nil, fmt.Errorf("Parameters undefined in:%s", req.In) 371 | } 372 | par.Description = req.Description 373 | 374 | typ := req.Type 375 | if typ.Ref != "" { 376 | typ = g.api.Types[typ.Ref] 377 | } 378 | 379 | for _, v := range typ.Enum { 380 | sch.Enum = append(sch.Enum, oaspec.Any(v.Value)) 381 | if v.Description != "" { 382 | par.Description += "\n - " + v.Value + ":" + v.Description 383 | } 384 | } 385 | par.Description, _ = utils.GetTag(par.Description) 386 | 387 | return 388 | } 389 | 390 | func (g *GenOpenAPI) generateRequestBody(req *spec.Request) (body *oaspec.RequestBody, err error) { 391 | if req.Ref != "" { 392 | return oaspec.RefRequestBody(req.Ref), nil 393 | } 394 | sch, err := g.generateSchemas(req.Type) 395 | if err != nil { 396 | return nil, err 397 | } 398 | 399 | body = &oaspec.RequestBody{} 400 | body.Content, err = g.generateContents(req.Name, req.Content, sch) 401 | if err != nil { 402 | return nil, err 403 | } 404 | body.Description = req.Description 405 | return 406 | } 407 | 408 | func (g *GenOpenAPI) generateContents(name string, content string, sch *oaspec.Schema) (medias map[string]*oaspec.MediaType, err error) { 409 | medias = map[string]*oaspec.MediaType{} 410 | 411 | switch content { 412 | case "json": 413 | medias[oaspec.MimeJSON] = &oaspec.MediaType{ 414 | Schema: sch, 415 | } 416 | case "xml": 417 | medias[oaspec.MimeXML] = &oaspec.MediaType{ 418 | Schema: sch, 419 | } 420 | case "octetstream", "file": 421 | prop := &oaspec.Schema{} 422 | prop.Type = "object" 423 | prop.Properties = map[string]*oaspec.Schema{ 424 | name: sch, 425 | } 426 | medias[oaspec.MimeFormData] = &oaspec.MediaType{ 427 | Schema: prop, 428 | } 429 | case "urlencoded": 430 | medias[oaspec.MimeURLEncoded] = &oaspec.MediaType{ 431 | Schema: sch, 432 | } 433 | case "formdata": 434 | medias[oaspec.MimeFormData] = &oaspec.MediaType{ 435 | Schema: sch, 436 | } 437 | case "textplain": 438 | medias[oaspec.MimeTextPlain] = &oaspec.MediaType{ 439 | Schema: sch, 440 | } 441 | 442 | case "error": 443 | medias[oaspec.MimeTextPlain] = &oaspec.MediaType{ 444 | Schema: sch, 445 | } 446 | 447 | for _, wrap := range g.api.Wrappings { 448 | if len(wrap.Responses) == 0 { 449 | continue 450 | } 451 | resp := wrap.Responses[0] 452 | if resp.Ref != "" { 453 | resp = g.api.Responses[resp.Ref] 454 | } 455 | 456 | sch, err := g.generateSchemas(resp.Type) 457 | if err != nil { 458 | return nil, err 459 | } 460 | med, err := g.generateContents(resp.Name, resp.Content, sch) 461 | if err != nil { 462 | return nil, err 463 | } 464 | for k, v := range med { 465 | if medias[k] == nil { 466 | medias[k] = v 467 | } 468 | } 469 | } 470 | case "image": 471 | medias["image/*"] = &oaspec.MediaType{ 472 | Schema: sch, 473 | } 474 | prop := &oaspec.Schema{} 475 | prop.Type = "object" 476 | prop.Properties = map[string]*oaspec.Schema{ 477 | name: sch, 478 | } 479 | medias[oaspec.MimeFormData] = &oaspec.MediaType{ 480 | Schema: prop, 481 | } 482 | default: 483 | medias[content] = &oaspec.MediaType{ 484 | Schema: sch, 485 | } 486 | } 487 | 488 | return medias, nil 489 | } 490 | 491 | func (g *GenOpenAPI) generateSchemas(typ *spec.Type) (sch *oaspec.Schema, err error) { 492 | if typ.Ref != "" { 493 | if g.openapi.Components.Schemas[typ.Ref] == nil { 494 | typ0, err := g.generateSchemas(g.api.Types[typ.Ref]) 495 | if err != nil { 496 | return nil, err 497 | } 498 | g.openapi.Components.Schemas[typ.Ref] = typ0 499 | } 500 | return oaspec.RefSchemas(typ.Ref), nil 501 | } 502 | 503 | if typ.Kind == spec.Time { 504 | sch = oaspec.DateTimeProperty() 505 | } else if typ.Attr.HasOne(spec.AttrReader | spec.AttrImage) { 506 | sch = &oaspec.Schema{} 507 | sch.Type = "string" 508 | sch.Format = "binary" 509 | } else if typ.Attr.Has(spec.AttrTextMarshaler) { 510 | sch = oaspec.StrFmtProperty(typ.Name) 511 | } else { 512 | switch typ.Kind { 513 | default: 514 | sch = oaspec.StrFmtProperty(strings.ToLower(typ.Kind.String())) 515 | case spec.String: 516 | sch = oaspec.StringProperty() 517 | case spec.Bool: 518 | sch = oaspec.BooleanProperty() 519 | case spec.Float32: 520 | sch = oaspec.Float32Property() // .WithMinimum(-math.MaxFloat32, false).WithMaximum(math.MaxFloat32, false) 521 | case spec.Float64: 522 | sch = oaspec.Float64Property() // .WithMinimum(-math.MaxFloat64, false).WithMaximum(math.MaxFloat64, false) 523 | case spec.Int8: 524 | sch = oaspec.Int8Property().WithMinimum(math.MinInt8, false).WithMaximum(math.MaxInt8, false) 525 | case spec.Int16: 526 | sch = oaspec.Int16Property().WithMinimum(math.MinInt16, false).WithMaximum(math.MaxInt16, false) 527 | case spec.Int32: 528 | sch = oaspec.Int32Property() // .WithMinimum(math.MinInt32, false).WithMaximum(math.MaxInt32, false) 529 | case spec.Int64, spec.Int: 530 | sch = oaspec.Int64Property() // .WithMinimum(math.MinInt64, false).WithMaximum(math.MaxInt64, false) 531 | case spec.Uint8: 532 | sch = oaspec.IntFmtProperty("uint8").WithMinimum(0, false).WithMaximum(math.MaxUint8, false) 533 | case spec.Uint16: 534 | sch = oaspec.IntFmtProperty("uint16").WithMinimum(0, false).WithMaximum(math.MaxUint16, false) 535 | case spec.Uint32: 536 | sch = oaspec.IntFmtProperty("uint32") // .WithMinimum(0, false).WithMaximum(math.MaxUint32, false) 537 | case spec.Uint64, spec.Uint: 538 | sch = oaspec.IntFmtProperty("uint64") // .WithMinimum(0, false).WithMaximum(math.MaxUint64, false) 539 | case spec.Map: 540 | sch, err = g.generateSchemas(typ.Elem) 541 | if err != nil { 542 | return nil, err 543 | } 544 | sch = oaspec.MapProperty(sch) 545 | case spec.Slice: 546 | if typ.Elem.Kind == spec.Byte { 547 | sch = oaspec.StrFmtProperty("binary") 548 | } else { 549 | sch, err = g.generateSchemas(typ.Elem) 550 | if err != nil { 551 | return nil, err 552 | } 553 | sch = oaspec.ArrayProperty(sch) 554 | } 555 | case spec.Array: 556 | sch, err = g.generateSchemas(typ.Elem) 557 | if err != nil { 558 | return nil, err 559 | } 560 | sch = oaspec.ArrayProperty(sch).WithMaxItems(int64(typ.Len)) 561 | case spec.Ptr: 562 | sch, err = g.generateSchemas(typ.Elem) 563 | if err != nil { 564 | return nil, err 565 | } 566 | case spec.Error: 567 | sch = oaspec.StrFmtProperty("error") 568 | case spec.Struct: 569 | sch = &oaspec.Schema{} 570 | sch.Type = "object" 571 | for _, v := range typ.Fields { 572 | name := v.Name 573 | tag := v.Tag 574 | required := true 575 | val, err := g.generateSchemas(v.Type) 576 | if err != nil { 577 | return nil, err 578 | } 579 | if val.Ref != "" { 580 | tt := g.openapi.Components.Schemas[val.Ref] 581 | oldval := val 582 | val = &oaspec.Schema{} 583 | val.AllOf = []*oaspec.Schema{oldval} 584 | val.Description = tt.Description 585 | } 586 | if v.Description != "" { 587 | val.Description, _ = utils.GetTag(val.Description + "\n" + v.Description) 588 | } 589 | if v.Anonymous { 590 | sch.AllOf = append(sch.AllOf, val) 591 | continue 592 | } 593 | if sch.Properties == nil { 594 | sch.Properties = map[string]*oaspec.Schema{} 595 | } 596 | 597 | if raw := tag.Get("xml"); raw != "" { 598 | tags := strings.Split(raw, ",") 599 | if n := tags[0]; n != "" && n != "-" { 600 | if name == "XMLName" { 601 | sch.WithXMLName(n) 602 | } else { 603 | if index := strings.Index(n, ">"); index != -1 { 604 | lname := n[:index] 605 | if lname == "" { 606 | lname = name 607 | } 608 | val = val.WithXMLName(lname) 609 | rname := n[index+1:] 610 | if rname == lname && val.Type == "array" { 611 | val.XML.Wrapped = true 612 | } 613 | } else { 614 | val = val.WithXMLName(n) 615 | } 616 | for _, tag := range tags { 617 | switch tag { 618 | case "attr": 619 | val.XML.Attribute = true 620 | case "string": 621 | val.Type = "string" 622 | case "omitempty": 623 | required = false 624 | } 625 | } 626 | } 627 | } 628 | } 629 | 630 | if raw := tag.Get("json"); raw != "" { 631 | tags := strings.Split(raw, ",") 632 | if n := tags[0]; n != "" && n != "-" { 633 | name = n 634 | for _, tag := range tags { 635 | switch tag { 636 | case "string": 637 | val.Type = "string" 638 | case "omitempty": 639 | required = false 640 | } 641 | } 642 | } 643 | } 644 | 645 | if required { 646 | sch.AddRequired(name) 647 | } 648 | sch.Properties[name] = val 649 | } 650 | } 651 | } 652 | 653 | sch.Description = typ.Description 654 | for _, v := range typ.Enum { 655 | sch.Enum = append(sch.Enum, oaspec.Any(v.Value)) 656 | if v.Description != "" { 657 | sch.Description += "\n - " + v.Value + ": " + v.Description 658 | } 659 | } 660 | sch.Description, _ = utils.GetTag(sch.Description) 661 | return sch, nil 662 | } 663 | 664 | func (g *GenOpenAPI) generateSecurityScheme(sec *spec.Security) (err error) { 665 | 666 | if len(sec.Requests) == 0 { 667 | return nil 668 | } 669 | req := sec.Requests[0] 670 | if req.Ref != "" { 671 | req = g.api.Requests[req.Ref] 672 | } 673 | 674 | secu := &oaspec.SecurityScheme{} 675 | 676 | sch := strings.Split(sec.Schema, ",") 677 | switch sch[0] { 678 | case oaspec.SecurityAPIKey: 679 | secu = oaspec.APIKeyAuth(req.Name, req.In) 680 | case "basic": 681 | secu = oaspec.BasicAuth() 682 | case "bearer": 683 | bearerFormat := "" 684 | if len(sch) > 1 { 685 | bearerFormat = sch[1] 686 | } 687 | secu = oaspec.BearerAuth(bearerFormat) 688 | default: 689 | secu.Type = sec.Schema 690 | } 691 | secu.Description = sec.Description 692 | g.openapi.Components.SecuritySchemes[sec.Name] = secu 693 | return 694 | } 695 | -------------------------------------------------------------------------------- /parser/kind_mapping.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/wzshiming/gen/spec" 5 | "github.com/wzshiming/gotype" 6 | ) 7 | 8 | var kindMapping = map[gotype.Kind]spec.Kind{ 9 | gotype.Bool: spec.Bool, 10 | gotype.Int: spec.Int, 11 | gotype.Int8: spec.Int8, 12 | gotype.Int16: spec.Int16, 13 | gotype.Int32: spec.Int32, 14 | gotype.Int64: spec.Int64, 15 | gotype.Uint: spec.Uint, 16 | gotype.Uint8: spec.Uint8, 17 | gotype.Uint16: spec.Uint16, 18 | gotype.Uint32: spec.Uint32, 19 | gotype.Uint64: spec.Uint64, 20 | gotype.Uintptr: spec.Uintptr, 21 | gotype.Float32: spec.Float32, 22 | gotype.Float64: spec.Float64, 23 | gotype.Complex64: spec.Complex64, 24 | gotype.Complex128: spec.Complex128, 25 | gotype.String: spec.String, 26 | gotype.Byte: spec.Byte, 27 | gotype.Rune: spec.Rune, 28 | gotype.Error: spec.Error, 29 | gotype.Array: spec.Array, 30 | gotype.Chan: spec.Chan, 31 | gotype.Func: spec.Func, 32 | gotype.Interface: spec.Interface, 33 | gotype.Map: spec.Map, 34 | gotype.Ptr: spec.Ptr, 35 | gotype.Slice: spec.Slice, 36 | gotype.Struct: spec.Struct, 37 | } 38 | -------------------------------------------------------------------------------- /parser/parser.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path" 7 | "strings" 8 | 9 | "github.com/wzshiming/gen/named" 10 | "github.com/wzshiming/gen/spec" 11 | "github.com/wzshiming/gen/utils" 12 | "github.com/wzshiming/gotype" 13 | ) 14 | 15 | // Parser is the parse type generating definitions 16 | type Parser struct { 17 | imp *gotype.Importer 18 | api *spec.API 19 | ways map[string]bool 20 | consts map[string]gotype.Type 21 | 22 | namedReq *named.Named 23 | namedResp *named.Named 24 | namedMidd *named.Named 25 | namedWrap *named.Named 26 | namedSecu *named.Named 27 | namedTyp *named.Named 28 | } 29 | 30 | func NewParser(imp *gotype.Importer) *Parser { 31 | if imp == nil { 32 | imp = gotype.NewImporter( 33 | gotype.WithCommentLocator(), 34 | gotype.ImportHandler(func(path, src, dir string) { 35 | fmt.Fprintln(os.Stderr, "gen: import", dir) 36 | }), 37 | gotype.ErrorHandler(func(err error) { 38 | fmt.Fprintln(os.Stderr, "gen: error", err.Error()) 39 | })) 40 | } 41 | return &Parser{ 42 | imp: imp, 43 | api: spec.NewAPI(), 44 | ways: map[string]bool{}, 45 | consts: map[string]gotype.Type{}, 46 | namedReq: named.NewNamed("."), 47 | namedResp: named.NewNamed("."), 48 | namedMidd: named.NewNamed("."), 49 | namedWrap: named.NewNamed("."), 50 | namedSecu: named.NewNamed("."), 51 | namedTyp: named.NewNamed("."), 52 | } 53 | } 54 | 55 | func (g *Parser) API() *spec.API { 56 | return g.api 57 | } 58 | 59 | func (g *Parser) Import(pkgpath string, ways string) error { 60 | if !strings.HasSuffix(pkgpath, "/...") { 61 | return g.importOnce(pkgpath) 62 | } 63 | 64 | if ways != "" { 65 | for _, way := range strings.Split(ways, ",") { 66 | g.ways[way] = true 67 | } 68 | } 69 | 70 | pkgs := utils.PackageOmitted(pkgpath) 71 | for _, out := range pkgs { 72 | err := g.importOnce(out) 73 | if err != nil { 74 | return err 75 | } 76 | } 77 | 78 | return nil 79 | } 80 | 81 | func (g *Parser) isWay(way string) bool { 82 | if way == "" { 83 | return true 84 | } 85 | pre := 0 86 | for i, c := range way { 87 | if c == ',' { 88 | if g.ways[way[pre:i]] { 89 | return true 90 | } 91 | pre = i + 1 92 | } 93 | } 94 | return g.ways[way[pre:]] 95 | } 96 | 97 | func (g *Parser) importChild(pkgpath string, src string, name string) (gotype.Type, bool) { 98 | t, ok := g.consts[name] 99 | if ok { 100 | return t, true 101 | } 102 | 103 | pkg, err := g.imp.Import(pkgpath, src) 104 | if err != nil { 105 | return nil, false 106 | } 107 | return pkg.ChildByName(name) 108 | } 109 | 110 | func (g *Parser) importOnce(pkgpath string) error { 111 | src, err := os.Getwd() 112 | if err != nil { 113 | return err 114 | } 115 | pkg, err := g.imp.Import(pkgpath, src) 116 | if err != nil { 117 | return err 118 | } 119 | g.api.Imports = append(g.api.Imports, pkg.PkgPath()) 120 | g.api.Package = pkg.Name() 121 | numchi := pkg.NumChild() 122 | 123 | for i := 0; i != numchi; i++ { 124 | v := pkg.Child(i) 125 | if !IsExported(v.Name()) { 126 | continue 127 | } 128 | switch v.Kind() { 129 | case gotype.Declaration: 130 | if len(g.ways) != 0 { 131 | doc := v.Doc().Text() 132 | _, tag := utils.GetTag(doc) 133 | if !g.isWay(tag.Get("way")) { 134 | continue 135 | } 136 | } 137 | err = g.addWrapping(src, nil, v) 138 | if err != nil { 139 | return err 140 | } 141 | err = g.addMiddleware(src, nil, v) 142 | if err != nil { 143 | return err 144 | } 145 | err = g.addSecurity(src, nil, v) 146 | if err != nil { 147 | return err 148 | } 149 | err = g.addOperation(src, "", nil, v, nil) 150 | if err != nil { 151 | return err 152 | } 153 | default: 154 | 155 | if len(g.ways) != 0 { 156 | doc := v.Doc().Text() 157 | _, tag := utils.GetTag(doc) 158 | if !g.isWay(tag.Get("way")) { 159 | continue 160 | } 161 | } 162 | 163 | err = g.addPaths(src, v) 164 | if err != nil { 165 | return err 166 | } 167 | case gotype.Scope, gotype.Invalid: 168 | // No action 169 | } 170 | } 171 | 172 | return nil 173 | } 174 | 175 | func (g *Parser) addPaths(src string, t gotype.Type) (err error) { 176 | _, tag := utils.GetTag(t.Doc().Text()) 177 | if tag == "" { 178 | return nil 179 | } 180 | path := tag.Get("path") 181 | if path == "" { 182 | return nil 183 | } 184 | 185 | sch, err := g.addType(src, t) 186 | if err != nil { 187 | return err 188 | } 189 | 190 | filter := map[string]bool{} 191 | return g.addMethods(src, path, sch, t, nil, filter) 192 | } 193 | 194 | func (g *Parser) addMethods(src string, basePath string, sch *spec.Type, t gotype.Type, chain []string, filter map[string]bool) (err error) { 195 | if t.Kind() == gotype.Ptr { 196 | t = t.Elem() 197 | } 198 | 199 | numm := t.NumMethod() 200 | for i := 0; i != numm; i++ { 201 | v := t.Method(i) 202 | name := v.Name() 203 | if !IsExported(name) || filter[name] { 204 | continue 205 | } 206 | filter[name] = true 207 | 208 | if len(g.ways) != 0 { 209 | doc := v.Doc().Text() 210 | _, tag := utils.GetTag(doc) 211 | if !g.isWay(tag.Get("way")) { 212 | continue 213 | } 214 | } 215 | err = g.addWrapping(src, nil, v) 216 | if err != nil { 217 | return err 218 | } 219 | err = g.addMiddleware(src, sch, v) 220 | if err != nil { 221 | return err 222 | } 223 | err = g.addSecurity(src, sch, v) 224 | if err != nil { 225 | return err 226 | } 227 | err = g.addOperation(src, basePath, sch, v, chain) 228 | if err != nil { 229 | return err 230 | } 231 | } 232 | 233 | if t.Kind() == gotype.Struct { 234 | numf := t.NumField() 235 | for i := 0; i != numf; i++ { 236 | v := t.Field(i) 237 | if v.IsAnonymous() { 238 | _, tag := utils.GetTag(v.Doc().Text()) 239 | if tag == "" { 240 | continue 241 | } 242 | lpath := tag.Get("path") 243 | if lpath == "" { 244 | continue 245 | } 246 | 247 | basePath := path.Join(basePath, lpath) 248 | v = v.Elem() 249 | err = g.addMethods(src, basePath, sch, v, chain, filter) 250 | if err != nil { 251 | return err 252 | } 253 | 254 | } else { 255 | name := v.Name() 256 | if !IsExported(name) { 257 | continue 258 | } 259 | 260 | _, tag := utils.GetTag(v.Doc().Text()) 261 | if tag == "" { 262 | continue 263 | } 264 | lpath := tag.Get("path") 265 | if lpath == "" { 266 | continue 267 | } 268 | 269 | basePath := path.Join(basePath, lpath) 270 | v = v.Elem() 271 | newChain := make([]string, len(chain), len(chain)+1) 272 | copy(newChain, chain) 273 | newChain = append(newChain, name) 274 | filter := map[string]bool{} 275 | err = g.addMethods(src, basePath, sch, v, newChain, filter) 276 | if err != nil { 277 | return err 278 | } 279 | } 280 | } 281 | } 282 | return 283 | } 284 | 285 | func (g *Parser) addMiddleware(src string, sch *spec.Type, t gotype.Type) (err error) { 286 | doc, tag := utils.GetTag(t.Doc().Text()) 287 | if tag == "" { 288 | return nil 289 | } 290 | middleware := tag.Get("middleware") 291 | if middleware == "" { 292 | return nil 293 | } 294 | 295 | oname := t.Name() 296 | pkgpath := t.PkgPath() 297 | 298 | t = t.Declaration() 299 | if t.Kind() != gotype.Func { 300 | return nil 301 | } 302 | 303 | path := "" 304 | route := tag.Get("route") 305 | if route != "" { 306 | _, _, path, _ = GetRoute(route) 307 | } 308 | 309 | name := GetName(oname, tag) 310 | hash := utils.Hash(pkgpath, name, oname, middleware) 311 | key := g.namedMidd.GetName(name, hash) 312 | 313 | midd := &spec.Middleware{} 314 | midd.Name = name 315 | midd.PkgPath = pkgpath 316 | midd.Schema = middleware 317 | midd.Description = doc 318 | midd.DescriptionTag = tag 319 | midd.Type = sch 320 | 321 | reqs, err := g.addRequests(src, path, t, false) 322 | midd.Requests = reqs 323 | 324 | resps, err := g.addResponses(src, t) 325 | midd.Responses = resps 326 | 327 | g.api.Middlewares[key] = midd 328 | return nil 329 | } 330 | 331 | func (g *Parser) addWrapping(src string, sch *spec.Type, t gotype.Type) (err error) { 332 | 333 | doc, tag := utils.GetTag(t.Doc().Text()) 334 | if tag == "" { 335 | return nil 336 | } 337 | wrapping := tag.Get("wrapping") 338 | if wrapping == "" { 339 | return nil 340 | } 341 | 342 | oname := t.Name() 343 | pkgpath := t.PkgPath() 344 | 345 | t = t.Declaration() 346 | if t.Kind() != gotype.Func { 347 | return nil 348 | } 349 | 350 | path := "" 351 | route := tag.Get("route") 352 | if route != "" { 353 | _, _, path, _ = GetRoute(route) 354 | } 355 | 356 | name := GetName(oname, tag) 357 | hash := utils.Hash(pkgpath, name, oname, wrapping) 358 | key := g.namedWrap.GetName(name, hash) 359 | 360 | wrap := &spec.Wrapping{} 361 | wrap.Name = name 362 | wrap.PkgPath = pkgpath 363 | wrap.Schema = wrapping 364 | wrap.Description = doc 365 | wrap.DescriptionTag = tag 366 | wrap.Type = sch 367 | 368 | reqs, err := g.addRequests(src, path, t, true) 369 | wrap.Requests = reqs 370 | 371 | resps, err := g.addResponses(src, t) 372 | wrap.Responses = resps 373 | 374 | g.api.Wrappings[key] = wrap 375 | return nil 376 | } 377 | 378 | func (g *Parser) addSecurity(src string, sch *spec.Type, t gotype.Type) (err error) { 379 | 380 | doc, tag := utils.GetTag(t.Doc().Text()) 381 | if tag == "" { 382 | return nil 383 | } 384 | security := tag.Get("security") 385 | if security == "" { 386 | return nil 387 | } 388 | 389 | oname := t.Name() 390 | pkgpath := t.PkgPath() 391 | 392 | t = t.Declaration() 393 | if t.Kind() != gotype.Func { 394 | return nil 395 | } 396 | 397 | name := GetName(oname, tag) 398 | hash := utils.Hash(pkgpath, name, oname, security) 399 | key := g.namedSecu.GetName(name, hash) 400 | 401 | secu := &spec.Security{} 402 | secu.Name = name 403 | secu.PkgPath = pkgpath 404 | secu.Schema = security 405 | secu.Description = doc 406 | secu.DescriptionTag = tag 407 | secu.Type = sch 408 | 409 | reqs, err := g.addRequests(src, "", t, false) 410 | secu.Requests = reqs 411 | 412 | resps, err := g.addResponses(src, t) 413 | secu.Responses = resps 414 | 415 | g.api.Securitys[key] = secu 416 | return nil 417 | } 418 | 419 | func (g *Parser) addOperation(src string, basePath string, sch *spec.Type, t gotype.Type, chain []string) (err error) { 420 | 421 | doc, tag := utils.GetTag(t.Doc().Text()) 422 | if tag == "" { 423 | return nil 424 | } 425 | route := tag.Get("route") 426 | if route == "" { 427 | return nil 428 | } 429 | 430 | oname := t.Name() 431 | pkgpath := t.PkgPath() 432 | 433 | t = t.Declaration() 434 | if t.Kind() != gotype.Func { 435 | return nil 436 | } 437 | 438 | deprecated, method, pat, ok := GetRoute(route) 439 | if !ok { 440 | return nil 441 | } 442 | 443 | if basePath != "" { 444 | basePath = path.Clean(basePath) 445 | pat = path.Join(basePath, pat) 446 | } 447 | 448 | name := GetName(oname, tag) 449 | oper := &spec.Operation{} 450 | oper.PkgPath = pkgpath 451 | oper.Method = method 452 | oper.BasePath = basePath 453 | oper.Path = pat 454 | oper.Description = doc 455 | oper.Summary = strings.SplitN(oper.Description, "\n", 2)[0] 456 | oper.Deprecated = deprecated 457 | oper.DescriptionTag = tag 458 | oper.Type = sch 459 | oper.Name = name 460 | oper.Chain = chain 461 | 462 | if sch != nil { 463 | sch := sch 464 | if sch.Ref != "" { 465 | sch = g.api.Types[sch.Ref] 466 | } 467 | oper.Tags = append(oper.Tags, sch.Name) 468 | } 469 | 470 | reqs, err := g.addRequests(src, pat, t, false) 471 | if err != nil { 472 | return err 473 | } 474 | oper.Requests = reqs 475 | 476 | resps, err := g.addResponses(src, t) 477 | if err != nil { 478 | return err 479 | } 480 | oper.Responses = resps 481 | 482 | g.api.Operations = append(g.api.Operations, oper) 483 | return nil 484 | } 485 | 486 | func (g *Parser) addResponses(src string, t gotype.Type) (resps []*spec.Response, err error) { 487 | numout := t.NumOut() 488 | for i := 0; i != numout; i++ { 489 | v := t.Out(i) 490 | resp, err := g.addResponse(src, v) 491 | if err != nil { 492 | return nil, err 493 | } 494 | resps = append(resps, resp) 495 | } 496 | return resps, nil 497 | } 498 | 499 | func (g *Parser) addResponse(src string, t gotype.Type) (resp *spec.Response, err error) { 500 | 501 | oname := t.Name() 502 | doc, tag := utils.GetTag(t.Comment().Text()) 503 | name := GetName(oname, tag) 504 | code := tag.Get("code") 505 | in := tag.Get("in") 506 | content := tag.Get("content") 507 | pkgpath := t.PkgPath() 508 | t = t.Declaration() 509 | 510 | kind := t.Kind() 511 | if in == "" { 512 | in = "body" 513 | } 514 | 515 | sch, err := g.addType(src, t) 516 | if err != nil { 517 | return nil, err 518 | } 519 | 520 | if in == "body" { 521 | if code == "" { 522 | if kind != gotype.Error { 523 | code = "200" 524 | } else { 525 | code = "400" 526 | } 527 | } 528 | 529 | if content == "" { 530 | typ := sch 531 | if typ.Ref != "" { 532 | typ = g.api.Types[typ.Ref] 533 | } 534 | 535 | if typ.Attr.Has(spec.AttrImage) { 536 | content = "image" 537 | } else if typ.Attr.Has(spec.AttrReader) { 538 | content = "file" 539 | } else if kind != gotype.Error { 540 | content = "json" 541 | } else { 542 | content = "error" 543 | } 544 | } 545 | } 546 | 547 | hash := utils.Hash(pkgpath, name, oname, in, code, content, sch.Ref, sch.Ident) 548 | 549 | key := g.namedResp.GetName(name+"_"+in, hash) 550 | 551 | if g.api.Responses[key] != nil { 552 | return &spec.Response{ 553 | Ref: key, 554 | }, nil 555 | } 556 | 557 | resp = &spec.Response{} 558 | resp.Ident = key 559 | resp.In = in 560 | resp.Name = name 561 | resp.Code = code 562 | resp.Content = content 563 | resp.Description = doc 564 | resp.DescriptionTag = tag 565 | resp.Type = sch 566 | 567 | g.api.Responses[key] = resp 568 | return &spec.Response{ 569 | Ref: key, 570 | }, nil 571 | } 572 | 573 | func (g *Parser) addRequests(src string, basePath string, t gotype.Type, resp bool) (reqs []*spec.Request, err error) { 574 | numin := t.NumIn() 575 | 576 | for i := 0; i != numin; i++ { 577 | v := t.In(i) 578 | req, err := g.addRequest(src, basePath, v, resp) 579 | if err != nil { 580 | return nil, err 581 | } 582 | reqs = append(reqs, req) 583 | } 584 | return reqs, nil 585 | } 586 | 587 | func (g *Parser) addRequest(src string, basePath string, t gotype.Type, resp bool) (par *spec.Request, err error) { 588 | 589 | oname := t.Name() 590 | doc, tag := utils.GetTag(t.Comment().Text()) 591 | name := GetName(oname, tag) 592 | in := tag.Get("in") 593 | pkgpath := t.PkgPath() 594 | t = t.Declaration() 595 | switch t.Kind() { 596 | case gotype.Ptr: 597 | if req, ok := g.importChild("net/http", "", "Request"); ok && gotype.Equal(t.Elem(), req) { 598 | return &spec.Request{ 599 | In: "none", 600 | Ident: "*net/http.Request", 601 | Name: "r", 602 | }, nil 603 | } else if req, ok := g.importChild("net/url", "", "Userinfo"); ok && gotype.Equal(t.Elem(), req) { 604 | return &spec.Request{ 605 | In: "none", 606 | Ident: "*net/url.Userinfo", 607 | Name: "r.URL.User", 608 | }, nil 609 | } 610 | 611 | case gotype.Interface: 612 | if resp, ok := g.importChild("net/http", "", "ResponseWriter"); ok && gotype.Implements(resp, t) { 613 | return &spec.Request{ 614 | In: "none", 615 | Ident: "net/http.ResponseWriter", 616 | Name: "w", 617 | }, nil 618 | } else if resp, ok := g.importChild("context", "", "Context"); ok && gotype.Implements(resp, t) { 619 | return &spec.Request{ 620 | In: "none", 621 | Ident: "context.Context", 622 | Name: "r.Context()", 623 | }, nil 624 | } 625 | } 626 | 627 | sch, err := g.addType(src, t) 628 | if err != nil { 629 | return nil, err 630 | } 631 | 632 | content := tag.Get("content") 633 | 634 | if in == "" { 635 | if resp { 636 | in = "wrapping" 637 | } else { 638 | typ := sch 639 | if typ.Ref != "" { 640 | typ = g.api.Types[typ.Ref] 641 | } 642 | 643 | if typ.Attr.Has(spec.AttrImage) { 644 | in = "body" 645 | if content == "" { 646 | content = "image" 647 | } 648 | } else if typ.Attr.Has(spec.AttrReader) { 649 | in = "body" 650 | if content == "" { 651 | content = "file" 652 | } 653 | } else if typ.Attr.Has(spec.AttrTextUnmarshaler) { 654 | if basePath != "" && strings.Index(basePath, "{"+name+"}") != -1 { 655 | in = "path" 656 | } else { 657 | in = "query" 658 | } 659 | } else { 660 | if typ.Kind == spec.Ptr { 661 | typ = typ.Elem 662 | if typ.Ref != "" { 663 | typ = g.api.Types[typ.Ref] 664 | } 665 | } 666 | switch typ.Kind { 667 | case spec.Map, spec.Struct: 668 | in = "body" 669 | case spec.Interface: 670 | in = "middleware" 671 | default: 672 | if basePath != "" && strings.Index(basePath, "{"+name+"}") != -1 { 673 | in = "path" 674 | } else { 675 | in = "query" 676 | } 677 | } 678 | } 679 | } 680 | } 681 | 682 | if content == "" { 683 | content = "json" 684 | } 685 | 686 | hash := utils.Hash(pkgpath, name, oname, in, content, sch.Ref, sch.Ident) 687 | key := g.namedReq.GetName(name+"_"+in, hash) 688 | 689 | if g.api.Requests[key] != nil { 690 | return &spec.Request{ 691 | Ref: key, 692 | }, nil 693 | } 694 | 695 | par = &spec.Request{} 696 | par.Ident = key 697 | par.In = in 698 | par.Name = name 699 | par.Content = content 700 | par.Description = doc 701 | par.DescriptionTag = tag 702 | par.Type = sch 703 | 704 | g.api.Requests[key] = par 705 | return &spec.Request{ 706 | Ref: key, 707 | }, nil 708 | } 709 | 710 | func (g *Parser) addType(src string, t gotype.Type) (sch *spec.Type, err error) { 711 | oname := t.Name() 712 | pkgpath := t.PkgPath() 713 | 714 | doc, tag := utils.GetTag(t.Doc().Text()) 715 | name := GetName(oname, tag) 716 | kind := t.Kind() 717 | isRoot := t.IsGoroot() 718 | isBuiltin := name == strings.ToLower(kind.String()) 719 | hash := "" 720 | if !isBuiltin { 721 | hash = utils.Hash(pkgpath, name, oname, t.String()) 722 | } 723 | key := g.namedTyp.GetName(name, hash) 724 | 725 | if g.api.Types[key] != nil { 726 | return &spec.Type{ 727 | Ref: key, 728 | }, nil 729 | } 730 | 731 | sch = &spec.Type{} 732 | if isRoot { 733 | sch.Attr.Add(spec.AttrRoot) 734 | } 735 | sch.Ident = key 736 | sch.Name = name 737 | sch.PkgPath = pkgpath 738 | sch.Description = doc 739 | sch.DescriptionTag = tag 740 | 741 | if time, ok := g.importChild("time", "", "Time"); ok && gotype.Equal(time, t) { 742 | sch.Description = "This is the time string in RFC3339 format" 743 | sch.Kind = spec.Time 744 | sch.Attr.Add(spec.AttrTextUnmarshaler) 745 | sch.Attr.Add(spec.AttrTextMarshaler) 746 | sch.Attr.Add(spec.AttrJSONUnmarshaler) 747 | sch.Attr.Add(spec.AttrJSONMarshaler) 748 | return sch, nil 749 | } 750 | 751 | switch kind { 752 | case gotype.Struct: 753 | // Field 754 | { 755 | num := t.NumField() 756 | for i := 0; i != num; i++ { 757 | v := t.Field(i) 758 | name := v.Name() 759 | if !IsExported(name) { 760 | continue 761 | } 762 | tag := v.Tag() 763 | val, err := g.addType(src, v.Elem()) 764 | if err != nil { 765 | return nil, err 766 | } 767 | 768 | desc := v.Doc().Text() + "\n" + v.Comment().Text() 769 | desc, _ = utils.GetTag(desc) 770 | field := &spec.Field{ 771 | Name: name, 772 | Type: val, 773 | Tag: tag, 774 | Anonymous: v.IsAnonymous(), 775 | Description: desc, 776 | } 777 | 778 | sch.Fields = append(sch.Fields, field) 779 | } 780 | } 781 | 782 | case gotype.Error, gotype.String, gotype.Bool, gotype.Float32, gotype.Float64, 783 | gotype.Int8, gotype.Int16, gotype.Int32, gotype.Int64, gotype.Int, 784 | gotype.Uint8, gotype.Uint16, gotype.Uint32, gotype.Uint64, gotype.Uint, 785 | gotype.Byte, gotype.Rune: 786 | 787 | if name != "_" && name != strings.ToLower(kind.String()) { 788 | scope, err := g.imp.Import(t.PkgPath(), src) 789 | if err != nil { 790 | return nil, err 791 | } 792 | 793 | numchi := scope.NumChild() 794 | for i := 0; i != numchi; i++ { 795 | v := scope.Child(i) 796 | if v.Kind() != gotype.Declaration { 797 | continue 798 | } 799 | vname := v.Name() 800 | v = v.Declaration() 801 | if typname := v.Name(); name == typname { 802 | if value := v.Value(); value != "" { 803 | desc := v.Doc().Text() + "\n" + v.Comment().Text() 804 | desc, _ = utils.GetTag(desc) 805 | sch.Enum = append(sch.Enum, &spec.Enum{ 806 | Name: vname, 807 | Value: value, 808 | Description: desc, 809 | }) 810 | } 811 | } 812 | } 813 | } 814 | case gotype.Map: 815 | schk, err := g.addType(src, t.Key()) 816 | if err != nil { 817 | return nil, err 818 | } 819 | schv, err := g.addType(src, t.Elem()) 820 | if err != nil { 821 | return nil, err 822 | } 823 | sch.Key = schk 824 | sch.Elem = schv 825 | case gotype.Slice: 826 | schv, err := g.addType(src, t.Elem()) 827 | if err != nil { 828 | return nil, err 829 | } 830 | sch.Elem = schv 831 | case gotype.Array: 832 | schv, err := g.addType(src, t.Elem()) 833 | if err != nil { 834 | return nil, err 835 | } 836 | sch.Elem = schv 837 | sch.Len = t.Len() 838 | case gotype.Ptr: 839 | schv, err := g.addType(src, t.Elem()) 840 | if err != nil { 841 | return nil, err 842 | } 843 | sch.Elem = schv 844 | case gotype.Interface, gotype.Func: 845 | // No action 846 | default: 847 | return nil, fmt.Errorf("Gen.addType: unsupported type: %s %s is %s kind\n", pkgpath, t, kind) 848 | } 849 | 850 | sch.Kind = kindMapping[kind] 851 | 852 | if text, ok := g.importChild("encoding", "", "TextUnmarshaler"); ok && gotype.Implements(t, text) { 853 | sch.Attr.Add(spec.AttrTextUnmarshaler) 854 | } 855 | if text, ok := g.importChild("encoding", "", "TextMarshaler"); ok && gotype.Implements(t, text) { 856 | sch.Attr.Add(spec.AttrTextMarshaler) 857 | } 858 | if text, ok := g.importChild("encoding/json", "", "Unmarshaler"); ok && gotype.Implements(t, text) { 859 | sch.Attr.Add(spec.AttrJSONUnmarshaler) 860 | } 861 | if text, ok := g.importChild("encoding/json", "", "Marshaler"); ok && gotype.Implements(t, text) { 862 | sch.Attr.Add(spec.AttrJSONMarshaler) 863 | } 864 | 865 | if read, ok := g.importChild("io", "", "Reader"); ok && gotype.Implements(t, read) { 866 | sch.Attr.Add(spec.AttrReader) 867 | } 868 | 869 | if read, ok := g.importChild("image", "", "Image"); ok && gotype.Implements(t, read) { 870 | sch.Attr.Add(spec.AttrImage) 871 | } 872 | 873 | if name != "" && !isBuiltin { 874 | g.api.Types[key] = sch 875 | return &spec.Type{ 876 | Ref: key, 877 | }, nil 878 | } 879 | 880 | return sch, nil 881 | } 882 | -------------------------------------------------------------------------------- /parser/util.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "go/ast" 5 | "reflect" 6 | "strings" 7 | 8 | "github.com/wzshiming/gen/utils" 9 | "github.com/wzshiming/gotype" 10 | ) 11 | 12 | func GetName(t string, tag reflect.StructTag) string { 13 | name, ok := tag.Lookup("name") 14 | if !ok { 15 | name = t 16 | } 17 | return name 18 | } 19 | 20 | func IsExported(name string) bool { 21 | return ast.IsExported(name) 22 | } 23 | 24 | func GetTypeHash(typ gotype.Type) string { 25 | tp := 0 26 | for typ.Kind() == gotype.Ptr { 27 | tp++ 28 | typ = typ.Elem() 29 | } 30 | 31 | pkgpath := typ.PkgPath() 32 | name := typ.Name() 33 | return strings.Repeat("_", tp) + name + "." + utils.Hash(name, pkgpath) 34 | } 35 | 36 | // GetRoute "!GET /path" 37 | func GetRoute(route string) (deprecated bool, method, path string, ok bool) { 38 | 39 | route = strings.TrimSpace(route) 40 | if route == "" { 41 | return false, "", "", false 42 | } 43 | if route[0] == '!' { 44 | deprecated = true 45 | route = route[1:] 46 | } 47 | 48 | route = strings.TrimSpace(route) 49 | rs := strings.SplitN(route, " ", 2) 50 | if len(rs) != 2 { 51 | return false, "", "", false 52 | } 53 | path = strings.TrimSpace(rs[1]) 54 | if path == "" { 55 | return false, "", "", false 56 | } 57 | method = strings.TrimSpace(strings.ToUpper(rs[0])) 58 | if method == "" { 59 | return false, "", "", false 60 | } 61 | return deprecated, method, path, true 62 | } 63 | -------------------------------------------------------------------------------- /route/call.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/wzshiming/gen/spec" 7 | ) 8 | 9 | func (g *GenRoute) generateCallExec(name string, chain []string, pkgpath string, typ *spec.Type, requests []*spec.Request, responses []*spec.Response, errName string, onErr bool) (err error) { 10 | 11 | for _, req := range requests { 12 | req := req 13 | if req.Ref != "" { 14 | req = g.api.Requests[req.Ref] 15 | } 16 | if req.Content == "formdata" { 17 | g.buf.WriteFormat(` 18 | if r.MultipartForm == nil { 19 | %s := r.ParseMultipartForm(10 << 20)`, errName) 20 | g.generateResponseError(errName, "400", false) 21 | g.buf.WriteFormat(` 22 | } 23 | `) 24 | break 25 | } 26 | } 27 | 28 | for _, req := range requests { 29 | err = g.generateRequest(req, errName) 30 | if err != nil { 31 | return err 32 | } 33 | } 34 | 35 | err = g.generateCall(name, chain, pkgpath, typ, requests, responses, errName) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | err = g.generateCallOnErr(name, chain, pkgpath, typ, requests, responses, errName, onErr) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | return nil 46 | } 47 | 48 | func (g *GenRoute) generateCallOnErr(name string, chain []string, pkgpath string, typ *spec.Type, requests []*spec.Request, responses []*spec.Response, errName string, onErr bool) (err error) { 49 | 50 | for _, resp := range responses { 51 | if onErr { 52 | if resp.Ref != "" { 53 | resp = g.api.Responses[resp.Ref] 54 | } 55 | typ := resp.Type 56 | if typ == nil { 57 | continue 58 | } 59 | if typ.Ref != "" { 60 | typ = g.api.Types[typ.Ref] 61 | } 62 | if typ.Kind != spec.Error { 63 | continue 64 | } 65 | } 66 | err = g.generateResponse(resp, errName) 67 | if err != nil { 68 | return err 69 | } 70 | } 71 | return nil 72 | } 73 | 74 | func (g *GenRoute) generateCall(name string, chain []string, pkgpath string, typ *spec.Type, requests []*spec.Request, responses []*spec.Response, errName string) (err error) { 75 | 76 | name = strings.Join(append(chain, name), ".") 77 | if typ != nil { 78 | if typ.Ref != "" { 79 | typ = g.api.Types[typ.Ref] 80 | } 81 | g.buf.WriteFormat(` 82 | // Call %s %s.%s. 83 | `, pkgpath, typ.Name, name) 84 | } else { 85 | g.buf.WriteFormat(` 86 | // Call %s %s. 87 | `, pkgpath, name) 88 | } 89 | 90 | if len(responses) != 0 { 91 | err = g.generateArgsResponses(responses) 92 | if err != nil { 93 | return err 94 | } 95 | g.buf.WriteString(" = ") 96 | } 97 | if typ != nil { 98 | g.buf.WriteString("s.") 99 | } else { 100 | g.PkgPath(pkgpath) 101 | } 102 | 103 | g.buf.WriteFormat("%s(", name) 104 | 105 | err = g.generateArgsRequests(requests) 106 | if err != nil { 107 | return err 108 | } 109 | g.buf.WriteString(")\n") 110 | 111 | return nil 112 | } 113 | 114 | func (g *GenRoute) generateArgsResponses(resps []*spec.Response) error { 115 | for i, resp := range resps { 116 | if resp.Ref != "" { 117 | resp = g.api.Responses[resp.Ref] 118 | } 119 | if i != 0 { 120 | g.buf.WriteByte(',') 121 | } 122 | g.buf.WriteString(g.getVarName(resp.Name, resp.Type)) 123 | } 124 | return nil 125 | } 126 | 127 | func (g *GenRoute) generateArgsRequests(reqs []*spec.Request) error { 128 | for i, req := range reqs { 129 | if req.Ref != "" { 130 | req = g.api.Requests[req.Ref] 131 | } 132 | if i != 0 { 133 | g.buf.WriteByte(',') 134 | } 135 | g.buf.WriteString(g.getVarName(req.Name, req.Type)) 136 | } 137 | return nil 138 | } 139 | 140 | func (g *GenRoute) generateFunctionDefine(commit string, name, oriName string, typ *spec.Type, requests []*spec.Request, responses []*spec.Response) error { 141 | g.buf.AddImport("", "net/http") 142 | g.buf.WriteFormat(` 143 | // %s Is the %s of %s 144 | func %s(`, name, commit, oriName, name) 145 | if typ != nil { 146 | g.buf.WriteString("s ") 147 | g.PtrTypes(typ) 148 | g.buf.WriteString(", ") 149 | } 150 | g.buf.WriteFormat(`w http.ResponseWriter, r *http.Request`) 151 | if len(requests) != 0 { 152 | for _, req := range requests { 153 | g.buf.WriteByte(',') 154 | if req.Ref != "" { 155 | req = g.api.Requests[req.Ref] 156 | } 157 | g.buf.WriteFormat("%s ", g.getVarName(req.Name, req.Type)) 158 | g.Types(req.Type) 159 | } 160 | } 161 | g.buf.WriteFormat(`)`) 162 | 163 | if len(responses) != 0 { 164 | g.buf.WriteFormat(`(`) 165 | for i, resp := range responses { 166 | if i != 0 { 167 | g.buf.WriteByte(',') 168 | } 169 | if resp.Ref != "" { 170 | resp = g.api.Responses[resp.Ref] 171 | } 172 | g.buf.WriteFormat("%s ", g.getVarName(resp.Name, resp.Type)) 173 | g.Types(resp.Type) 174 | } 175 | g.buf.WriteString(`)`) 176 | } 177 | return nil 178 | } 179 | -------------------------------------------------------------------------------- /route/middleware.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "github.com/wzshiming/gen/spec" 5 | ) 6 | 7 | func (g *GenRoute) generateMiddlewareFunction(midd *spec.Middleware) (err error) { 8 | name := g.getMiddlewareFunctionName(midd) 9 | 10 | if g.only[name] { 11 | return nil 12 | } 13 | g.only[name] = true 14 | 15 | err = g.generateFunctionDefine("middleware", name, midd.Name, midd.Type, nil, midd.Responses) 16 | if err != nil { 17 | return err 18 | } 19 | 20 | g.buf.WriteString(`{ 21 | `) 22 | 23 | pname := midd.Name 24 | if typ := midd.Type; typ != nil { 25 | if typ.Ref != "" { 26 | typ = g.api.Types[typ.Ref] 27 | } 28 | pname = typ.Name + "." + pname 29 | } 30 | err = g.generateRequestsVar(pname, midd.Requests) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | errName, err := g.generateResponsesErrorName(midd.Responses) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | err = g.generateCallExec(midd.Name, nil, midd.PkgPath, midd.Type, midd.Requests, midd.Responses, errName, true) 41 | if err != nil { 42 | return err 43 | } 44 | g.buf.WriteString(` 45 | return 46 | } 47 | `) 48 | 49 | return 50 | } 51 | -------------------------------------------------------------------------------- /route/not_found.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | func (g *GenRoute) generateNotFound(name string) error { 4 | 5 | err := g.generateFunctionDefine("not found", name, "handler", nil, nil, nil) 6 | if err != nil { 7 | return err 8 | } 9 | 10 | g.buf.WriteFormat(`{ 11 | `) 12 | g.buf.WriteString(` 13 | err := fmt.Errorf("Not found '%s %s'", r.Method, r.URL.Path) 14 | `) 15 | g.generateResponseErrorReturn("err", "404", false) 16 | 17 | g.buf.WriteFormat(` 18 | } 19 | `) 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /route/operation.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "github.com/wzshiming/gen/spec" 5 | ) 6 | 7 | func (g *GenRoute) generateOperationFunction(oper *spec.Operation) (err error) { 8 | name := g.getOperationFunctionName(oper) 9 | 10 | if g.only[name] { 11 | return nil 12 | } 13 | g.only[name] = true 14 | 15 | err = g.generateFunctionDefine("route", name, oper.Name, oper.Type, nil, nil) 16 | if err != nil { 17 | return err 18 | } 19 | 20 | g.buf.WriteFormat(`{ 21 | `) 22 | 23 | pname := oper.Name 24 | if typ := oper.Type; typ != nil { 25 | if typ.Ref != "" { 26 | typ = g.api.Types[typ.Ref] 27 | } 28 | pname = typ.Name + "." + pname 29 | } 30 | err = g.generateRequestsVar(pname, oper.Requests) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | err = g.generateResponsesVar(pname, oper.Responses) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | errName, err := g.generateResponsesErrorName(oper.Responses) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | err = g.generateCallExec(oper.Name, oper.Chain, oper.PkgPath, oper.Type, oper.Requests, oper.Responses, errName, false) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | err = g.generateResponses(oper.Responses, "200", errName) 51 | if err != nil { 52 | return err 53 | } 54 | 55 | return 56 | } 57 | -------------------------------------------------------------------------------- /route/request.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | 7 | "github.com/wzshiming/gen/spec" 8 | ) 9 | 10 | func (g *GenRoute) generateRequestsVar(name string, reqs []*spec.Request) error { 11 | 12 | for _, req := range reqs { 13 | if req.Ref != "" { 14 | req = g.api.Requests[req.Ref] 15 | } 16 | if req.Type == nil { 17 | continue 18 | } 19 | 20 | g.buf.WriteFormat(`// requests %s %s.%s 21 | var %s `, req.Type.PkgPath, name, req.Name, g.getVarName(req.Name, req.Type)) 22 | g.Types(req.Type) 23 | g.buf.WriteString("\n") 24 | } 25 | return nil 26 | } 27 | 28 | func (g *GenRoute) generateRequest(req *spec.Request, errName string) error { 29 | if req.Ref != "" { 30 | req = g.api.Requests[req.Ref] 31 | } 32 | 33 | vname := g.getVarName(req.Name, req.Type) 34 | switch req.In { 35 | case "none": 36 | // No action 37 | 38 | case "middleware": 39 | midds := []*spec.Middleware{} 40 | for _, midd := range g.api.Middlewares { 41 | if len(midd.Responses) == 0 { 42 | continue 43 | } 44 | resp := midd.Responses[0] 45 | if resp.Ref != "" { 46 | resp = g.api.Responses[resp.Ref] 47 | } 48 | 49 | if resp.Name != req.Name { 50 | continue 51 | } 52 | 53 | if resp.Type.Ref != req.Type.Ref { 54 | continue 55 | } 56 | 57 | midds = append(midds, midd) 58 | } 59 | switch len(midds) { 60 | default: 61 | g.buf.WriteFormat(` 62 | // Permission middleware undefined %s. 63 | `, req.Name) 64 | case 1: 65 | midd := midds[0] 66 | name := g.getMiddlewareFunctionName(midd) 67 | g.buf.WriteFormat(` 68 | // Permission middlewares call %s. 69 | %s, %s = %s(`, midd.Name, vname, errName, name) 70 | if midd.Type != nil { 71 | g.buf.WriteString(`s, `) 72 | } 73 | g.buf.WriteFormat(`w, r) 74 | if %s != nil { 75 | return 76 | } 77 | `, errName) 78 | } 79 | case "security": 80 | secus := []*spec.Security{} 81 | secuKey := make([]string, 0, len(g.api.Securitys)) 82 | for k := range g.api.Securitys { 83 | secuKey = append(secuKey, k) 84 | } 85 | sort.Strings(secuKey) 86 | for _, k := range secuKey { 87 | secu := g.api.Securitys[k] 88 | if len(secu.Responses) == 0 { 89 | continue 90 | } 91 | resp := secu.Responses[0] 92 | if resp.Ref != "" { 93 | resp = g.api.Responses[resp.Ref] 94 | } 95 | 96 | if resp.Name != req.Name { 97 | continue 98 | } 99 | secus = append(secus, secu) 100 | } 101 | switch len(secus) { 102 | case 0: 103 | g.buf.WriteFormat(` 104 | // Permission verification undefined. 105 | var %s `, vname) 106 | g.Types(req.Type) 107 | g.generateResponseErrorReturn(errName, "401", false) 108 | default: 109 | g.buf.WriteFormat(` 110 | // Permission verification 111 | `) 112 | for _, secu := range secus { 113 | name := g.getSecurityFunctionName(secu) 114 | 115 | switch secu.Schema { 116 | case "basic": 117 | g.buf.AddImport("", "strings") 118 | g.buf.WriteFormat(`if strings.HasPrefix(r.Header.Get("Authorization"), "Basic ") { // Call %s. 119 | %s, %s = %s(`, secu.Name, vname, errName, name) 120 | 121 | case "bearer": 122 | g.buf.AddImport("", "strings") 123 | g.buf.WriteFormat(`if strings.HasPrefix(r.Header.Get("Authorization"), "Bearer ") { // Call %s. 124 | %s, %s = %s(`, secu.Name, vname, errName, name) 125 | 126 | case "apiKey": 127 | req := secu.Requests[0] 128 | if req.Ref != "" { 129 | req = g.api.Requests[req.Ref] 130 | } 131 | 132 | switch req.In { 133 | default: 134 | case "header": 135 | g.buf.WriteFormat(`if r.Header.Get("%s") != "" { // Call %s. 136 | %s, %s = %s(`, req.Name, secu.Name, vname, errName, name) 137 | case "query": 138 | g.buf.WriteFormat(`if r.URL.Query().Get("%s") != "" { // Call %s. 139 | %s, %s = %s(`, req.Name, secu.Name, vname, errName, name) 140 | case "cookie": 141 | g.buf.WriteFormat(`if cookie, err := r.Cookie("%s"); err == nil && cookie.Value != "" { // Call %s. 142 | %s, %s = %s(`, req.Name, secu.Name, vname, errName, name) 143 | } 144 | 145 | } 146 | if secu.Type != nil { 147 | g.buf.WriteString(`s, `) 148 | } 149 | g.buf.WriteString(`w, r) 150 | } else `) 151 | } 152 | g.buf.AddImport("", "errors") 153 | g.buf.WriteFormat(`{ 154 | %s = errors.New("Unauthorized") 155 | }`, errName) 156 | g.generateResponseError(errName, "401", false) 157 | } 158 | default: 159 | g.buf.WriteFormat(` 160 | // Parsing %s. 161 | %s, %s = %s(w, r)`, req.Name, vname, errName, g.getRequestFunctionName(req)) 162 | g.buf.WriteFormat(` 163 | if %s != nil { 164 | return 165 | } 166 | `, errName) 167 | } 168 | 169 | return nil 170 | } 171 | 172 | func (g *GenRoute) generateRequestFunction(req *spec.Request) error { 173 | g.buf.AddImport("", "net/http") 174 | 175 | name := g.getRequestFunctionName(req) 176 | 177 | if g.only[name] { 178 | return nil 179 | } 180 | g.only[name] = true 181 | 182 | g.buf.WriteFormat(` 183 | // %s Parsing the %s for of %s 184 | func %s(w http.ResponseWriter, r *http.Request) (%s `, name, req.In, req.Name, name, g.getVarName(req.Name, req.Type)) 185 | g.Types(req.Type) 186 | g.buf.WriteString(`, err error) { 187 | `) 188 | err := g.generateRequestVar(req) 189 | if err != nil { 190 | return err 191 | } 192 | g.buf.WriteString(` 193 | 194 | return 195 | }`) 196 | return nil 197 | } 198 | 199 | func (g *GenRoute) generateRequestVar(req *spec.Request) error { 200 | 201 | name := g.getVarName(req.Name, req.Type) 202 | switch req.In { 203 | case "body": 204 | g.buf.WriteFormat(` 205 | defer r.Body.Close() 206 | `) 207 | switch req.Content { 208 | case "json": 209 | g.buf.AddImport("", "io/ioutil") 210 | g.buf.AddImport("", "encoding/json") 211 | g.buf.WriteFormat(` 212 | var _%s []byte 213 | _%s, err = ioutil.ReadAll(r.Body)`, name, name) 214 | g.generateResponseError("err", "400", false) 215 | g.buf.WriteFormat(` 216 | err = json.Unmarshal(_%s, &%s)`, name, name) 217 | g.generateResponseError("err", "400", false) 218 | case "xml": 219 | g.buf.AddImport("", "io/ioutil") 220 | g.buf.AddImport("", "encoding/xml") 221 | g.buf.WriteFormat(` 222 | var _%s []byte 223 | _%s, err = ioutil.ReadAll(r.Body)`, name, name) 224 | g.generateResponseError("err", "400", false) 225 | g.buf.WriteFormat(` 226 | err = xml.Unmarshal(_%s, &%s)`, name, name) 227 | g.generateResponseError("err", "400", false) 228 | case "formdata": 229 | g.buf.WriteFormat(` 230 | if _%s := r.MultipartForm.File["%s"]; len(_%s) != 0 { 231 | %s, err = _%s[0].Open()`, name, name, name, name, name) 232 | g.generateResponseError("err", "400", false) 233 | g.buf.WriteFormat(` 234 | } 235 | `) 236 | case "file": 237 | 238 | g.buf.AddImport("", "io") 239 | g.buf.AddImport("", "bytes") 240 | g.buf.AddImport("", "strings") 241 | g.buf.WriteFormat(` 242 | body := r.Body 243 | contentType := r.Header.Get("Content-Type") 244 | if strings.HasPrefix(contentType, "multipart/form-data") { 245 | if r.MultipartForm == nil { 246 | err = r.ParseMultipartForm(10<<20) 247 | if err != nil { 248 | return 249 | } 250 | } 251 | file := r.MultipartForm.File["%s"] 252 | if len(file) != 0 { 253 | body, err = file[0].Open() 254 | if err != nil { 255 | return 256 | } 257 | } 258 | } 259 | 260 | _%s := bytes.NewBuffer(nil) 261 | _, err = io.Copy(_%s, body)`, req.Name, name, name) 262 | g.generateResponseError("err", "400", false) 263 | g.buf.WriteFormat(` 264 | %s = _%s 265 | `, name, name) 266 | case "image": 267 | g.buf.AddImport("", "image") 268 | g.buf.AddImport("_", "image/jpeg") 269 | g.buf.AddImport("_", "image/png") 270 | g.buf.AddImport("", "strings") 271 | g.buf.WriteFormat(` 272 | body := r.Body 273 | defer r.Body.Close() 274 | contentType := r.Header.Get("Content-Type") 275 | if strings.HasPrefix(contentType, "multipart/form-data") { 276 | if r.MultipartForm == nil { 277 | err = r.ParseMultipartForm(10<<20) 278 | if err != nil { 279 | return 280 | } 281 | } 282 | file := r.MultipartForm.File["%s"] 283 | if len(file) != 0 { 284 | body, err = file[0].Open() 285 | if err != nil { 286 | return 287 | } 288 | } 289 | } 290 | 291 | %s, _, err = image.Decode(body)`, req.Name, name) 292 | g.generateResponseError("err", "400", false) 293 | } 294 | case "cookie": 295 | g.buf.AddImport("", "net/http") 296 | g.buf.WriteFormat(` 297 | var cookie *http.Cookie 298 | cookie, err = r.Cookie("%s")`, req.Name) 299 | g.generateResponseError("err", "400", false) 300 | g.GenModel.ConvertTo(`cookie.Value`, name, req.Type) 301 | g.generateResponseError("err", "400", false) 302 | case "query": 303 | varName := g.getVarName("raw_"+name, req.Type) 304 | g.buf.WriteFormat(` 305 | var %s = r.URL.Query()["%s"] 306 | `, varName, req.Name) 307 | g.GenModel.ConvertToMulti(varName, name, req.Type, g.explode) 308 | g.generateResponseError("err", "400", false) 309 | case "header": 310 | varName := g.getVarName("raw_"+name, req.Type) 311 | g.buf.WriteFormat(` 312 | var %s = r.Header.Get("%s") 313 | `, varName, req.Name) 314 | g.GenModel.ConvertTo(varName, name, req.Type) 315 | g.generateResponseError("err", "400", false) 316 | case "path": 317 | varName := g.getVarName("raw_"+name, req.Type) 318 | g.buf.WriteFormat(` 319 | var %s = mux.Vars(r)["%s"] 320 | `, varName, req.Name) 321 | g.GenModel.ConvertTo(varName, name, req.Type) 322 | g.generateResponseError("err", "400", false) 323 | 324 | default: 325 | return fmt.Errorf("undefine in %s", req.In) 326 | } 327 | 328 | return nil 329 | } 330 | -------------------------------------------------------------------------------- /route/response.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strconv" 7 | 8 | "github.com/wzshiming/gen/spec" 9 | ) 10 | 11 | func (g *GenRoute) generateResponsesErrorName(resps []*spec.Response) (string, error) { 12 | for i := 0; i != len(resps); i++ { 13 | resp := resps[len(resps)-i-1] 14 | if resp.Ref != "" { 15 | resp = g.api.Responses[resp.Ref] 16 | } 17 | if resp.Type == nil { 18 | continue 19 | } 20 | if resp.Type.Kind == spec.Error { 21 | return g.getVarName(resp.Name, resp.Type), nil 22 | } 23 | } 24 | g.buf.WriteFormat("var err error") 25 | return "err", nil 26 | } 27 | 28 | func (g *GenRoute) generateResponsesVar(name string, resps []*spec.Response) error { 29 | 30 | for _, resp := range resps { 31 | if resp.Ref != "" { 32 | resp = g.api.Responses[resp.Ref] 33 | } 34 | if resp.Type == nil { 35 | continue 36 | } 37 | 38 | g.buf.WriteFormat(`// responses %s %s.%s 39 | var %s `, resp.Type.PkgPath, name, resp.Name, g.getVarName(resp.Name, resp.Type)) 40 | g.Types(resp.Type) 41 | g.buf.WriteString("\n") 42 | } 43 | 44 | return nil 45 | } 46 | 47 | func (g *GenRoute) generateResponses(resps []*spec.Response, code string, errName string) error { 48 | noCtx := true 49 | switch len(resps) { 50 | case 1: 51 | resp := resps[0] 52 | if resp.Ref != "" { 53 | resp = g.api.Responses[resp.Ref] 54 | } 55 | typ := resp.Type 56 | if typ.Ref != "" { 57 | typ = g.api.Types[typ.Ref] 58 | } 59 | if typ.Kind != spec.Error { 60 | noCtx = false 61 | } 62 | case 0: 63 | // No action 64 | default: 65 | for _, resp := range resps { 66 | if resp.Ref != "" { 67 | resp = g.api.Responses[resp.Ref] 68 | } 69 | if resp.In == "body" && resp.Content != "error" { 70 | g.generateResponseBodyItem(resp.Name, resp.Type, code, resp.Content, errName, false) 71 | noCtx = false 72 | break 73 | } 74 | } 75 | } 76 | if noCtx { 77 | g.buf.WriteFormat(` 78 | w.Header().Set("Content-Type", "application/json; charset=utf-8") 79 | w.WriteHeader(%s) 80 | w.Write([]byte("null")) 81 | `, code) 82 | } 83 | g.buf.WriteString(` 84 | return 85 | } 86 | `) 87 | return nil 88 | } 89 | 90 | func (g *GenRoute) generateResponse(resp *spec.Response, errName string) error { 91 | if resp.Ref != "" { 92 | resp = g.api.Responses[resp.Ref] 93 | } 94 | switch resp.In { 95 | case "header": 96 | return g.generateResponseHeader(resp) 97 | case "body": 98 | return g.generateResponseBody(resp, errName) 99 | } 100 | return nil 101 | } 102 | 103 | func (g *GenRoute) generateResponseHeader(resp *spec.Response) error { 104 | g.buf.AddImport("", "fmt") 105 | g.buf.WriteFormat(` 106 | w.Header().Set("%s",fmt.Sprint(%s)) 107 | `, resp.Name, g.getVarName(resp.Name, resp.Type)) 108 | return nil 109 | } 110 | 111 | func (g *GenRoute) generateResponseBody(resp *spec.Response, errName string) error { 112 | text := "" 113 | if i, err := strconv.Atoi(resp.Code); err == nil { 114 | text = http.StatusText(i) 115 | } 116 | g.buf.WriteFormat(` 117 | // Response code %s %s for %s. 118 | if %s != `, resp.Code, text, resp.Name, g.getVarName(resp.Name, resp.Type)) 119 | g.TypesZero(resp.Type) 120 | g.buf.WriteString(`{`) 121 | g.generateResponseBodyItem(resp.Name, resp.Type, resp.Code, resp.Content, errName, false) 122 | g.buf.WriteString(`return 123 | } 124 | `) 125 | return nil 126 | } 127 | 128 | func (g *GenRoute) generateResponseBodyItem(respName string, respType *spec.Type, respCode string, respContent string, errName string, noFmtErr bool) error { 129 | contentType := "" 130 | name := g.getVarName(respName, respType) 131 | 132 | switch respContent { 133 | case "json": 134 | g.buf.AddImport("", "encoding/json") 135 | contentType = "\"application/json; charset=utf-8\"" 136 | g.buf.WriteFormat(` 137 | var _%s []byte 138 | _%s, %s = json.Marshal(%s)`, name, name, errName, name) 139 | g.generateResponseError(errName, "500", noFmtErr) 140 | case "xml": 141 | g.buf.AddImport("", "encoding/xml") 142 | contentType = "\"application/xml; charset=utf-8\"" 143 | g.buf.WriteFormat(` 144 | var _%s []byte 145 | _%s, %s = xml.Marshal(%s)`, name, name, errName, name) 146 | g.generateResponseError(errName, "500", noFmtErr) 147 | case "error": 148 | g.generateResponseErrorReturn(name, respCode, noFmtErr) 149 | return nil 150 | default: 151 | typ := respType 152 | if typ.Attr.Has(spec.AttrReader) { 153 | g.buf.AddImport("", "io/ioutil") 154 | g.buf.WriteFormat(` 155 | var _%s []byte 156 | _%s, %s = ioutil.ReadAll(%s) 157 | `, name, name, errName, name) 158 | } else if typ.Attr.Has(spec.AttrTextMarshaler) { 159 | g.buf.WriteFormat(` 160 | var _%s []byte 161 | _%s, %s = %s.MarshalText() 162 | `, name, name, errName, name) 163 | } else if typ.Kind == spec.Slice && typ.Elem.Kind == spec.Byte { 164 | g.buf.WriteFormat(` 165 | var _%s []byte 166 | _%s = %s 167 | `, name, name, name) 168 | } else { 169 | g.buf.AddImport("", "unsafe") 170 | g.buf.WriteFormat(` 171 | var _%s []byte 172 | var __%s string 173 | __%s = fmt.Sprint(%s) 174 | _%s = *(*[]byte)(unsafe.Pointer(&__%s)) 175 | `, name, name, name, name, name, name) 176 | } 177 | 178 | switch respContent { 179 | case "": 180 | contentType = "\"text/plain; charset=utf-8\"" 181 | case "file": 182 | g.buf.AddImport("", "net/http") 183 | contentType = fmt.Sprintf("http.DetectContentType(_%s)", name) 184 | default: 185 | contentType = strconv.Quote(respContent) 186 | } 187 | 188 | } 189 | 190 | g.buf.WriteFormat(` 191 | w.Header().Set("Content-Type", %s)`, contentType) 192 | 193 | g.buf.WriteFormat(` 194 | w.WriteHeader(%s)`, respCode) 195 | 196 | switch respContent { 197 | case "xml": 198 | g.buf.AddImport("", "io") 199 | g.buf.WriteFormat(` 200 | io.WriteString(w, xml.Header)`) 201 | } 202 | 203 | g.buf.WriteFormat(` 204 | w.Write(_%s) 205 | `, name) 206 | 207 | return nil 208 | } 209 | -------------------------------------------------------------------------------- /route/route.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "encoding/json" 5 | "sort" 6 | "strings" 7 | 8 | "github.com/wzshiming/gen/model" 9 | "github.com/wzshiming/gen/named" 10 | "github.com/wzshiming/gen/spec" 11 | "github.com/wzshiming/gen/srcgen" 12 | "github.com/wzshiming/openapi/util" 13 | ) 14 | 15 | // GenClient is the generating generating 16 | type GenRoute struct { 17 | model.GenModel 18 | api *spec.API 19 | apiInterface interface{} 20 | buf *srcgen.File 21 | only map[string]bool 22 | named *named.Named 23 | explode bool 24 | } 25 | 26 | func NewGenRoute(api *spec.API) *GenRoute { 27 | buf := &srcgen.File{} 28 | return &GenRoute{ 29 | api: api, 30 | buf: buf, 31 | only: map[string]bool{}, 32 | named: named.NewNamed("_"), 33 | } 34 | } 35 | 36 | func (g *GenRoute) SetExplode(b bool) *GenRoute { 37 | g.explode = b 38 | return g 39 | } 40 | 41 | func (g *GenRoute) SetBuildIgnore(b bool) *GenRoute { 42 | g.buf.SetBuildIgnore(b) 43 | return g 44 | } 45 | 46 | func (g *GenRoute) Generate(pkg, outpkg, funcName string) (*srcgen.File, error) { 47 | g.buf.WithPackname(pkg) 48 | g.GenModel = *model.NewGenModel(g.api, g.buf, []string{outpkg}) 49 | 50 | err := g.generateNotFound("notFoundHandler") 51 | if err != nil { 52 | return nil, err 53 | } 54 | err = g.generateRoutes(funcName) 55 | if err != nil { 56 | return nil, err 57 | } 58 | if g.apiInterface != nil { 59 | err := g.generateOpenAPI() 60 | if err != nil { 61 | return nil, err 62 | } 63 | } 64 | return g.buf, nil 65 | } 66 | 67 | func (g *GenRoute) generateOpenAPI() (err error) { 68 | dj, err := json.Marshal(g.apiInterface) 69 | if err != nil { 70 | return err 71 | } 72 | dy, err := util.JSON2YAML(dj) 73 | if err != nil { 74 | return err 75 | } 76 | oay := strings.Replace(string(dy), "`", "`+\"`\"+`", -1) 77 | oaj := strings.Replace(string(dj), "`", "`+\"`\"+`", -1) 78 | g.buf.WriteFormat("var OpenAPI4YAML=[]byte(`%s`)\n", oay) 79 | g.buf.WriteFormat("var OpenAPI4JSON=[]byte(`%s`)\n", oaj) 80 | 81 | g.buf.AddImport("", "github.com/wzshiming/openapiui") 82 | g.buf.AddImport("", "github.com/wzshiming/openapiui/swaggerui") 83 | g.buf.AddImport("", "github.com/wzshiming/openapiui/swaggereditor") 84 | g.buf.AddImport("", "github.com/wzshiming/openapiui/redoc") 85 | g.buf.AddImport("", "github.com/gorilla/mux") 86 | 87 | g.buf.WriteString(` 88 | // RouteOpenAPI 89 | func RouteOpenAPI(router *mux.Router) *mux.Router { 90 | openapi := map[string][]byte { 91 | "openapi.json": OpenAPI4JSON, 92 | "openapi.yml": OpenAPI4YAML, 93 | "openapi.yaml": OpenAPI4YAML, 94 | } 95 | router.PathPrefix("/swagger/").Handler(http.StripPrefix("/swagger", openapiui.HandleWithFiles(openapi, swaggerui.Asset))) 96 | router.PathPrefix("/swaggerui/").Handler(http.StripPrefix("/swaggerui", openapiui.HandleWithFiles(openapi, swaggerui.Asset))) 97 | router.PathPrefix("/swaggereditor/").Handler(http.StripPrefix("/swaggereditor", openapiui.HandleWithFiles(openapi, swaggereditor.Asset))) 98 | router.PathPrefix("/redoc/").Handler(http.StripPrefix("/redoc", openapiui.HandleWithFiles(openapi, redoc.Asset))) 99 | return router 100 | } 101 | `) 102 | return nil 103 | } 104 | 105 | func (g *GenRoute) WithOpenAPI(api interface{}) *GenRoute { 106 | g.apiInterface = api 107 | return g 108 | } 109 | 110 | func (g *GenRoute) generateSubRoutes(basePath string, op []*spec.Operation) (err error) { 111 | 112 | sort.Slice(op, func(i, j int) bool { 113 | io := op[i].Path 114 | jo := op[j].Path 115 | if io == jo { 116 | return true 117 | } 118 | ip := strings.Count(io, "{") 119 | jp := strings.Count(jo, "{") 120 | if ip == jp { 121 | return io > jo 122 | } 123 | return ip < jp 124 | }) 125 | 126 | name := g.getVarName("route_"+basePath, op[0].Type) 127 | 128 | g.buf.WriteFormat(` 129 | %s := router.PathPrefix("%s").Subrouter() 130 | if len(fs) != 0 { 131 | %s.Use(fs...) 132 | } 133 | `, name, basePath, name) 134 | 135 | for _, v := range op { 136 | typ := v.Type 137 | if typ.Ref != "" { 138 | typ = g.api.Types[typ.Ref] 139 | } 140 | 141 | err = g.generateRoute(name, v) 142 | if err != nil { 143 | return err 144 | } 145 | } 146 | 147 | return nil 148 | } 149 | 150 | func (g *GenRoute) generateRouteFunc(typ *spec.Type, op []*spec.Operation) (err error) { 151 | name := g.getRouteName(typ) 152 | g.buf.WriteFormat(` 153 | // %s is routing for %s 154 | func %s(router *mux.Router, %s `, name, typ.Name, name, g.getVarName("", typ)) 155 | g.PtrTypes(typ) 156 | g.buf.WriteFormat(`, fs ...mux.MiddlewareFunc) *mux.Router { 157 | if router == nil { 158 | router = mux.NewRouter() 159 | } 160 | `) 161 | 162 | group := map[string][]*spec.Operation{} 163 | sortGroup := []string{} 164 | for _, v := range op { 165 | if group[v.BasePath] == nil { 166 | sortGroup = append(sortGroup, v.BasePath) 167 | } 168 | group[v.BasePath] = append(group[v.BasePath], v) 169 | } 170 | 171 | sort.Slice(sortGroup, func(i, j int) bool { 172 | io := sortGroup[i] 173 | jo := sortGroup[j] 174 | if io == jo { 175 | return true 176 | } 177 | ip := strings.Count(io, "{") 178 | jp := strings.Count(jo, "{") 179 | if ip == jp { 180 | return io > jo 181 | } 182 | return ip < jp 183 | }) 184 | 185 | for _, basePath := range sortGroup { 186 | op := group[basePath] 187 | err := g.generateSubRoutes(basePath, op) 188 | if err != nil { 189 | return err 190 | } 191 | } 192 | 193 | g.buf.WriteString(` 194 | if router.NotFoundHandler == nil { 195 | router.NotFoundHandler = http.HandlerFunc(notFoundHandler) 196 | } 197 | return router 198 | } 199 | `) 200 | return nil 201 | } 202 | 203 | func (g *GenRoute) generateRoutes(funcName string) (err error) { 204 | g.buf.AddImport("", "github.com/gorilla/mux") 205 | g.buf.AddImport("", "net/http") 206 | 207 | m := map[string]bool{} 208 | g.buf.WriteFormat(` 209 | // %s is all routing for package 210 | // generated do not edit. 211 | func %s() http.Handler { 212 | router := mux.NewRouter() 213 | `, funcName, funcName) 214 | 215 | for _, v := range g.api.Operations { 216 | err = g.generateRouteTypes(v, m) 217 | if err != nil { 218 | return err 219 | } 220 | } 221 | 222 | if g.apiInterface != nil { 223 | g.buf.WriteString(` 224 | router = RouteOpenAPI(router) 225 | `) 226 | } 227 | 228 | g.buf.WriteString(` 229 | router.NotFoundHandler = http.HandlerFunc(notFoundHandler) 230 | return router 231 | } 232 | `) 233 | 234 | group := map[*spec.Type][]*spec.Operation{} 235 | sortGroup := []*spec.Type{} 236 | for _, v := range g.api.Operations { 237 | if v.Type == nil { 238 | continue 239 | } 240 | typ := v.Type 241 | if typ.Ref != "" { 242 | typ = g.api.Types[typ.Ref] 243 | } 244 | if group[typ] == nil { 245 | sortGroup = append(sortGroup, typ) 246 | } 247 | group[typ] = append(group[typ], v) 248 | } 249 | 250 | for _, typ := range sortGroup { 251 | op := group[typ] 252 | err := g.generateRouteFunc(typ, op) 253 | if err != nil { 254 | return err 255 | } 256 | } 257 | 258 | reqKey := make([]string, 0, len(g.api.Requests)) 259 | for k := range g.api.Requests { 260 | reqKey = append(reqKey, k) 261 | } 262 | sort.Strings(reqKey) 263 | for _, k := range reqKey { 264 | v := g.api.Requests[k] 265 | switch v.In { 266 | case "security": 267 | case "middleware": 268 | case "wrapping": 269 | case "none": 270 | default: 271 | err = g.generateRequestFunction(v) 272 | if err != nil { 273 | return err 274 | } 275 | } 276 | } 277 | 278 | { 279 | middKey := make([]string, 0, len(g.api.Middlewares)) 280 | for k := range g.api.Middlewares { 281 | middKey = append(middKey, k) 282 | } 283 | sort.Strings(middKey) 284 | 285 | for _, k := range middKey { 286 | v := g.api.Middlewares[k] 287 | err = g.generateMiddlewareFunction(v) 288 | if err != nil { 289 | return err 290 | } 291 | } 292 | } 293 | 294 | { 295 | wrapKey := make([]string, 0, len(g.api.Wrappings)) 296 | for k := range g.api.Wrappings { 297 | wrapKey = append(wrapKey, k) 298 | } 299 | sort.Strings(wrapKey) 300 | 301 | for _, k := range wrapKey { 302 | v := g.api.Wrappings[k] 303 | err = g.generateWrappingFunction(v) 304 | if err != nil { 305 | return err 306 | } 307 | } 308 | } 309 | 310 | { 311 | secuKey := make([]string, 0, len(g.api.Securitys)) 312 | for k := range g.api.Securitys { 313 | secuKey = append(secuKey, k) 314 | } 315 | sort.Strings(secuKey) 316 | for _, k := range secuKey { 317 | secu := g.api.Securitys[k] 318 | err = g.generateSecurityFunction(secu) 319 | if err != nil { 320 | return err 321 | } 322 | } 323 | } 324 | 325 | for _, v := range g.api.Operations { 326 | err = g.generateOperationFunction(v) 327 | if err != nil { 328 | return err 329 | } 330 | } 331 | 332 | return 333 | } 334 | 335 | func (g *GenRoute) generateRouteTypes(oper *spec.Operation, m map[string]bool) (err error) { 336 | if oper.Type == nil { 337 | return 338 | } 339 | typ := oper.Type 340 | if typ.Ref != "" { 341 | typ = g.api.Types[oper.Type.Ref] 342 | } 343 | if m[typ.Name] { 344 | return 345 | } 346 | 347 | name := g.getVarName("", typ) 348 | g.buf.WriteFormat(` 349 | // %s Define the method scope 350 | var %s `, typ.Name, name) 351 | g.Types(oper.Type) 352 | g.buf.WriteFormat(` 353 | %s(router, `, g.getRouteName(typ)) 354 | if g.Ptr(typ) { 355 | g.buf.WriteString("&") 356 | } 357 | g.buf.WriteFormat(`%s) 358 | `, name) 359 | m[typ.Name] = true 360 | return 361 | } 362 | 363 | func (g *GenRoute) generateRoute(subrouter string, oper *spec.Operation) (err error) { 364 | name := g.getOperationFunctionName(oper) 365 | 366 | methods := strings.Split(oper.Method, ",") 367 | for i := range methods { 368 | methods[i] = strings.ToUpper(methods[i]) 369 | } 370 | 371 | path := oper.Path[len(oper.BasePath):] 372 | g.buf.WriteFormat(` 373 | // Registered routing %s %s`, strings.ToUpper(oper.Method), oper.Path) 374 | if oper.Type != nil { 375 | typ := oper.Type 376 | if typ.Ref != "" { 377 | typ = g.api.Types[oper.Type.Ref] 378 | } 379 | typname := g.getVarName("", typ) 380 | g.buf.WriteFormat(` 381 | var _%s http.Handler 382 | _%s = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 383 | %s(%s, w, r) 384 | })`, name, name, name, typname) 385 | } else { 386 | g.buf.WriteFormat(` 387 | _%s := http.HandlerFunc(%s)`, name, name) 388 | } 389 | g.buf.WriteFormat(` 390 | %s.Methods("%s").Path("%s").Handler(_%s) 391 | `, subrouter, strings.Join(methods, `", "`), path, name) 392 | return 393 | } 394 | -------------------------------------------------------------------------------- /route/security.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "github.com/wzshiming/gen/spec" 5 | ) 6 | 7 | func (g *GenRoute) generateSecurityFunction(secu *spec.Security) (err error) { 8 | name := g.getSecurityFunctionName(secu) 9 | 10 | if g.only[name] { 11 | return nil 12 | } 13 | g.only[name] = true 14 | 15 | err = g.generateFunctionDefine("security", name, secu.Name, secu.Type, nil, secu.Responses) 16 | if err != nil { 17 | return err 18 | } 19 | 20 | g.buf.WriteString(`{ 21 | `) 22 | 23 | pname := secu.Name 24 | if typ := secu.Type; typ != nil { 25 | if typ.Ref != "" { 26 | typ = g.api.Types[typ.Ref] 27 | } 28 | pname = typ.Name + "." + pname 29 | } 30 | err = g.generateRequestsVar(pname, secu.Requests) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | errName, err := g.generateResponsesErrorName(secu.Responses) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | err = g.generateCallExec(secu.Name, nil, secu.PkgPath, secu.Type, secu.Requests, secu.Responses, errName, true) 41 | if err != nil { 42 | return err 43 | } 44 | g.buf.WriteString(` 45 | return 46 | } 47 | `) 48 | 49 | return 50 | } 51 | -------------------------------------------------------------------------------- /route/util.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "unsafe" 7 | 8 | "github.com/wzshiming/gen/spec" 9 | "github.com/wzshiming/namecase" 10 | ) 11 | 12 | var codeRequest = &spec.Request{ 13 | Name: "code", 14 | Type: &spec.Type{ 15 | Kind: spec.Int, 16 | }, 17 | } 18 | 19 | func (g *GenRoute) getVarName(name string, typ *spec.Type) string { 20 | if typ == nil { 21 | if name == "" { 22 | return "_" 23 | } 24 | return name 25 | } 26 | 27 | addr := strconv.FormatUint(uint64(uintptr(unsafe.Pointer(typ))), 16) 28 | 29 | if typ.Ref != "" { 30 | typ = g.api.Types[typ.Ref] 31 | } 32 | for typ != nil && (name == "_" || name == "") { 33 | if typ.Ref != "" { 34 | typ = g.api.Types[typ.Ref] 35 | } 36 | name = typ.Name 37 | typ = typ.Elem 38 | } 39 | 40 | return g.named.GetSubNamed("").GetName("_"+namecase.ToLowerHumpInitialisms(fmt.Sprintf("%s", name)), addr) 41 | } 42 | 43 | func (g *GenRoute) getRouteName(typ *spec.Type) string { 44 | name := namecase.ToUpperHumpInitialisms(fmt.Sprintf("route_%s", typ.Name)) 45 | addr := strconv.FormatUint(uint64(uintptr(unsafe.Pointer(typ))), 16) 46 | return g.named.GetName(name, addr) 47 | } 48 | 49 | func (g *GenRoute) getOperationFunctionName(oper *spec.Operation) string { 50 | name := "_" + namecase.ToLowerHumpInitialisms(fmt.Sprintf("operation_%s_%s", oper.Method, oper.Path)) 51 | addr := strconv.FormatUint(uint64(uintptr(unsafe.Pointer(oper))), 16) 52 | return g.named.GetName(name, addr) 53 | } 54 | 55 | func (g *GenRoute) getRequestFunctionName(req *spec.Request) string { 56 | name := "_" + namecase.ToLowerHumpInitialisms(fmt.Sprintf("request_%s_%s", req.In, req.Name)) 57 | addr := strconv.FormatUint(uint64(uintptr(unsafe.Pointer(req))), 16) 58 | return g.named.GetName(name, addr) 59 | } 60 | 61 | func (g *GenRoute) getSecurityFunctionName(secu *spec.Security) string { 62 | name := "_" + namecase.ToLowerHumpInitialisms(fmt.Sprintf("security_%s_%s", secu.Schema, secu.Name)) 63 | addr := strconv.FormatUint(uint64(uintptr(unsafe.Pointer(secu))), 16) 64 | return g.named.GetName(name, addr) 65 | } 66 | 67 | func (g *GenRoute) getMiddlewareFunctionName(midd *spec.Middleware) string { 68 | name := "_" + namecase.ToLowerHumpInitialisms(fmt.Sprintf("middleware_%s_%s", midd.Schema, midd.Name)) 69 | addr := strconv.FormatUint(uint64(uintptr(unsafe.Pointer(midd))), 16) 70 | return g.named.GetName(name, addr) 71 | } 72 | 73 | func (g *GenRoute) getWrappingFunctionName(wrap *spec.Wrapping) string { 74 | name := "_" + namecase.ToLowerHumpInitialisms(fmt.Sprintf("wrapping_%s_%s", wrap.Schema, wrap.Name)) 75 | addr := strconv.FormatUint(uint64(uintptr(unsafe.Pointer(wrap))), 16) 76 | return g.named.GetName(name, addr) 77 | } 78 | -------------------------------------------------------------------------------- /route/wrapping.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "github.com/wzshiming/gen/spec" 5 | ) 6 | 7 | func (g *GenRoute) generateWrappingFunction(wrap *spec.Wrapping) (err error) { 8 | name := g.getWrappingFunctionName(wrap) 9 | 10 | if g.only[name] { 11 | return nil 12 | } 13 | g.only[name] = true 14 | 15 | err = g.generateFunctionDefine("wrapping", name, wrap.Name, wrap.Type, append([]*spec.Request{codeRequest}, wrap.Requests...), nil) 16 | if err != nil { 17 | return err 18 | } 19 | 20 | g.buf.WriteString(`{ 21 | `) 22 | 23 | pname := wrap.Name 24 | if typ := wrap.Type; typ != nil { 25 | if typ.Ref != "" { 26 | typ = g.api.Types[typ.Ref] 27 | } 28 | pname = typ.Name + "." + pname 29 | } 30 | err = g.generateResponsesVar(pname, wrap.Responses) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | errName, err := g.generateResponsesErrorName(wrap.Responses) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | err = g.generateCall(wrap.Name, nil, wrap.PkgPath, wrap.Type, wrap.Requests, wrap.Responses, errName) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | codeName := g.getVarName(codeRequest.Name, codeRequest.Type) 46 | err = g.generateResponses(wrap.Responses, codeName, errName) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | return 52 | } 53 | 54 | func (g *GenRoute) generateResponseErrorReturn(errName string, code string, noFmtErr bool) error { 55 | if !noFmtErr { 56 | for _, wrap := range g.api.Wrappings { 57 | if len(wrap.Responses) == 0 { 58 | continue 59 | } 60 | resp := wrap.Responses[0] 61 | if resp.Ref != "" { 62 | resp = g.api.Responses[resp.Ref] 63 | } 64 | 65 | // if resp.Name != errName { 66 | // continue 67 | // } 68 | 69 | name := g.getWrappingFunctionName(wrap) 70 | g.buf.WriteFormat(` 71 | %s(w, r, %s, %s) 72 | `, name, code, errName) 73 | 74 | return nil 75 | } 76 | } 77 | g.buf.AddImport("", "net/http") 78 | g.buf.WriteFormat(` 79 | http.Error(w, %s.Error(), %s) 80 | `, errName, code) 81 | return nil 82 | } 83 | 84 | func (g *GenRoute) generateResponseError(errName string, code string, noFmtErr bool) error { 85 | g.buf.WriteFormat(` 86 | if %s != nil {`, errName) 87 | g.generateResponseErrorReturn(errName, code, noFmtErr) 88 | g.buf.WriteFormat(` 89 | return 90 | } 91 | `) 92 | return nil 93 | } 94 | -------------------------------------------------------------------------------- /run/run.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | "strings" 10 | 11 | "github.com/wzshiming/gen/openapi" 12 | "github.com/wzshiming/gen/parser" 13 | "github.com/wzshiming/gen/route" 14 | oaspec "github.com/wzshiming/openapi/spec" 15 | ) 16 | 17 | func Run(pkgs []string, port string, way string, explode bool) error { 18 | f, err := file(pkgs, port, way, explode) 19 | if err != nil { 20 | return err 21 | } 22 | 23 | dir := os.TempDir() 24 | pkg := filepath.Join(dir, "gen-run") 25 | err = os.MkdirAll(pkg, 0755) 26 | if err != nil { 27 | return err 28 | } 29 | defer func() { 30 | if err != nil { 31 | fmt.Fprintln(os.Stderr, "gen: workdir", pkg) 32 | } else { 33 | os.RemoveAll(dir) 34 | } 35 | }() 36 | 37 | const modFile = `module gen-run` 38 | 39 | err = ioutil.WriteFile(filepath.Join(pkg, "go.mod"), []byte(modFile), 0666) 40 | if err != nil { 41 | return err 42 | } 43 | err = ioutil.WriteFile(filepath.Join(pkg, "main.go"), f, 0666) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | command(pkg, "go", "mod", "tidy") 49 | 50 | err = command(pkg, "go", "run", "./main.go") 51 | if err != nil { 52 | return err 53 | } 54 | return nil 55 | } 56 | 57 | func command(pkg string, cmd ...string) error { 58 | c := exec.Command(cmd[0], cmd[1:]...) 59 | c.Dir = pkg 60 | c.Stderr = os.Stderr 61 | c.Stdout = os.Stdout 62 | c.Stdin = os.Stdin 63 | return c.Run() 64 | } 65 | 66 | func file(pkgs []string, port string, way string, explode bool) ([]byte, error) { 67 | def := parser.NewParser(nil) 68 | 69 | for _, pkg := range pkgs { 70 | err := def.Import(pkg, way) 71 | if err != nil { 72 | return nil, err 73 | } 74 | } 75 | 76 | server := "http://127.0.0.1" + port 77 | 78 | api, err := openapi.NewGenOpenAPI(def.API()). 79 | SetInfo(&oaspec.Info{ 80 | Title: "OpenAPI Demo", 81 | Description: "The current environment is only used for testing documents, and some interfaces do not work properly. Generated by gen run " + strings.Join(pkgs, " "), 82 | Version: "test", 83 | Contact: &oaspec.Contact{ 84 | Name: "wzshiming", 85 | URL: "https://github.com/wzshiming/gen", 86 | }, 87 | }). 88 | WithServices(server). 89 | SetExplode(explode). 90 | Generate() 91 | if err != nil { 92 | return nil, err 93 | } 94 | 95 | router, err := route.NewGenRoute(def.API()). 96 | WithOpenAPI(api). 97 | SetExplode(explode). 98 | Generate("main", ".", "Router") 99 | if err != nil { 100 | return nil, err 101 | } 102 | 103 | router.AddImport("", "net/http") 104 | router.AddImport("", "fmt") 105 | router.AddImport("", "os") 106 | router.AddImport("", "github.com/gorilla/handlers") 107 | 108 | router.WriteFormat(` 109 | 110 | func main() { 111 | mux := Router() 112 | mux0 := handlers.RecoveryHandler()(mux) 113 | mux0 = handlers.CombinedLoggingHandler(os.Stdout, mux0) 114 | 115 | fmt.Printf("Open %s/swagger/#\n") 116 | fmt.Printf(" or %s/swaggerui/#\n") 117 | fmt.Printf(" or %s/swaggereditor/#\n") 118 | fmt.Printf(" or %s/redoc/#\n") 119 | fmt.Printf(" with your browser.\n") 120 | 121 | err := http.ListenAndServe("%s", mux0) 122 | if err != nil { 123 | fmt.Println(err) 124 | } 125 | return 126 | } 127 | `, server, server, server, server, port) 128 | 129 | return router.Bytes(), nil 130 | } 131 | -------------------------------------------------------------------------------- /spec/kind.go: -------------------------------------------------------------------------------- 1 | package spec 2 | 3 | //go:generate stringer -type Kind kind.go 4 | type Kind uint8 5 | 6 | const ( 7 | Invalid Kind = iota 8 | 9 | predeclaredTypesBeg 10 | Bool 11 | Int 12 | Int8 13 | Int16 14 | Int32 15 | Int64 16 | Uint 17 | Uint8 18 | Uint16 19 | Uint32 20 | Uint64 21 | Uintptr 22 | Float32 23 | Float64 24 | Complex64 25 | Complex128 26 | String 27 | Byte 28 | Rune 29 | predeclaredTypesEnd 30 | 31 | Error 32 | 33 | Array 34 | Chan 35 | Func 36 | Interface 37 | Map 38 | Ptr 39 | Slice 40 | Struct 41 | 42 | Time 43 | Duration 44 | ) 45 | 46 | func (k Kind) IsPredeclared() bool { 47 | return k > predeclaredTypesBeg && k < predeclaredTypesEnd 48 | } 49 | -------------------------------------------------------------------------------- /spec/kind_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type Kind kind.go"; DO NOT EDIT. 2 | 3 | package spec 4 | 5 | import "strconv" 6 | 7 | const _Kind_name = "InvalidpredeclaredTypesBegBoolIntInt8Int16Int32Int64UintUint8Uint16Uint32Uint64UintptrFloat32Float64Complex64Complex128StringByteRunepredeclaredTypesEndErrorArrayChanFuncInterfaceMapPtrSliceStructTimeDuration" 8 | 9 | var _Kind_index = [...]uint8{0, 7, 26, 30, 33, 37, 42, 47, 52, 56, 61, 67, 73, 79, 86, 93, 100, 109, 119, 125, 129, 133, 152, 157, 162, 166, 170, 179, 182, 185, 190, 196, 200, 208} 10 | 11 | func (i Kind) String() string { 12 | if i >= Kind(len(_Kind_index)-1) { 13 | return "Kind(" + strconv.FormatInt(int64(i), 10) + ")" 14 | } 15 | return _Kind_name[_Kind_index[i]:_Kind_index[i+1]] 16 | } 17 | -------------------------------------------------------------------------------- /spec/spec.go: -------------------------------------------------------------------------------- 1 | package spec 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | type API struct { 8 | Imports []string 9 | Package string 10 | Operations []*Operation 11 | Middlewares map[string]*Middleware 12 | Wrappings map[string]*Wrapping 13 | Securitys map[string]*Security 14 | Requests map[string]*Request 15 | Responses map[string]*Response 16 | Types map[string]*Type 17 | } 18 | 19 | func NewAPI() *API { 20 | return &API{ 21 | Middlewares: map[string]*Middleware{}, 22 | Wrappings: map[string]*Wrapping{}, 23 | Securitys: map[string]*Security{}, 24 | Requests: map[string]*Request{}, 25 | Responses: map[string]*Response{}, 26 | Types: map[string]*Type{}, 27 | } 28 | } 29 | 30 | type Middleware struct { 31 | Ref string 32 | PkgPath string 33 | Schema string 34 | Name string 35 | Type *Type 36 | Requests []*Request 37 | Responses []*Response 38 | Description string 39 | DescriptionTag reflect.StructTag 40 | } 41 | 42 | type Wrapping struct { 43 | PkgPath string 44 | Schema string 45 | Name string 46 | Type *Type 47 | Requests []*Request 48 | Responses []*Response 49 | Description string 50 | DescriptionTag reflect.StructTag 51 | } 52 | 53 | type Security struct { 54 | // Ref string 55 | PkgPath string 56 | Schema string 57 | Name string 58 | Type *Type 59 | Requests []*Request 60 | Responses []*Response 61 | Description string 62 | DescriptionTag reflect.StructTag 63 | } 64 | 65 | type Operation struct { 66 | PkgPath string 67 | Method string 68 | BasePath string 69 | Path string 70 | Tags []string 71 | Name string 72 | Type *Type 73 | Chain []string 74 | Requests []*Request 75 | Responses []*Response 76 | Securitys []*Security 77 | 78 | Deprecated bool 79 | Summary string 80 | Description string 81 | DescriptionTag reflect.StructTag 82 | } 83 | 84 | type Request struct { 85 | Ref string 86 | Ident string 87 | Name string 88 | In string 89 | Content string 90 | Type *Type 91 | Description string 92 | DescriptionTag reflect.StructTag 93 | } 94 | 95 | type Response struct { 96 | Ref string 97 | Ident string 98 | Name string 99 | In string 100 | Code string 101 | Content string 102 | Type *Type 103 | Description string 104 | DescriptionTag reflect.StructTag 105 | } 106 | 107 | type Type struct { 108 | Ref string 109 | Ident string 110 | PkgPath string 111 | Name string 112 | Kind Kind 113 | Key *Type 114 | Elem *Type 115 | Fields []*Field 116 | Len int 117 | Enum []*Enum 118 | Description string 119 | DescriptionTag reflect.StructTag 120 | Attr Attr 121 | } 122 | 123 | type Field struct { 124 | Name string 125 | Type *Type 126 | Tag reflect.StructTag 127 | Anonymous bool 128 | Description string 129 | DescriptionTag reflect.StructTag 130 | } 131 | 132 | type Enum struct { 133 | Name string 134 | Value string 135 | Description string 136 | DescriptionTag reflect.StructTag 137 | } 138 | 139 | type Attr uint64 140 | 141 | const ( 142 | AttrRoot Attr = 1 << (63 - iota) 143 | AttrJSONUnmarshaler 144 | AttrJSONMarshaler 145 | AttrTextUnmarshaler 146 | AttrTextMarshaler 147 | AttrReader 148 | AttrImage 149 | ) 150 | 151 | func (a Attr) HasOne(b Attr) bool { 152 | return a&b != 0 153 | } 154 | 155 | func (a Attr) Has(b Attr) bool { 156 | return a&b == b 157 | } 158 | 159 | func (a *Attr) Add(b Attr) { 160 | *a |= b 161 | } 162 | -------------------------------------------------------------------------------- /srcgen/file.go: -------------------------------------------------------------------------------- 1 | package srcgen 2 | 3 | import ( 4 | "go/build" 5 | "io/ioutil" 6 | "os" 7 | "path" 8 | "path/filepath" 9 | "strings" 10 | "unsafe" 11 | 12 | "github.com/wzshiming/namecase" 13 | "golang.org/x/tools/imports" 14 | ) 15 | 16 | type File struct { 17 | srcgen 18 | filename string 19 | packname string 20 | imports map[string]string 21 | buildIgnore bool 22 | } 23 | 24 | func NewFile() *File { 25 | return &File{} 26 | } 27 | 28 | func (f *File) Save() error { 29 | if f.filename == "" { 30 | f.filename = "auto_gen.go" 31 | } 32 | 33 | if f.packname == "" { 34 | dir := filepath.Dir(f.filename) 35 | b, err := build.ImportDir(dir, 0) 36 | if err != nil { 37 | _, dir = filepath.Split(dir) 38 | if dir == "." || dir == ".." || dir == "" { 39 | f.packname = "main" 40 | } else { 41 | f.packname = dir 42 | } 43 | } else { 44 | f.packname = b.Name 45 | } 46 | f.packname = namecase.ToLowerSnake(f.packname) 47 | } 48 | 49 | err := os.MkdirAll(filepath.Dir(f.filename), 0755) 50 | if err != nil { 51 | return err 52 | } 53 | return ioutil.WriteFile(f.filename, f.Bytes(), 0666) 54 | } 55 | 56 | func (f *File) WithPackname(packname string) *File { 57 | f.packname = packname 58 | return f 59 | } 60 | 61 | func (f *File) WithFilename(filename string) *File { 62 | f.filename = filename 63 | return f 64 | } 65 | 66 | func (f *File) AddImport(aliase, importpath string) *File { 67 | if f.imports == nil { 68 | f.imports = map[string]string{} 69 | } 70 | if aliase == "" { 71 | _, aliase = path.Split(importpath) 72 | aliase = strings.SplitN(aliase, ".", 2)[0] 73 | } 74 | if aliase == "_" { 75 | _, ok := f.imports[importpath] 76 | if ok { 77 | return f 78 | } 79 | } 80 | f.imports[importpath] = aliase 81 | return f 82 | } 83 | 84 | func (f *File) SetBuildIgnore(b bool) *File { 85 | f.buildIgnore = b 86 | return f 87 | } 88 | 89 | func (f *File) Bytes() []byte { 90 | buf := srcgen{} 91 | if f.buildIgnore { 92 | buf.WriteFormat(`// +build ignore 93 | `) 94 | } 95 | buf.WriteFormat(`// Code generated; DO NOT EDIT. 96 | // file %s 97 | 98 | package %s 99 | 100 | `, f.filename, f.packname) 101 | 102 | if len(f.imports) != 0 { 103 | buf.WriteFormat("import(\n") 104 | for path, aliase := range f.imports { 105 | buf.WriteFormat("%s \"%s\"\n", aliase, path) 106 | } 107 | buf.WriteFormat(")\n\n") 108 | } 109 | 110 | buf.WriteString(f.srcgen.String()) 111 | 112 | data := buf.Bytes() 113 | 114 | dataf, err := imports.Process(f.filename, data, nil) 115 | if err != nil { 116 | return data 117 | } 118 | return dataf 119 | } 120 | 121 | func (f *File) String() string { 122 | data := f.Bytes() 123 | return *(*string)(unsafe.Pointer(&data)) 124 | } 125 | -------------------------------------------------------------------------------- /srcgen/srcgen.go: -------------------------------------------------------------------------------- 1 | package srcgen 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "unsafe" 7 | ) 8 | 9 | type srcgen struct { 10 | bytes.Buffer 11 | } 12 | 13 | func (s *srcgen) WriteFormat(str string, a ...interface{}) error { 14 | _, err := s.Buffer.WriteString(fmt.Sprintf(str, a...)) 15 | if err != nil { 16 | return err 17 | } 18 | return nil 19 | } 20 | 21 | func (s *srcgen) String() string { 22 | data := s.Buffer.Bytes() 23 | return *(*string)(unsafe.Pointer(&data)) 24 | } 25 | 26 | func (s *srcgen) Bytes() []byte { 27 | return s.Buffer.Bytes() 28 | } 29 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "crypto/md5" 6 | "encoding/hex" 7 | "go/build" 8 | "io/ioutil" 9 | "os" 10 | "path" 11 | "reflect" 12 | "strings" 13 | ) 14 | 15 | func PackageOmitted(pkgpath string) []string { 16 | dirs := build.Default.SrcDirs() 17 | pkgpath = strings.TrimSuffix(pkgpath, "/...") 18 | 19 | isLocal := strings.HasPrefix(pkgpath, "./") 20 | 21 | dir := "" 22 | for _, d := range dirs { 23 | fi, err := os.Stat(path.Join(d, pkgpath)) 24 | if err == nil && fi.IsDir() { 25 | dir = d 26 | break 27 | } 28 | } 29 | 30 | pkgs := []string{pkgpath} 31 | outs := []string{} 32 | for i := 0; i != len(pkgs); i++ { 33 | pkg := pkgs[i] 34 | fis, err := ioutil.ReadDir(path.Join(dir, pkg)) 35 | if err != nil { 36 | continue 37 | } 38 | 39 | curr := true 40 | for _, fi := range fis { 41 | name := fi.Name() 42 | if fi.IsDir() { 43 | pkgs = append(pkgs, path.Join(pkg, name)) 44 | } else if curr && strings.HasSuffix(name, ".go") { 45 | if isLocal { 46 | pkg = "./" + pkg 47 | } 48 | outs = append(outs, pkg) 49 | curr = false 50 | } 51 | } 52 | } 53 | return outs 54 | } 55 | 56 | func GetPackagePath(path string) string { 57 | const ( 58 | src = "/src/" 59 | vendor = "/vendor/" 60 | ) 61 | if index := strings.LastIndex(path, vendor); index != -1 { 62 | return path[index+len(vendor):] 63 | } 64 | if index := strings.Index(path, src); index != -1 { 65 | return path[index+len(src):] 66 | } 67 | return strings.TrimLeft(path, "/") 68 | } 69 | 70 | func MergeLine(t string) string { 71 | return strings.TrimSpace(strings.Replace(t, "\n", " ", -1)) 72 | } 73 | 74 | func CommentLine(t string) string { 75 | t = strings.TrimSpace(t) 76 | if t == "" { 77 | return "\n" 78 | } 79 | return "// " + strings.Join(strings.Split(t, "\n"), "\n// ") + "\n" 80 | } 81 | 82 | func Hash(s ...string) string { 83 | h := md5.New() 84 | for _, v := range s { 85 | h.Write([]byte(v)) 86 | } 87 | return hex.EncodeToString(h.Sum(nil)[:]) 88 | } 89 | 90 | // GetTag [#[^#]+#]... 91 | func GetTag(text string) (string, reflect.StructTag) { 92 | text = strings.TrimSpace(text) 93 | if text == "" { 94 | return "", "" 95 | } 96 | ss := []string{} 97 | other := bytes.NewBuffer(nil) 98 | for _, text := range strings.Split(text, "\n") { 99 | if text == "" { 100 | continue 101 | } 102 | prev := -1 103 | for i, v := range text { 104 | if v != '#' { 105 | if prev == -1 { 106 | other.WriteRune(v) 107 | } 108 | continue 109 | } 110 | if prev == -1 { 111 | prev = i 112 | } else { 113 | ss = append(ss, strings.TrimSpace(text[prev+1:i])) 114 | prev = -1 115 | } 116 | } 117 | other.WriteRune('\n') 118 | } 119 | 120 | tag := strings.Join(ss, "#\n#") 121 | if tag != "" { 122 | other.WriteString("\n#") 123 | other.WriteString(tag) 124 | other.WriteRune('#') 125 | } 126 | return other.String(), reflect.StructTag(strings.Join(ss, " ")) 127 | } 128 | --------------------------------------------------------------------------------