Merge pull request #1121 from sagikazarmark/remove-kitgen
Remove deprecated kitgen
Márk Sági-Kazár authored 2 years ago
GitHub committed 2 years ago
0 | **DEPRECATED** | |
1 | ||
2 | kitgen is no longer maintained. | |
3 | ||
4 | # kitgen | |
5 | ||
6 | kitgen is an experimental code generation utility that helps with some of the | |
7 | boilerplate code required to implement the "onion" pattern `go-kit` utilizes. | |
8 | ||
9 | ## Usage | |
10 | Before using this tool please explore the [testdata]() directory for examples | |
11 | of the inputs it requires and the outputs that will be produced. _You may not | |
12 | need this tool._ If you are new to and just learning `go-kit` or if your use | |
13 | case involves introducing `go-kit` to an existing codebase you are better | |
14 | suited by slowly building out the "onion" by hand. | |
15 | ||
16 | Before starting you need to *install* `kitgen` utility — see instructions below. | |
17 | 1. **Define** your service. Create a `.go` file with the definition of your | |
18 | Service interface and any of the custom types it refers to: | |
19 | ```go | |
20 | // service.go | |
21 | package profilesvc // don't forget to name your package | |
22 | ||
23 | type Service interface { | |
24 | PostProfile(ctx context.Context, p Profile) error | |
25 | // ... | |
26 | } | |
27 | type Profile struct { | |
28 | ID string `json:"id"` | |
29 | Name string `json:"name,omitempty"` | |
30 | // ... | |
31 | } | |
32 | ``` | |
33 | 2. **Generate** your code. Run the following command: | |
34 | ```sh | |
35 | kitgen ./service.go | |
36 | # kitgen has a couple of flags that you may find useful | |
37 | ||
38 | # keep all code in the root directory | |
39 | kitgen -repo-layout flat ./service.go | |
40 | ||
41 | # put generated code elsewhere | |
42 | kitgen -target-dir ~/Projects/gohome/src/home.com/kitchenservice/brewcoffee | |
43 | ``` | |
44 | ||
45 | ## Installation | |
46 | 1. **Fetch** the `inlinefiles` utility. Go generate will use it to create your | |
47 | code: | |
48 | ``` | |
49 | go get github.com/nyarly/inlinefiles | |
50 | ``` | |
51 | 2. **Install** the binary for easy access to `kitgen`. Run the following commands: | |
52 | ```sh | |
53 | cd $GOPATH/src/github.com/go-kit/kit/cmd/kitgen | |
54 | go install | |
55 | ||
56 | # Check installation by running: | |
57 | kitgen -h | |
58 | ``` |
0 | package main | |
1 | ||
2 | import "go/ast" | |
3 | ||
4 | type arg struct { | |
5 | name, asField *ast.Ident | |
6 | typ ast.Expr | |
7 | } | |
8 | ||
9 | func (a arg) chooseName(scope *ast.Scope) *ast.Ident { | |
10 | if a.name == nil || scope.Lookup(a.name.Name) != nil { | |
11 | return inventName(a.typ, scope) | |
12 | } | |
13 | return a.name | |
14 | } | |
15 | ||
16 | func (a arg) field(scope *ast.Scope) *ast.Field { | |
17 | return &ast.Field{ | |
18 | Names: []*ast.Ident{a.chooseName(scope)}, | |
19 | Type: a.typ, | |
20 | } | |
21 | } | |
22 | ||
23 | func (a arg) result() *ast.Field { | |
24 | return &ast.Field{ | |
25 | Names: nil, | |
26 | Type: a.typ, | |
27 | } | |
28 | } | |
29 | ||
30 | func (a arg) exported() *ast.Field { | |
31 | return &ast.Field{ | |
32 | Names: []*ast.Ident{id(export(a.asField.Name))}, | |
33 | Type: a.typ, | |
34 | } | |
35 | } |
0 | package main | |
1 | ||
2 | import ( | |
3 | "fmt" | |
4 | "go/ast" | |
5 | "go/parser" | |
6 | "go/token" | |
7 | "path/filepath" | |
8 | "strings" | |
9 | "unicode" | |
10 | ) | |
11 | ||
12 | func export(s string) string { | |
13 | return strings.Title(s) | |
14 | } | |
15 | ||
16 | func unexport(s string) string { | |
17 | first := true | |
18 | return strings.Map(func(r rune) rune { | |
19 | if first { | |
20 | first = false | |
21 | return unicode.ToLower(r) | |
22 | } | |
23 | return r | |
24 | }, s) | |
25 | } | |
26 | ||
27 | func inventName(t ast.Expr, scope *ast.Scope) *ast.Ident { | |
28 | n := baseName(t) | |
29 | for try := 0; ; try++ { | |
30 | nstr := pickName(n, try) | |
31 | obj := ast.NewObj(ast.Var, nstr) | |
32 | if alt := scope.Insert(obj); alt == nil { | |
33 | return ast.NewIdent(nstr) | |
34 | } | |
35 | } | |
36 | } | |
37 | ||
38 | func baseName(t ast.Expr) string { | |
39 | switch tt := t.(type) { | |
40 | default: | |
41 | panic(fmt.Sprintf("don't know how to choose a base name for %T (%[1]v)", tt)) | |
42 | case *ast.ArrayType: | |
43 | return "slice" | |
44 | case *ast.Ident: | |
45 | return tt.Name | |
46 | case *ast.SelectorExpr: | |
47 | return tt.Sel.Name | |
48 | } | |
49 | } | |
50 | ||
51 | func pickName(base string, idx int) string { | |
52 | if idx == 0 { | |
53 | switch base { | |
54 | default: | |
55 | return strings.Split(base, "")[0] | |
56 | case "Context": | |
57 | return "ctx" | |
58 | case "error": | |
59 | return "err" | |
60 | } | |
61 | } | |
62 | return fmt.Sprintf("%s%d", base, idx) | |
63 | } | |
64 | ||
65 | func scopeWith(names ...string) *ast.Scope { | |
66 | scope := ast.NewScope(nil) | |
67 | for _, name := range names { | |
68 | scope.Insert(ast.NewObj(ast.Var, name)) | |
69 | } | |
70 | return scope | |
71 | } | |
72 | ||
73 | type visitFn func(ast.Node, func(ast.Node)) | |
74 | ||
75 | func (fn visitFn) Visit(node ast.Node, r func(ast.Node)) Visitor { | |
76 | fn(node, r) | |
77 | return fn | |
78 | } | |
79 | ||
80 | func replaceIdent(src ast.Node, named string, with ast.Node) ast.Node { | |
81 | r := visitFn(func(node ast.Node, replaceWith func(ast.Node)) { | |
82 | switch id := node.(type) { | |
83 | case *ast.Ident: | |
84 | if id.Name == named { | |
85 | replaceWith(with) | |
86 | } | |
87 | } | |
88 | }) | |
89 | return WalkReplace(r, src) | |
90 | } | |
91 | ||
92 | func replaceLit(src ast.Node, from, to string) ast.Node { | |
93 | r := visitFn(func(node ast.Node, replaceWith func(ast.Node)) { | |
94 | switch lit := node.(type) { | |
95 | case *ast.BasicLit: | |
96 | if lit.Value == from { | |
97 | replaceWith(&ast.BasicLit{Value: to}) | |
98 | } | |
99 | } | |
100 | }) | |
101 | return WalkReplace(r, src) | |
102 | } | |
103 | ||
104 | func fullAST() *ast.File { | |
105 | full, err := ASTTemplates.Open("full.go") | |
106 | if err != nil { | |
107 | panic(err) | |
108 | } | |
109 | f, err := parser.ParseFile(token.NewFileSet(), "templates/full.go", full, parser.DeclarationErrors) | |
110 | if err != nil { | |
111 | panic(err) | |
112 | } | |
113 | return f | |
114 | } | |
115 | ||
116 | func fetchImports() []*ast.ImportSpec { | |
117 | return fullAST().Imports | |
118 | } | |
119 | ||
120 | func fetchFuncDecl(name string) *ast.FuncDecl { | |
121 | f := fullAST() | |
122 | for _, decl := range f.Decls { | |
123 | if f, ok := decl.(*ast.FuncDecl); ok && f.Name.Name == name { | |
124 | return f | |
125 | } | |
126 | } | |
127 | panic(fmt.Errorf("no function called %q in 'templates/full.go'", name)) | |
128 | } | |
129 | ||
130 | func id(name string) *ast.Ident { | |
131 | return ast.NewIdent(name) | |
132 | } | |
133 | ||
134 | func sel(ids ...*ast.Ident) ast.Expr { | |
135 | switch len(ids) { | |
136 | default: | |
137 | return &ast.SelectorExpr{ | |
138 | X: sel(ids[:len(ids)-1]...), | |
139 | Sel: ids[len(ids)-1], | |
140 | } | |
141 | case 1: | |
142 | return ids[0] | |
143 | case 0: | |
144 | panic("zero ids to sel()") | |
145 | } | |
146 | } | |
147 | ||
148 | func typeField(t ast.Expr) *ast.Field { | |
149 | return &ast.Field{Type: t} | |
150 | } | |
151 | ||
152 | func field(n *ast.Ident, t ast.Expr) *ast.Field { | |
153 | return &ast.Field{ | |
154 | Names: []*ast.Ident{n}, | |
155 | Type: t, | |
156 | } | |
157 | } | |
158 | ||
159 | func fieldList(list ...*ast.Field) *ast.FieldList { | |
160 | return &ast.FieldList{List: list} | |
161 | } | |
162 | ||
163 | func mappedFieldList(fn func(arg) *ast.Field, args ...arg) *ast.FieldList { | |
164 | fl := &ast.FieldList{List: []*ast.Field{}} | |
165 | for _, a := range args { | |
166 | fl.List = append(fl.List, fn(a)) | |
167 | } | |
168 | return fl | |
169 | } | |
170 | ||
171 | func blockStmt(stmts ...ast.Stmt) *ast.BlockStmt { | |
172 | return &ast.BlockStmt{ | |
173 | List: stmts, | |
174 | } | |
175 | } | |
176 | ||
177 | func structDecl(name *ast.Ident, fields *ast.FieldList) ast.Decl { | |
178 | return typeDecl(&ast.TypeSpec{ | |
179 | Name: name, | |
180 | Type: &ast.StructType{ | |
181 | Fields: fields, | |
182 | }, | |
183 | }) | |
184 | } | |
185 | ||
186 | func typeDecl(ts *ast.TypeSpec) ast.Decl { | |
187 | return &ast.GenDecl{ | |
188 | Tok: token.TYPE, | |
189 | Specs: []ast.Spec{ts}, | |
190 | } | |
191 | } | |
192 | ||
193 | func pasteStmts(body *ast.BlockStmt, idx int, stmts []ast.Stmt) { | |
194 | list := body.List | |
195 | prefix := list[:idx] | |
196 | suffix := make([]ast.Stmt, len(list)-idx-1) | |
197 | copy(suffix, list[idx+1:]) | |
198 | ||
199 | body.List = append(append(prefix, stmts...), suffix...) | |
200 | } | |
201 | ||
202 | func importFor(is *ast.ImportSpec) *ast.GenDecl { | |
203 | return &ast.GenDecl{Tok: token.IMPORT, Specs: []ast.Spec{is}} | |
204 | } | |
205 | ||
206 | func importSpec(path string) *ast.ImportSpec { | |
207 | return &ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"` + filepath.ToSlash(path) + `"`}} | |
208 | } |
0 | // This file was automatically generated based on the contents of *.tmpl | |
1 | // If you need to update this file, change the contents of those files | |
2 | // (or add new ones) and run 'go generate' | |
3 | ||
4 | package main | |
5 | ||
6 | import "golang.org/x/tools/godoc/vfs/mapfs" | |
7 | ||
8 | var ASTTemplates = mapfs.New(map[string]string{ | |
9 | `full.go`: "package foo\n\nimport (\n \"context\"\n \"encoding/json\"\n \"errors\"\n \"net/http\"\n\n \"github.com/go-kit/kit/endpoint\"\n httptransport \"github.com/go-kit/kit/transport/http\"\n)\n\ntype ExampleService struct {\n}\n\ntype ExampleRequest struct {\n I int\n S string\n}\ntype ExampleResponse struct {\n S string\n Err error\n}\n\ntype Endpoints struct {\n ExampleEndpoint endpoint.Endpoint\n}\n\nfunc (f ExampleService) ExampleEndpoint(ctx context.Context, i int, s string) (string, error) {\n panic(errors.New(\"not implemented\"))\n}\n\nfunc makeExampleEndpoint(f ExampleService) endpoint.Endpoint {\n return func(ctx context.Context, request interface{}) (interface{}, error) {\n req := request.(ExampleRequest)\n s, err := f.ExampleEndpoint(ctx, req.I, req.S)\n return ExampleResponse{S: s, Err: err}, nil\n }\n}\n\nfunc inlineHandlerBuilder(m *http.ServeMux, endpoints Endpoints) {\n m.Handle(\"/bar\", httptransport.NewServer(endpoints.ExampleEndpoint, DecodeExampleRequest, EncodeExampleResponse))\n}\n\nfunc NewHTTPHandler(endpoints Endpoints) http.Handler {\n m := http.NewServeMux()\n inlineHandlerBuilder(m, endpoints)\n return m\n}\n\nfunc DecodeExampleRequest(_ context.Context, r *http.Request) (interface{}, error) {\n var req ExampleRequest\n err := json.NewDecoder(r.Body).Decode(&req)\n return req, err\n}\n\nfunc EncodeExampleResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {\n w.Header().Set(\"Content-Type\", \"application/json; charset=utf-8\")\n return json.NewEncoder(w).Encode(response)\n}\n", | |
10 | }) |
0 | package main | |
1 | ||
2 | import "path/filepath" | |
3 | ||
4 | type deflayout struct { | |
5 | targetDir string | |
6 | } | |
7 | ||
8 | func (l deflayout) packagePath(sub string) string { | |
9 | return filepath.Join(l.targetDir, sub) | |
10 | } | |
11 | ||
12 | func (l deflayout) transformAST(ctx *sourceContext) (files, error) { | |
13 | out := make(outputTree) | |
14 | ||
15 | endpoints := out.addFile("endpoints/endpoints.go", "endpoints") | |
16 | http := out.addFile("http/http.go", "http") | |
17 | service := out.addFile("service/service.go", "service") | |
18 | ||
19 | addImports(endpoints, ctx) | |
20 | addImports(http, ctx) | |
21 | addImports(service, ctx) | |
22 | ||
23 | for _, typ := range ctx.types { | |
24 | addType(service, typ) | |
25 | } | |
26 | ||
27 | for _, iface := range ctx.interfaces { //only one... | |
28 | addStubStruct(service, iface) | |
29 | ||
30 | for _, meth := range iface.methods { | |
31 | addMethod(service, iface, meth) | |
32 | addRequestStruct(endpoints, meth) | |
33 | addResponseStruct(endpoints, meth) | |
34 | addEndpointMaker(endpoints, iface, meth) | |
35 | } | |
36 | ||
37 | addEndpointsStruct(endpoints, iface) | |
38 | addHTTPHandler(http, iface) | |
39 | ||
40 | for _, meth := range iface.methods { | |
41 | addDecoder(http, meth) | |
42 | addEncoder(http, meth) | |
43 | } | |
44 | ||
45 | for name := range out { | |
46 | out[name] = selectify(out[name], "service", iface.stubName().Name, l.packagePath("service")) | |
47 | for _, meth := range iface.methods { | |
48 | out[name] = selectify(out[name], "endpoints", meth.requestStructName().Name, l.packagePath("endpoints")) | |
49 | } | |
50 | } | |
51 | } | |
52 | ||
53 | for name := range out { | |
54 | out[name] = selectify(out[name], "endpoints", "Endpoints", l.packagePath("endpoints")) | |
55 | ||
56 | for _, typ := range ctx.types { | |
57 | out[name] = selectify(out[name], "service", typ.Name.Name, l.packagePath("service")) | |
58 | } | |
59 | } | |
60 | ||
61 | return formatNodes(out) | |
62 | } |
0 | package main | |
1 | ||
2 | import "go/ast" | |
3 | ||
4 | type flat struct{} | |
5 | ||
6 | func (f flat) transformAST(ctx *sourceContext) (files, error) { | |
7 | root := &ast.File{ | |
8 | Name: ctx.pkg, | |
9 | Decls: []ast.Decl{}, | |
10 | } | |
11 | ||
12 | addImports(root, ctx) | |
13 | ||
14 | for _, typ := range ctx.types { | |
15 | addType(root, typ) | |
16 | } | |
17 | ||
18 | for _, iface := range ctx.interfaces { //only one... | |
19 | addStubStruct(root, iface) | |
20 | ||
21 | for _, meth := range iface.methods { | |
22 | addMethod(root, iface, meth) | |
23 | addRequestStruct(root, meth) | |
24 | addResponseStruct(root, meth) | |
25 | addEndpointMaker(root, iface, meth) | |
26 | } | |
27 | ||
28 | addEndpointsStruct(root, iface) | |
29 | addHTTPHandler(root, iface) | |
30 | ||
31 | for _, meth := range iface.methods { | |
32 | addDecoder(root, meth) | |
33 | addEncoder(root, meth) | |
34 | } | |
35 | } | |
36 | ||
37 | return formatNodes(outputTree{"gokit.go": root}) | |
38 | } |
0 | package main | |
1 | ||
2 | import "go/ast" | |
3 | ||
4 | // because "interface" is a keyword... | |
5 | type iface struct { | |
6 | name, stubname, rcvrName *ast.Ident | |
7 | methods []method | |
8 | } | |
9 | ||
10 | func (i iface) stubName() *ast.Ident { | |
11 | return i.stubname | |
12 | } | |
13 | ||
14 | func (i iface) stubStructDecl() ast.Decl { | |
15 | return structDecl(i.stubName(), &ast.FieldList{}) | |
16 | } | |
17 | ||
18 | func (i iface) endpointsStruct() ast.Decl { | |
19 | fl := &ast.FieldList{} | |
20 | for _, m := range i.methods { | |
21 | fl.List = append(fl.List, &ast.Field{Names: []*ast.Ident{m.name}, Type: sel(id("endpoint"), id("Endpoint"))}) | |
22 | } | |
23 | return structDecl(id("Endpoints"), fl) | |
24 | } | |
25 | ||
26 | func (i iface) httpHandler() ast.Decl { | |
27 | handlerFn := fetchFuncDecl("NewHTTPHandler") | |
28 | ||
29 | // does this "inlining" process merit a helper akin to replaceIdent? | |
30 | handleCalls := []ast.Stmt{} | |
31 | for _, m := range i.methods { | |
32 | handleCall := fetchFuncDecl("inlineHandlerBuilder").Body.List[0].(*ast.ExprStmt).X.(*ast.CallExpr) | |
33 | ||
34 | handleCall = replaceLit(handleCall, `"/bar"`, `"`+m.pathName()+`"`).(*ast.CallExpr) | |
35 | handleCall = replaceIdent(handleCall, "ExampleEndpoint", m.name).(*ast.CallExpr) | |
36 | handleCall = replaceIdent(handleCall, "DecodeExampleRequest", m.decodeFuncName()).(*ast.CallExpr) | |
37 | handleCall = replaceIdent(handleCall, "EncodeExampleResponse", m.encodeFuncName()).(*ast.CallExpr) | |
38 | ||
39 | handleCalls = append(handleCalls, &ast.ExprStmt{X: handleCall}) | |
40 | } | |
41 | ||
42 | pasteStmts(handlerFn.Body, 1, handleCalls) | |
43 | ||
44 | return handlerFn | |
45 | } | |
46 | ||
47 | func (i iface) receiver() *ast.Field { | |
48 | return field(i.receiverName(), i.stubName()) | |
49 | } | |
50 | ||
51 | func (i iface) receiverName() *ast.Ident { | |
52 | if i.rcvrName != nil { | |
53 | return i.rcvrName | |
54 | } | |
55 | scope := ast.NewScope(nil) | |
56 | for _, meth := range i.methods { | |
57 | for _, arg := range meth.params { | |
58 | if arg.name != nil { | |
59 | scope.Insert(ast.NewObj(ast.Var, arg.name.Name)) | |
60 | } | |
61 | } | |
62 | for _, arg := range meth.results { | |
63 | if arg.name != nil { | |
64 | scope.Insert(ast.NewObj(ast.Var, arg.name.Name)) | |
65 | } | |
66 | } | |
67 | } | |
68 | return id(unexport(inventName(i.name, scope).Name)) | |
69 | } |
0 | package main | |
1 | ||
2 | import ( | |
3 | "flag" | |
4 | "fmt" | |
5 | "go/ast" | |
6 | "go/parser" | |
7 | "go/token" | |
8 | "io" | |
9 | "log" | |
10 | "os" | |
11 | "path" | |
12 | ||
13 | "github.com/pkg/errors" | |
14 | ) | |
15 | ||
16 | // go get github.com/nyarly/inlinefiles | |
17 | //go:generate inlinefiles --package=main --vfs=ASTTemplates ./templates ast_templates.go | |
18 | ||
19 | func usage() string { | |
20 | return fmt.Sprintf("Usage: %s <filename> (try -h)", os.Args[0]) | |
21 | } | |
22 | ||
23 | var ( | |
24 | help = flag.Bool("h", false, "print this help") | |
25 | layoutkind = flag.String("repo-layout", "default", "default, flat...") | |
26 | outdirrel = flag.String("target-dir", ".", "base directory to emit into") | |
27 | //contextOmittable = flag.Bool("allow-no-context", false, "allow service methods to omit context parameter") | |
28 | ) | |
29 | ||
30 | func helpText() { | |
31 | fmt.Println("USAGE") | |
32 | fmt.Println(" kitgen [flags] path/to/service.go") | |
33 | fmt.Println("") | |
34 | fmt.Println("FLAGS") | |
35 | flag.PrintDefaults() | |
36 | } | |
37 | ||
38 | func main() { | |
39 | flag.Parse() | |
40 | ||
41 | if *help { | |
42 | helpText() | |
43 | os.Exit(0) | |
44 | } | |
45 | ||
46 | outdir := *outdirrel | |
47 | if !path.IsAbs(*outdirrel) { | |
48 | wd, err := os.Getwd() | |
49 | if err != nil { | |
50 | log.Fatalf("error getting current working directory: %v", err) | |
51 | } | |
52 | outdir = path.Join(wd, *outdirrel) | |
53 | } | |
54 | ||
55 | var layout layout | |
56 | switch *layoutkind { | |
57 | default: | |
58 | log.Fatalf("Unrecognized layout kind: %q - try 'default' or 'flat'", *layoutkind) | |
59 | case "default": | |
60 | gopath := getGopath() | |
61 | importBase, err := importPath(outdir, gopath) | |
62 | if err != nil { | |
63 | log.Fatal(err) | |
64 | } | |
65 | layout = deflayout{targetDir: importBase} | |
66 | case "flat": | |
67 | layout = flat{} | |
68 | } | |
69 | ||
70 | if len(os.Args) < 2 { | |
71 | log.Fatal(usage()) | |
72 | } | |
73 | filename := flag.Arg(0) | |
74 | file, err := os.Open(filename) | |
75 | if err != nil { | |
76 | log.Fatalf("error while opening %q: %v", filename, err) | |
77 | } | |
78 | ||
79 | tree, err := process(filename, file, layout) | |
80 | if err != nil { | |
81 | log.Fatal(err) | |
82 | } | |
83 | ||
84 | err = splat(outdir, tree) | |
85 | if err != nil { | |
86 | log.Fatal(err) | |
87 | } | |
88 | } | |
89 | ||
90 | func process(filename string, source io.Reader, layout layout) (files, error) { | |
91 | f, err := parseFile(filename, source) | |
92 | if err != nil { | |
93 | return nil, errors.Wrapf(err, "parsing input %q", filename) | |
94 | } | |
95 | ||
96 | context, err := extractContext(f) | |
97 | if err != nil { | |
98 | return nil, errors.Wrapf(err, "examining input file %q", filename) | |
99 | } | |
100 | ||
101 | tree, err := layout.transformAST(context) | |
102 | if err != nil { | |
103 | return nil, errors.Wrapf(err, "generating AST") | |
104 | } | |
105 | return tree, nil | |
106 | } | |
107 | ||
108 | /* | |
109 | buf, err := formatNode(dest) | |
110 | if err != nil { | |
111 | return nil, errors.Wrapf(err, "formatting") | |
112 | } | |
113 | return buf, nil | |
114 | } | |
115 | */ | |
116 | ||
117 | func parseFile(fname string, source io.Reader) (ast.Node, error) { | |
118 | f, err := parser.ParseFile(token.NewFileSet(), fname, source, parser.DeclarationErrors) | |
119 | if err != nil { | |
120 | return nil, err | |
121 | } | |
122 | return f, nil | |
123 | } | |
124 | ||
125 | func extractContext(f ast.Node) (*sourceContext, error) { | |
126 | context := &sourceContext{} | |
127 | visitor := &parseVisitor{src: context} | |
128 | ||
129 | ast.Walk(visitor, f) | |
130 | ||
131 | return context, context.validate() | |
132 | } | |
133 | ||
134 | func splat(dir string, tree files) error { | |
135 | for fn, buf := range tree { | |
136 | if err := splatFile(path.Join(dir, fn), buf); err != nil { | |
137 | return err | |
138 | } | |
139 | } | |
140 | return nil | |
141 | } | |
142 | ||
143 | func splatFile(target string, buf io.Reader) error { | |
144 | err := os.MkdirAll(path.Dir(target), os.ModePerm) | |
145 | if err != nil { | |
146 | return errors.Wrapf(err, "Couldn't create directory for %q", target) | |
147 | } | |
148 | f, err := os.Create(target) | |
149 | if err != nil { | |
150 | return errors.Wrapf(err, "Couldn't create file %q", target) | |
151 | } | |
152 | defer f.Close() | |
153 | _, err = io.Copy(f, buf) | |
154 | return errors.Wrapf(err, "Error writing data to file %q", target) | |
155 | } |
0 | package main | |
1 | ||
2 | import ( | |
3 | "bufio" | |
4 | "bytes" | |
5 | "flag" | |
6 | "fmt" | |
7 | "io/ioutil" | |
8 | "os/exec" | |
9 | "path/filepath" | |
10 | "testing" | |
11 | ||
12 | "github.com/aryann/difflib" | |
13 | ) | |
14 | ||
15 | var update = flag.Bool("update", false, "update golden files") | |
16 | ||
17 | func TestProcess(t *testing.T) { | |
18 | cases, err := filepath.Glob("testdata/*") | |
19 | if err != nil { | |
20 | t.Fatal(err) | |
21 | } | |
22 | ||
23 | laidout := func(t *testing.T, inpath, dir, kind string, layout layout, in []byte) { | |
24 | t.Run(kind, func(t *testing.T) { | |
25 | targetDir := filepath.Join(dir, kind) | |
26 | tree, err := process(inpath, bytes.NewBuffer(in), layout) | |
27 | if err != nil { | |
28 | t.Fatal(inpath, fmt.Sprintf("%+#v", err)) | |
29 | } | |
30 | ||
31 | if *update { | |
32 | err := splat(targetDir, tree) | |
33 | if err != nil { | |
34 | t.Fatal(kind, err) | |
35 | } | |
36 | // otherwise we need to do some tomfoolery with resetting buffers | |
37 | // I'm willing to just run the tests again - besides, we shouldn't be | |
38 | // regerating the golden files that often | |
39 | t.Error("Updated outputs - DID NOT COMPARE! (run tests again without -update)") | |
40 | return | |
41 | } | |
42 | ||
43 | for filename, buf := range tree { | |
44 | actual, err := ioutil.ReadAll(buf) | |
45 | if err != nil { | |
46 | t.Fatal(kind, filename, err) | |
47 | } | |
48 | ||
49 | outpath := filepath.Join(targetDir, filename) | |
50 | ||
51 | expected, err := ioutil.ReadFile(outpath) | |
52 | if err != nil { | |
53 | t.Fatal(outpath, err) | |
54 | } | |
55 | ||
56 | if !bytes.Equal(expected, actual) { | |
57 | results := difflib.Diff(splitLines(expected), splitLines(actual)) | |
58 | for _, result := range results { | |
59 | if result.Delta == difflib.Common { | |
60 | continue | |
61 | } | |
62 | t.Error(result) | |
63 | } | |
64 | } | |
65 | } | |
66 | ||
67 | if !t.Failed() { | |
68 | build := exec.Command("go", "build", "./...") | |
69 | build.Dir = targetDir | |
70 | out, err := build.CombinedOutput() | |
71 | if err != nil { | |
72 | t.Fatalf("Cannot build output: %v\n%s", err, string(out)) | |
73 | } | |
74 | } | |
75 | }) | |
76 | ||
77 | } | |
78 | ||
79 | testcase := func(dir string) { | |
80 | name := filepath.Base(dir) | |
81 | t.Run(name, func(t *testing.T) { | |
82 | inpath := filepath.Join(dir, "in.go") | |
83 | ||
84 | in, err := ioutil.ReadFile(inpath) | |
85 | if err != nil { | |
86 | t.Fatal(inpath, err) | |
87 | } | |
88 | laidout(t, inpath, dir, "flat", flat{}, in) | |
89 | laidout(t, inpath, dir, "default", deflayout{ | |
90 | targetDir: filepath.Join("github.com/go-kit/kit/cmd/kitgen", dir, "default"), | |
91 | }, in) | |
92 | }) | |
93 | } | |
94 | ||
95 | for _, dir := range cases { | |
96 | testcase(dir) | |
97 | } | |
98 | } | |
99 | ||
100 | func TestTemplatesBuild(t *testing.T) { | |
101 | build := exec.Command("go", "build", "./...") | |
102 | build.Dir = "templates" | |
103 | out, err := build.CombinedOutput() | |
104 | if err != nil { | |
105 | t.Fatal(err, "\n", string(out)) | |
106 | } | |
107 | } | |
108 | ||
109 | func splitLines(txt []byte) []string { | |
110 | s := bufio.NewScanner(bytes.NewReader(txt)) | |
111 | var ss []string | |
112 | for s.Scan() { | |
113 | ss = append(ss, s.Text()) | |
114 | } | |
115 | return ss | |
116 | } |
0 | package main | |
1 | ||
2 | import ( | |
3 | "go/ast" | |
4 | "go/token" | |
5 | "strings" | |
6 | ) | |
7 | ||
8 | type method struct { | |
9 | name *ast.Ident | |
10 | params []arg | |
11 | results []arg | |
12 | structsResolved bool | |
13 | } | |
14 | ||
15 | func (m method) definition(ifc iface) ast.Decl { | |
16 | notImpl := fetchFuncDecl("ExampleEndpoint") | |
17 | ||
18 | notImpl.Name = m.name | |
19 | notImpl.Recv = fieldList(ifc.receiver()) | |
20 | scope := scopeWith(notImpl.Recv.List[0].Names[0].Name) | |
21 | notImpl.Type.Params = m.funcParams(scope) | |
22 | notImpl.Type.Results = m.funcResults() | |
23 | ||
24 | return notImpl | |
25 | } | |
26 | ||
27 | func (m method) endpointMaker(ifc iface) ast.Decl { | |
28 | endpointFn := fetchFuncDecl("makeExampleEndpoint") | |
29 | scope := scopeWith("ctx", "req", ifc.receiverName().Name) | |
30 | ||
31 | anonFunc := endpointFn.Body.List[0].(*ast.ReturnStmt).Results[0].(*ast.FuncLit) | |
32 | if !m.hasContext() { | |
33 | // strip context param from endpoint function | |
34 | anonFunc.Type.Params.List = anonFunc.Type.Params.List[1:] | |
35 | } | |
36 | ||
37 | anonFunc = replaceIdent(anonFunc, "ExampleRequest", m.requestStructName()).(*ast.FuncLit) | |
38 | callMethod := m.called(ifc, scope, "ctx", "req") | |
39 | anonFunc.Body.List[1] = callMethod | |
40 | anonFunc.Body.List[2].(*ast.ReturnStmt).Results[0] = m.wrapResult(callMethod.Lhs) | |
41 | ||
42 | endpointFn.Body.List[0].(*ast.ReturnStmt).Results[0] = anonFunc | |
43 | endpointFn.Name = m.endpointMakerName() | |
44 | endpointFn.Type.Params = fieldList(ifc.receiver()) | |
45 | endpointFn.Type.Results = fieldList(typeField(sel(id("endpoint"), id("Endpoint")))) | |
46 | return endpointFn | |
47 | } | |
48 | ||
49 | func (m method) pathName() string { | |
50 | return "/" + strings.ToLower(m.name.Name) | |
51 | } | |
52 | ||
53 | func (m method) encodeFuncName() *ast.Ident { | |
54 | return id("Encode" + m.name.Name + "Response") | |
55 | } | |
56 | ||
57 | func (m method) decodeFuncName() *ast.Ident { | |
58 | return id("Decode" + m.name.Name + "Request") | |
59 | } | |
60 | ||
61 | func (m method) resultNames(scope *ast.Scope) []*ast.Ident { | |
62 | ids := []*ast.Ident{} | |
63 | for _, rz := range m.results { | |
64 | ids = append(ids, rz.chooseName(scope)) | |
65 | } | |
66 | return ids | |
67 | } | |
68 | ||
69 | func (m method) called(ifc iface, scope *ast.Scope, ctxName, spreadStruct string) *ast.AssignStmt { | |
70 | m.resolveStructNames() | |
71 | ||
72 | resNamesExpr := []ast.Expr{} | |
73 | for _, r := range m.resultNames(scope) { | |
74 | resNamesExpr = append(resNamesExpr, ast.Expr(r)) | |
75 | } | |
76 | ||
77 | arglist := []ast.Expr{} | |
78 | if m.hasContext() { | |
79 | arglist = append(arglist, id(ctxName)) | |
80 | } | |
81 | ssid := id(spreadStruct) | |
82 | for _, f := range m.requestStructFields().List { | |
83 | arglist = append(arglist, sel(ssid, f.Names[0])) | |
84 | } | |
85 | ||
86 | return &ast.AssignStmt{ | |
87 | Lhs: resNamesExpr, | |
88 | Tok: token.DEFINE, | |
89 | Rhs: []ast.Expr{ | |
90 | &ast.CallExpr{ | |
91 | Fun: sel(ifc.receiverName(), m.name), | |
92 | Args: arglist, | |
93 | }, | |
94 | }, | |
95 | } | |
96 | } | |
97 | ||
98 | func (m method) wrapResult(results []ast.Expr) ast.Expr { | |
99 | kvs := []ast.Expr{} | |
100 | m.resolveStructNames() | |
101 | ||
102 | for i, a := range m.results { | |
103 | kvs = append(kvs, &ast.KeyValueExpr{ | |
104 | Key: ast.NewIdent(export(a.asField.Name)), | |
105 | Value: results[i], | |
106 | }) | |
107 | } | |
108 | return &ast.CompositeLit{ | |
109 | Type: m.responseStructName(), | |
110 | Elts: kvs, | |
111 | } | |
112 | } | |
113 | ||
114 | func (m method) resolveStructNames() { | |
115 | if m.structsResolved { | |
116 | return | |
117 | } | |
118 | m.structsResolved = true | |
119 | scope := ast.NewScope(nil) | |
120 | for i, p := range m.params { | |
121 | p.asField = p.chooseName(scope) | |
122 | m.params[i] = p | |
123 | } | |
124 | scope = ast.NewScope(nil) | |
125 | for i, r := range m.results { | |
126 | r.asField = r.chooseName(scope) | |
127 | m.results[i] = r | |
128 | } | |
129 | } | |
130 | ||
131 | func (m method) decoderFunc() ast.Decl { | |
132 | fn := fetchFuncDecl("DecodeExampleRequest") | |
133 | fn.Name = m.decodeFuncName() | |
134 | fn = replaceIdent(fn, "ExampleRequest", m.requestStructName()).(*ast.FuncDecl) | |
135 | return fn | |
136 | } | |
137 | ||
138 | func (m method) encoderFunc() ast.Decl { | |
139 | fn := fetchFuncDecl("EncodeExampleResponse") | |
140 | fn.Name = m.encodeFuncName() | |
141 | return fn | |
142 | } | |
143 | ||
144 | func (m method) endpointMakerName() *ast.Ident { | |
145 | return id("Make" + m.name.Name + "Endpoint") | |
146 | } | |
147 | ||
148 | func (m method) requestStruct() ast.Decl { | |
149 | m.resolveStructNames() | |
150 | return structDecl(m.requestStructName(), m.requestStructFields()) | |
151 | } | |
152 | ||
153 | func (m method) responseStruct() ast.Decl { | |
154 | m.resolveStructNames() | |
155 | return structDecl(m.responseStructName(), m.responseStructFields()) | |
156 | } | |
157 | ||
158 | func (m method) hasContext() bool { | |
159 | if len(m.params) < 1 { | |
160 | return false | |
161 | } | |
162 | carg := m.params[0].typ | |
163 | // ugh. this is maybe okay for the one-off, but a general case for matching | |
164 | // types would be helpful | |
165 | if sel, is := carg.(*ast.SelectorExpr); is && sel.Sel.Name == "Context" { | |
166 | if id, is := sel.X.(*ast.Ident); is && id.Name == "context" { | |
167 | return true | |
168 | } | |
169 | } | |
170 | return false | |
171 | } | |
172 | ||
173 | func (m method) nonContextParams() []arg { | |
174 | if m.hasContext() { | |
175 | return m.params[1:] | |
176 | } | |
177 | return m.params | |
178 | } | |
179 | ||
180 | func (m method) funcParams(scope *ast.Scope) *ast.FieldList { | |
181 | parms := &ast.FieldList{} | |
182 | if m.hasContext() { | |
183 | parms.List = []*ast.Field{{ | |
184 | Names: []*ast.Ident{ast.NewIdent("ctx")}, | |
185 | Type: sel(id("context"), id("Context")), | |
186 | }} | |
187 | scope.Insert(ast.NewObj(ast.Var, "ctx")) | |
188 | } | |
189 | parms.List = append(parms.List, mappedFieldList(func(a arg) *ast.Field { | |
190 | return a.field(scope) | |
191 | }, m.nonContextParams()...).List...) | |
192 | return parms | |
193 | } | |
194 | ||
195 | func (m method) funcResults() *ast.FieldList { | |
196 | return mappedFieldList(func(a arg) *ast.Field { | |
197 | return a.result() | |
198 | }, m.results...) | |
199 | } | |
200 | ||
201 | func (m method) requestStructName() *ast.Ident { | |
202 | return id(export(m.name.Name) + "Request") | |
203 | } | |
204 | ||
205 | func (m method) requestStructFields() *ast.FieldList { | |
206 | return mappedFieldList(func(a arg) *ast.Field { | |
207 | return a.exported() | |
208 | }, m.nonContextParams()...) | |
209 | } | |
210 | ||
211 | func (m method) responseStructName() *ast.Ident { | |
212 | return id(export(m.name.Name) + "Response") | |
213 | } | |
214 | ||
215 | func (m method) responseStructFields() *ast.FieldList { | |
216 | return mappedFieldList(func(a arg) *ast.Field { | |
217 | return a.exported() | |
218 | }, m.results...) | |
219 | } |
0 | package main | |
1 | ||
2 | import ( | |
3 | "go/ast" | |
4 | ) | |
5 | ||
6 | type ( | |
7 | parseVisitor struct { | |
8 | src *sourceContext | |
9 | } | |
10 | ||
11 | typeSpecVisitor struct { | |
12 | src *sourceContext | |
13 | node *ast.TypeSpec | |
14 | iface *iface | |
15 | name *ast.Ident | |
16 | } | |
17 | ||
18 | interfaceTypeVisitor struct { | |
19 | node *ast.TypeSpec | |
20 | ts *typeSpecVisitor | |
21 | methods []method | |
22 | } | |
23 | ||
24 | methodVisitor struct { | |
25 | depth int | |
26 | node *ast.TypeSpec | |
27 | list *[]method | |
28 | name *ast.Ident | |
29 | params, results *[]arg | |
30 | isMethod bool | |
31 | } | |
32 | ||
33 | argListVisitor struct { | |
34 | list *[]arg | |
35 | } | |
36 | ||
37 | argVisitor struct { | |
38 | node *ast.TypeSpec | |
39 | parts []ast.Expr | |
40 | list *[]arg | |
41 | } | |
42 | ) | |
43 | ||
44 | func (v *parseVisitor) Visit(n ast.Node) ast.Visitor { | |
45 | switch rn := n.(type) { | |
46 | default: | |
47 | return v | |
48 | case *ast.File: | |
49 | v.src.pkg = rn.Name | |
50 | return v | |
51 | case *ast.ImportSpec: | |
52 | v.src.imports = append(v.src.imports, rn) | |
53 | return nil | |
54 | ||
55 | case *ast.TypeSpec: | |
56 | switch rn.Type.(type) { | |
57 | default: | |
58 | v.src.types = append(v.src.types, rn) | |
59 | case *ast.InterfaceType: | |
60 | // can't output interfaces | |
61 | // because they'd conflict with our implementations | |
62 | } | |
63 | return &typeSpecVisitor{src: v.src, node: rn} | |
64 | } | |
65 | } | |
66 | ||
67 | /* | |
68 | package foo | |
69 | ||
70 | type FooService interface { | |
71 | Bar(ctx context.Context, i int, s string) (string, error) | |
72 | } | |
73 | */ | |
74 | ||
75 | func (v *typeSpecVisitor) Visit(n ast.Node) ast.Visitor { | |
76 | switch rn := n.(type) { | |
77 | default: | |
78 | return v | |
79 | case *ast.Ident: | |
80 | if v.name == nil { | |
81 | v.name = rn | |
82 | } | |
83 | return v | |
84 | case *ast.InterfaceType: | |
85 | return &interfaceTypeVisitor{ts: v, methods: []method{}} | |
86 | case nil: | |
87 | if v.iface != nil { | |
88 | v.iface.name = v.name | |
89 | sn := *v.name | |
90 | v.iface.stubname = &sn | |
91 | v.iface.stubname.Name = v.name.String() | |
92 | v.src.interfaces = append(v.src.interfaces, *v.iface) | |
93 | } | |
94 | return nil | |
95 | } | |
96 | } | |
97 | ||
98 | func (v *interfaceTypeVisitor) Visit(n ast.Node) ast.Visitor { | |
99 | switch n.(type) { | |
100 | default: | |
101 | return v | |
102 | case *ast.Field: | |
103 | return &methodVisitor{list: &v.methods} | |
104 | case nil: | |
105 | v.ts.iface = &iface{methods: v.methods} | |
106 | return nil | |
107 | } | |
108 | } | |
109 | ||
110 | func (v *methodVisitor) Visit(n ast.Node) ast.Visitor { | |
111 | switch rn := n.(type) { | |
112 | default: | |
113 | v.depth++ | |
114 | return v | |
115 | case *ast.Ident: | |
116 | if rn.IsExported() { | |
117 | v.name = rn | |
118 | } | |
119 | v.depth++ | |
120 | return v | |
121 | case *ast.FuncType: | |
122 | v.depth++ | |
123 | v.isMethod = true | |
124 | return v | |
125 | case *ast.FieldList: | |
126 | if v.params == nil { | |
127 | v.params = &[]arg{} | |
128 | return &argListVisitor{list: v.params} | |
129 | } | |
130 | if v.results == nil { | |
131 | v.results = &[]arg{} | |
132 | } | |
133 | return &argListVisitor{list: v.results} | |
134 | case nil: | |
135 | v.depth-- | |
136 | if v.depth == 0 && v.isMethod && v.name != nil { | |
137 | *v.list = append(*v.list, method{name: v.name, params: *v.params, results: *v.results}) | |
138 | } | |
139 | return nil | |
140 | } | |
141 | } | |
142 | ||
143 | func (v *argListVisitor) Visit(n ast.Node) ast.Visitor { | |
144 | switch n.(type) { | |
145 | default: | |
146 | return nil | |
147 | case *ast.Field: | |
148 | return &argVisitor{list: v.list} | |
149 | } | |
150 | } | |
151 | ||
152 | func (v *argVisitor) Visit(n ast.Node) ast.Visitor { | |
153 | switch t := n.(type) { | |
154 | case *ast.CommentGroup, *ast.BasicLit: | |
155 | return nil | |
156 | case *ast.Ident: //Expr -> everything, but clarity | |
157 | if t.Name != "_" { | |
158 | v.parts = append(v.parts, t) | |
159 | } | |
160 | case ast.Expr: | |
161 | v.parts = append(v.parts, t) | |
162 | case nil: | |
163 | names := v.parts[:len(v.parts)-1] | |
164 | tp := v.parts[len(v.parts)-1] | |
165 | if len(names) == 0 { | |
166 | *v.list = append(*v.list, arg{typ: tp}) | |
167 | return nil | |
168 | } | |
169 | for _, n := range names { | |
170 | *v.list = append(*v.list, arg{ | |
171 | name: n.(*ast.Ident), | |
172 | typ: tp, | |
173 | }) | |
174 | } | |
175 | } | |
176 | return nil | |
177 | } |
0 | package main | |
1 | ||
2 | import ( | |
3 | "fmt" | |
4 | "runtime" | |
5 | "strings" | |
6 | "testing" | |
7 | ) | |
8 | ||
9 | func TestImportPath(t *testing.T) { | |
10 | testcase := func(gopath, targetpath, expected string) { | |
11 | t.Run(fmt.Sprintf("%q + %q", gopath, targetpath), func(t *testing.T) { | |
12 | actual, err := importPath(targetpath, gopath) | |
13 | if err != nil { | |
14 | t.Fatalf("Expected no error, got %q", err) | |
15 | } | |
16 | if actual != expected { | |
17 | t.Errorf("Expected %q, got %q", expected, actual) | |
18 | } | |
19 | }) | |
20 | } | |
21 | ||
22 | if runtime.GOOS == "windows" { | |
23 | testcase("c:\\gopath\\", "c:\\gopath\\src\\somewhere", "somewhere") | |
24 | testcase("c:\\gopath", "c:\\gopath\\src\\somewhere", "somewhere") | |
25 | testcase("c:\\gopath;\\other", "c:\\gopath\\src\\somewhere", "somewhere") | |
26 | testcase("c:\\other;c:\\gopath\\", "c:\\gopath\\src\\somewhere", "somewhere") | |
27 | } else { | |
28 | testcase("/gopath/", "/gopath/src/somewhere", "somewhere") | |
29 | testcase("/gopath", "/gopath/src/somewhere", "somewhere") | |
30 | testcase("/gopath:/other", "/gopath/src/somewhere", "somewhere") | |
31 | testcase("/other:/gopath/", "/gopath/src/somewhere", "somewhere") | |
32 | } | |
33 | } | |
34 | ||
35 | func TestImportPathSadpath(t *testing.T) { | |
36 | testcase := func(gopath, targetpath, expected string) { | |
37 | t.Run(fmt.Sprintf("%q + %q", gopath, targetpath), func(t *testing.T) { | |
38 | actual, err := importPath(targetpath, gopath) | |
39 | if actual != "" { | |
40 | t.Errorf("Expected empty path, got %q", actual) | |
41 | } | |
42 | if strings.Index(err.Error(), expected) == -1 { | |
43 | t.Errorf("Expected %q to include %q", err, expected) | |
44 | } | |
45 | }) | |
46 | } | |
47 | if runtime.GOOS == "windows" { | |
48 | testcase("", "c:\\gopath\\src\\somewhere", "is not in") | |
49 | } else { | |
50 | testcase("", "/gopath/src/somewhere", "is not in") | |
51 | } | |
52 | testcase("", "./somewhere", "not an absolute") | |
53 | } |
0 | package main | |
1 | ||
2 | import ( | |
3 | "fmt" | |
4 | "go/ast" | |
5 | ) | |
6 | ||
7 | // A Visitor's Visit method is invoked for each node encountered by walkToReplace. | |
8 | // If the result visitor w is not nil, walkToReplace visits each of the children | |
9 | // of node with the visitor w, followed by a call of w.Visit(nil). | |
10 | type Visitor interface { | |
11 | Visit(node ast.Node, replace func(ast.Node)) (w Visitor) | |
12 | } | |
13 | ||
14 | // Helper functions for common node lists. They may be empty. | |
15 | ||
16 | func walkIdentList(v Visitor, list []*ast.Ident) { | |
17 | for i, x := range list { | |
18 | walkToReplace(v, x, func(r ast.Node) { | |
19 | list[i] = r.(*ast.Ident) | |
20 | }) | |
21 | } | |
22 | } | |
23 | ||
24 | func walkExprList(v Visitor, list []ast.Expr) { | |
25 | for i, x := range list { | |
26 | walkToReplace(v, x, func(r ast.Node) { | |
27 | list[i] = r.(ast.Expr) | |
28 | }) | |
29 | } | |
30 | } | |
31 | ||
32 | func walkStmtList(v Visitor, list []ast.Stmt) { | |
33 | for i, x := range list { | |
34 | walkToReplace(v, x, func(r ast.Node) { | |
35 | list[i] = r.(ast.Stmt) | |
36 | }) | |
37 | } | |
38 | } | |
39 | ||
40 | func walkDeclList(v Visitor, list []ast.Decl) { | |
41 | for i, x := range list { | |
42 | walkToReplace(v, x, func(r ast.Node) { | |
43 | list[i] = r.(ast.Decl) | |
44 | }) | |
45 | } | |
46 | } | |
47 | ||
48 | // WalkToReplace traverses an AST in depth-first order: It starts by calling | |
49 | // v.Visit(node); node must not be nil. If the visitor w returned by | |
50 | // v.Visit(node) is not nil, walkToReplace is invoked recursively with visitor | |
51 | // w for each of the non-nil children of node, followed by a call of | |
52 | // w.Visit(nil). | |
53 | func WalkReplace(v Visitor, node ast.Node) (replacement ast.Node) { | |
54 | walkToReplace(v, node, func(r ast.Node) { | |
55 | replacement = r | |
56 | }) | |
57 | return | |
58 | } | |
59 | ||
60 | func walkToReplace(v Visitor, node ast.Node, replace func(ast.Node)) { | |
61 | if v == nil { | |
62 | return | |
63 | } | |
64 | var replacement ast.Node | |
65 | repl := func(r ast.Node) { | |
66 | replacement = r | |
67 | replace(r) | |
68 | } | |
69 | ||
70 | v = v.Visit(node, repl) | |
71 | ||
72 | if replacement != nil { | |
73 | return | |
74 | } | |
75 | ||
76 | // walk children | |
77 | // (the order of the cases matches the order | |
78 | // of the corresponding node types in ast.go) | |
79 | switch n := node.(type) { | |
80 | ||
81 | // These are all leaves, so there's no sub-walk to do. | |
82 | // We just need to replace them on their parent with a copy. | |
83 | case *ast.Comment: | |
84 | cpy := *n | |
85 | replace(&cpy) | |
86 | case *ast.BadExpr: | |
87 | cpy := *n | |
88 | replace(&cpy) | |
89 | case *ast.Ident: | |
90 | cpy := *n | |
91 | replace(&cpy) | |
92 | case *ast.BasicLit: | |
93 | cpy := *n | |
94 | replace(&cpy) | |
95 | case *ast.BadDecl: | |
96 | cpy := *n | |
97 | replace(&cpy) | |
98 | case *ast.EmptyStmt: | |
99 | cpy := *n | |
100 | replace(&cpy) | |
101 | case *ast.BadStmt: | |
102 | cpy := *n | |
103 | replace(&cpy) | |
104 | ||
105 | case *ast.CommentGroup: | |
106 | cpy := *n | |
107 | ||
108 | if n.List != nil { | |
109 | cpy.List = make([]*ast.Comment, len(n.List)) | |
110 | copy(cpy.List, n.List) | |
111 | } | |
112 | ||
113 | for i, c := range cpy.List { | |
114 | walkToReplace(v, c, func(r ast.Node) { | |
115 | cpy.List[i] = r.(*ast.Comment) | |
116 | }) | |
117 | } | |
118 | replace(&cpy) | |
119 | ||
120 | case *ast.Field: | |
121 | cpy := *n | |
122 | if n.Names != nil { | |
123 | cpy.Names = make([]*ast.Ident, len(n.Names)) | |
124 | copy(cpy.Names, n.Names) | |
125 | } | |
126 | ||
127 | if cpy.Doc != nil { | |
128 | walkToReplace(v, cpy.Doc, func(r ast.Node) { | |
129 | cpy.Doc = r.(*ast.CommentGroup) | |
130 | }) | |
131 | } | |
132 | walkIdentList(v, cpy.Names) | |
133 | ||
134 | walkToReplace(v, cpy.Type, func(r ast.Node) { | |
135 | cpy.Type = r.(ast.Expr) | |
136 | }) | |
137 | if cpy.Tag != nil { | |
138 | walkToReplace(v, cpy.Tag, func(r ast.Node) { | |
139 | cpy.Tag = r.(*ast.BasicLit) | |
140 | }) | |
141 | } | |
142 | if cpy.Comment != nil { | |
143 | walkToReplace(v, cpy.Comment, func(r ast.Node) { | |
144 | cpy.Comment = r.(*ast.CommentGroup) | |
145 | }) | |
146 | } | |
147 | replace(&cpy) | |
148 | ||
149 | case *ast.FieldList: | |
150 | cpy := *n | |
151 | if n.List != nil { | |
152 | cpy.List = make([]*ast.Field, len(n.List)) | |
153 | copy(cpy.List, n.List) | |
154 | } | |
155 | ||
156 | for i, f := range cpy.List { | |
157 | walkToReplace(v, f, func(r ast.Node) { | |
158 | cpy.List[i] = r.(*ast.Field) | |
159 | }) | |
160 | } | |
161 | ||
162 | replace(&cpy) | |
163 | ||
164 | case *ast.Ellipsis: | |
165 | cpy := *n | |
166 | ||
167 | if cpy.Elt != nil { | |
168 | walkToReplace(v, cpy.Elt, func(r ast.Node) { | |
169 | cpy.Elt = r.(ast.Expr) | |
170 | }) | |
171 | } | |
172 | ||
173 | replace(&cpy) | |
174 | ||
175 | case *ast.FuncLit: | |
176 | cpy := *n | |
177 | walkToReplace(v, cpy.Type, func(r ast.Node) { | |
178 | cpy.Type = r.(*ast.FuncType) | |
179 | }) | |
180 | walkToReplace(v, cpy.Body, func(r ast.Node) { | |
181 | cpy.Body = r.(*ast.BlockStmt) | |
182 | }) | |
183 | ||
184 | replace(&cpy) | |
185 | case *ast.CompositeLit: | |
186 | cpy := *n | |
187 | if n.Elts != nil { | |
188 | cpy.Elts = make([]ast.Expr, len(n.Elts)) | |
189 | copy(cpy.Elts, n.Elts) | |
190 | } | |
191 | ||
192 | if cpy.Type != nil { | |
193 | walkToReplace(v, cpy.Type, func(r ast.Node) { | |
194 | cpy.Type = r.(ast.Expr) | |
195 | }) | |
196 | } | |
197 | walkExprList(v, cpy.Elts) | |
198 | ||
199 | replace(&cpy) | |
200 | case *ast.ParenExpr: | |
201 | cpy := *n | |
202 | walkToReplace(v, cpy.X, func(r ast.Node) { | |
203 | cpy.X = r.(ast.Expr) | |
204 | }) | |
205 | ||
206 | replace(&cpy) | |
207 | case *ast.SelectorExpr: | |
208 | cpy := *n | |
209 | walkToReplace(v, cpy.X, func(r ast.Node) { | |
210 | cpy.X = r.(ast.Expr) | |
211 | }) | |
212 | walkToReplace(v, cpy.Sel, func(r ast.Node) { | |
213 | cpy.Sel = r.(*ast.Ident) | |
214 | }) | |
215 | ||
216 | replace(&cpy) | |
217 | case *ast.IndexExpr: | |
218 | cpy := *n | |
219 | walkToReplace(v, cpy.X, func(r ast.Node) { | |
220 | cpy.X = r.(ast.Expr) | |
221 | }) | |
222 | walkToReplace(v, cpy.Index, func(r ast.Node) { | |
223 | cpy.Index = r.(ast.Expr) | |
224 | }) | |
225 | ||
226 | replace(&cpy) | |
227 | case *ast.SliceExpr: | |
228 | cpy := *n | |
229 | walkToReplace(v, cpy.X, func(r ast.Node) { | |
230 | cpy.X = r.(ast.Expr) | |
231 | }) | |
232 | if cpy.Low != nil { | |
233 | walkToReplace(v, cpy.Low, func(r ast.Node) { | |
234 | cpy.Low = r.(ast.Expr) | |
235 | }) | |
236 | } | |
237 | if cpy.High != nil { | |
238 | walkToReplace(v, cpy.High, func(r ast.Node) { | |
239 | cpy.High = r.(ast.Expr) | |
240 | }) | |
241 | } | |
242 | if cpy.Max != nil { | |
243 | walkToReplace(v, cpy.Max, func(r ast.Node) { | |
244 | cpy.Max = r.(ast.Expr) | |
245 | }) | |
246 | } | |
247 | ||
248 | replace(&cpy) | |
249 | case *ast.TypeAssertExpr: | |
250 | cpy := *n | |
251 | walkToReplace(v, cpy.X, func(r ast.Node) { | |
252 | cpy.X = r.(ast.Expr) | |
253 | }) | |
254 | if cpy.Type != nil { | |
255 | walkToReplace(v, cpy.Type, func(r ast.Node) { | |
256 | cpy.Type = r.(ast.Expr) | |
257 | }) | |
258 | } | |
259 | replace(&cpy) | |
260 | case *ast.CallExpr: | |
261 | cpy := *n | |
262 | if n.Args != nil { | |
263 | cpy.Args = make([]ast.Expr, len(n.Args)) | |
264 | copy(cpy.Args, n.Args) | |
265 | } | |
266 | ||
267 | walkToReplace(v, cpy.Fun, func(r ast.Node) { | |
268 | cpy.Fun = r.(ast.Expr) | |
269 | }) | |
270 | walkExprList(v, cpy.Args) | |
271 | ||
272 | replace(&cpy) | |
273 | case *ast.StarExpr: | |
274 | cpy := *n | |
275 | walkToReplace(v, cpy.X, func(r ast.Node) { | |
276 | cpy.X = r.(ast.Expr) | |
277 | }) | |
278 | ||
279 | replace(&cpy) | |
280 | case *ast.UnaryExpr: | |
281 | cpy := *n | |
282 | walkToReplace(v, cpy.X, func(r ast.Node) { | |
283 | cpy.X = r.(ast.Expr) | |
284 | }) | |
285 | ||
286 | replace(&cpy) | |
287 | case *ast.BinaryExpr: | |
288 | cpy := *n | |
289 | walkToReplace(v, cpy.X, func(r ast.Node) { | |
290 | cpy.X = r.(ast.Expr) | |
291 | }) | |
292 | walkToReplace(v, cpy.Y, func(r ast.Node) { | |
293 | cpy.Y = r.(ast.Expr) | |
294 | }) | |
295 | ||
296 | replace(&cpy) | |
297 | case *ast.KeyValueExpr: | |
298 | cpy := *n | |
299 | walkToReplace(v, cpy.Key, func(r ast.Node) { | |
300 | cpy.Key = r.(ast.Expr) | |
301 | }) | |
302 | walkToReplace(v, cpy.Value, func(r ast.Node) { | |
303 | cpy.Value = r.(ast.Expr) | |
304 | }) | |
305 | ||
306 | replace(&cpy) | |
307 | ||
308 | // Types | |
309 | case *ast.ArrayType: | |
310 | cpy := *n | |
311 | if cpy.Len != nil { | |
312 | walkToReplace(v, cpy.Len, func(r ast.Node) { | |
313 | cpy.Len = r.(ast.Expr) | |
314 | }) | |
315 | } | |
316 | walkToReplace(v, cpy.Elt, func(r ast.Node) { | |
317 | cpy.Elt = r.(ast.Expr) | |
318 | }) | |
319 | ||
320 | replace(&cpy) | |
321 | case *ast.StructType: | |
322 | cpy := *n | |
323 | walkToReplace(v, cpy.Fields, func(r ast.Node) { | |
324 | cpy.Fields = r.(*ast.FieldList) | |
325 | }) | |
326 | ||
327 | replace(&cpy) | |
328 | case *ast.FuncType: | |
329 | cpy := *n | |
330 | if cpy.Params != nil { | |
331 | walkToReplace(v, cpy.Params, func(r ast.Node) { | |
332 | cpy.Params = r.(*ast.FieldList) | |
333 | }) | |
334 | } | |
335 | if cpy.Results != nil { | |
336 | walkToReplace(v, cpy.Results, func(r ast.Node) { | |
337 | cpy.Results = r.(*ast.FieldList) | |
338 | }) | |
339 | } | |
340 | ||
341 | replace(&cpy) | |
342 | case *ast.InterfaceType: | |
343 | cpy := *n | |
344 | walkToReplace(v, cpy.Methods, func(r ast.Node) { | |
345 | cpy.Methods = r.(*ast.FieldList) | |
346 | }) | |
347 | ||
348 | replace(&cpy) | |
349 | case *ast.MapType: | |
350 | cpy := *n | |
351 | walkToReplace(v, cpy.Key, func(r ast.Node) { | |
352 | cpy.Key = r.(ast.Expr) | |
353 | }) | |
354 | walkToReplace(v, cpy.Value, func(r ast.Node) { | |
355 | cpy.Value = r.(ast.Expr) | |
356 | }) | |
357 | ||
358 | replace(&cpy) | |
359 | case *ast.ChanType: | |
360 | cpy := *n | |
361 | walkToReplace(v, cpy.Value, func(r ast.Node) { | |
362 | cpy.Value = r.(ast.Expr) | |
363 | }) | |
364 | ||
365 | replace(&cpy) | |
366 | case *ast.DeclStmt: | |
367 | cpy := *n | |
368 | walkToReplace(v, cpy.Decl, func(r ast.Node) { | |
369 | cpy.Decl = r.(ast.Decl) | |
370 | }) | |
371 | ||
372 | replace(&cpy) | |
373 | case *ast.LabeledStmt: | |
374 | cpy := *n | |
375 | walkToReplace(v, cpy.Label, func(r ast.Node) { | |
376 | cpy.Label = r.(*ast.Ident) | |
377 | }) | |
378 | walkToReplace(v, cpy.Stmt, func(r ast.Node) { | |
379 | cpy.Stmt = r.(ast.Stmt) | |
380 | }) | |
381 | ||
382 | replace(&cpy) | |
383 | case *ast.ExprStmt: | |
384 | cpy := *n | |
385 | walkToReplace(v, cpy.X, func(r ast.Node) { | |
386 | cpy.X = r.(ast.Expr) | |
387 | }) | |
388 | ||
389 | replace(&cpy) | |
390 | case *ast.SendStmt: | |
391 | cpy := *n | |
392 | walkToReplace(v, cpy.Chan, func(r ast.Node) { | |
393 | cpy.Chan = r.(ast.Expr) | |
394 | }) | |
395 | walkToReplace(v, cpy.Value, func(r ast.Node) { | |
396 | cpy.Value = r.(ast.Expr) | |
397 | }) | |
398 | ||
399 | replace(&cpy) | |
400 | case *ast.IncDecStmt: | |
401 | cpy := *n | |
402 | walkToReplace(v, cpy.X, func(r ast.Node) { | |
403 | cpy.X = r.(ast.Expr) | |
404 | }) | |
405 | ||
406 | replace(&cpy) | |
407 | case *ast.AssignStmt: | |
408 | cpy := *n | |
409 | if n.Lhs != nil { | |
410 | cpy.Lhs = make([]ast.Expr, len(n.Lhs)) | |
411 | copy(cpy.Lhs, n.Lhs) | |
412 | } | |
413 | if n.Rhs != nil { | |
414 | cpy.Rhs = make([]ast.Expr, len(n.Rhs)) | |
415 | copy(cpy.Rhs, n.Rhs) | |
416 | } | |
417 | ||
418 | walkExprList(v, cpy.Lhs) | |
419 | walkExprList(v, cpy.Rhs) | |
420 | ||
421 | replace(&cpy) | |
422 | case *ast.GoStmt: | |
423 | cpy := *n | |
424 | walkToReplace(v, cpy.Call, func(r ast.Node) { | |
425 | cpy.Call = r.(*ast.CallExpr) | |
426 | }) | |
427 | ||
428 | replace(&cpy) | |
429 | case *ast.DeferStmt: | |
430 | cpy := *n | |
431 | walkToReplace(v, cpy.Call, func(r ast.Node) { | |
432 | cpy.Call = r.(*ast.CallExpr) | |
433 | }) | |
434 | ||
435 | replace(&cpy) | |
436 | case *ast.ReturnStmt: | |
437 | cpy := *n | |
438 | if n.Results != nil { | |
439 | cpy.Results = make([]ast.Expr, len(n.Results)) | |
440 | copy(cpy.Results, n.Results) | |
441 | } | |
442 | ||
443 | walkExprList(v, cpy.Results) | |
444 | ||
445 | replace(&cpy) | |
446 | case *ast.BranchStmt: | |
447 | cpy := *n | |
448 | if cpy.Label != nil { | |
449 | walkToReplace(v, cpy.Label, func(r ast.Node) { | |
450 | cpy.Label = r.(*ast.Ident) | |
451 | }) | |
452 | } | |
453 | ||
454 | replace(&cpy) | |
455 | case *ast.BlockStmt: | |
456 | cpy := *n | |
457 | if n.List != nil { | |
458 | cpy.List = make([]ast.Stmt, len(n.List)) | |
459 | copy(cpy.List, n.List) | |
460 | } | |
461 | ||
462 | walkStmtList(v, cpy.List) | |
463 | ||
464 | replace(&cpy) | |
465 | case *ast.IfStmt: | |
466 | cpy := *n | |
467 | ||
468 | if cpy.Init != nil { | |
469 | walkToReplace(v, cpy.Init, func(r ast.Node) { | |
470 | cpy.Init = r.(ast.Stmt) | |
471 | }) | |
472 | } | |
473 | walkToReplace(v, cpy.Cond, func(r ast.Node) { | |
474 | cpy.Cond = r.(ast.Expr) | |
475 | }) | |
476 | walkToReplace(v, cpy.Body, func(r ast.Node) { | |
477 | cpy.Body = r.(*ast.BlockStmt) | |
478 | }) | |
479 | if cpy.Else != nil { | |
480 | walkToReplace(v, cpy.Else, func(r ast.Node) { | |
481 | cpy.Else = r.(ast.Stmt) | |
482 | }) | |
483 | } | |
484 | ||
485 | replace(&cpy) | |
486 | case *ast.CaseClause: | |
487 | cpy := *n | |
488 | if n.List != nil { | |
489 | cpy.List = make([]ast.Expr, len(n.List)) | |
490 | copy(cpy.List, n.List) | |
491 | } | |
492 | if n.Body != nil { | |
493 | cpy.Body = make([]ast.Stmt, len(n.Body)) | |
494 | copy(cpy.Body, n.Body) | |
495 | } | |
496 | ||
497 | walkExprList(v, cpy.List) | |
498 | walkStmtList(v, cpy.Body) | |
499 | ||
500 | replace(&cpy) | |
501 | case *ast.SwitchStmt: | |
502 | cpy := *n | |
503 | if cpy.Init != nil { | |
504 | walkToReplace(v, cpy.Init, func(r ast.Node) { | |
505 | cpy.Init = r.(ast.Stmt) | |
506 | }) | |
507 | } | |
508 | if cpy.Tag != nil { | |
509 | walkToReplace(v, cpy.Tag, func(r ast.Node) { | |
510 | cpy.Tag = r.(ast.Expr) | |
511 | }) | |
512 | } | |
513 | walkToReplace(v, cpy.Body, func(r ast.Node) { | |
514 | cpy.Body = r.(*ast.BlockStmt) | |
515 | }) | |
516 | ||
517 | replace(&cpy) | |
518 | case *ast.TypeSwitchStmt: | |
519 | cpy := *n | |
520 | if cpy.Init != nil { | |
521 | walkToReplace(v, cpy.Init, func(r ast.Node) { | |
522 | cpy.Init = r.(ast.Stmt) | |
523 | }) | |
524 | } | |
525 | walkToReplace(v, cpy.Assign, func(r ast.Node) { | |
526 | cpy.Assign = r.(ast.Stmt) | |
527 | }) | |
528 | walkToReplace(v, cpy.Body, func(r ast.Node) { | |
529 | cpy.Body = r.(*ast.BlockStmt) | |
530 | }) | |
531 | ||
532 | replace(&cpy) | |
533 | case *ast.CommClause: | |
534 | cpy := *n | |
535 | if n.Body != nil { | |
536 | cpy.Body = make([]ast.Stmt, len(n.Body)) | |
537 | copy(cpy.Body, n.Body) | |
538 | } | |
539 | ||
540 | if cpy.Comm != nil { | |
541 | walkToReplace(v, cpy.Comm, func(r ast.Node) { | |
542 | cpy.Comm = r.(ast.Stmt) | |
543 | }) | |
544 | } | |
545 | walkStmtList(v, cpy.Body) | |
546 | ||
547 | replace(&cpy) | |
548 | case *ast.SelectStmt: | |
549 | cpy := *n | |
550 | walkToReplace(v, cpy.Body, func(r ast.Node) { | |
551 | cpy.Body = r.(*ast.BlockStmt) | |
552 | }) | |
553 | ||
554 | replace(&cpy) | |
555 | case *ast.ForStmt: | |
556 | cpy := *n | |
557 | if cpy.Init != nil { | |
558 | walkToReplace(v, cpy.Init, func(r ast.Node) { | |
559 | cpy.Init = r.(ast.Stmt) | |
560 | }) | |
561 | } | |
562 | if cpy.Cond != nil { | |
563 | walkToReplace(v, cpy.Cond, func(r ast.Node) { | |
564 | cpy.Cond = r.(ast.Expr) | |
565 | }) | |
566 | } | |
567 | if cpy.Post != nil { | |
568 | walkToReplace(v, cpy.Post, func(r ast.Node) { | |
569 | cpy.Post = r.(ast.Stmt) | |
570 | }) | |
571 | } | |
572 | walkToReplace(v, cpy.Body, func(r ast.Node) { | |
573 | cpy.Body = r.(*ast.BlockStmt) | |
574 | }) | |
575 | ||
576 | replace(&cpy) | |
577 | case *ast.RangeStmt: | |
578 | cpy := *n | |
579 | if cpy.Key != nil { | |
580 | walkToReplace(v, cpy.Key, func(r ast.Node) { | |
581 | cpy.Key = r.(ast.Expr) | |
582 | }) | |
583 | } | |
584 | if cpy.Value != nil { | |
585 | walkToReplace(v, cpy.Value, func(r ast.Node) { | |
586 | cpy.Value = r.(ast.Expr) | |
587 | }) | |
588 | } | |
589 | walkToReplace(v, cpy.X, func(r ast.Node) { | |
590 | cpy.X = r.(ast.Expr) | |
591 | }) | |
592 | walkToReplace(v, cpy.Body, func(r ast.Node) { | |
593 | cpy.Body = r.(*ast.BlockStmt) | |
594 | }) | |
595 | ||
596 | // Declarations | |
597 | replace(&cpy) | |
598 | case *ast.ImportSpec: | |
599 | cpy := *n | |
600 | if cpy.Doc != nil { | |
601 | walkToReplace(v, cpy.Doc, func(r ast.Node) { | |
602 | cpy.Doc = r.(*ast.CommentGroup) | |
603 | }) | |
604 | } | |
605 | if cpy.Name != nil { | |
606 | walkToReplace(v, cpy.Name, func(r ast.Node) { | |
607 | cpy.Name = r.(*ast.Ident) | |
608 | }) | |
609 | } | |
610 | walkToReplace(v, cpy.Path, func(r ast.Node) { | |
611 | cpy.Path = r.(*ast.BasicLit) | |
612 | }) | |
613 | if cpy.Comment != nil { | |
614 | walkToReplace(v, cpy.Comment, func(r ast.Node) { | |
615 | cpy.Comment = r.(*ast.CommentGroup) | |
616 | }) | |
617 | } | |
618 | ||
619 | replace(&cpy) | |
620 | case *ast.ValueSpec: | |
621 | cpy := *n | |
622 | if n.Names != nil { | |
623 | cpy.Names = make([]*ast.Ident, len(n.Names)) | |
624 | copy(cpy.Names, n.Names) | |
625 | } | |
626 | if n.Values != nil { | |
627 | cpy.Values = make([]ast.Expr, len(n.Values)) | |
628 | copy(cpy.Values, n.Values) | |
629 | } | |
630 | ||
631 | if cpy.Doc != nil { | |
632 | walkToReplace(v, cpy.Doc, func(r ast.Node) { | |
633 | cpy.Doc = r.(*ast.CommentGroup) | |
634 | }) | |
635 | } | |
636 | ||
637 | walkIdentList(v, cpy.Names) | |
638 | ||
639 | if cpy.Type != nil { | |
640 | walkToReplace(v, cpy.Type, func(r ast.Node) { | |
641 | cpy.Type = r.(ast.Expr) | |
642 | }) | |
643 | } | |
644 | ||
645 | walkExprList(v, cpy.Values) | |
646 | ||
647 | if cpy.Comment != nil { | |
648 | walkToReplace(v, cpy.Comment, func(r ast.Node) { | |
649 | cpy.Comment = r.(*ast.CommentGroup) | |
650 | }) | |
651 | } | |
652 | ||
653 | replace(&cpy) | |
654 | ||
655 | case *ast.TypeSpec: | |
656 | cpy := *n | |
657 | ||
658 | if cpy.Doc != nil { | |
659 | walkToReplace(v, cpy.Doc, func(r ast.Node) { | |
660 | cpy.Doc = r.(*ast.CommentGroup) | |
661 | }) | |
662 | } | |
663 | walkToReplace(v, cpy.Name, func(r ast.Node) { | |
664 | cpy.Name = r.(*ast.Ident) | |
665 | }) | |
666 | walkToReplace(v, cpy.Type, func(r ast.Node) { | |
667 | cpy.Type = r.(ast.Expr) | |
668 | }) | |
669 | if cpy.Comment != nil { | |
670 | walkToReplace(v, cpy.Comment, func(r ast.Node) { | |
671 | cpy.Comment = r.(*ast.CommentGroup) | |
672 | }) | |
673 | } | |
674 | ||
675 | replace(&cpy) | |
676 | case *ast.GenDecl: | |
677 | cpy := *n | |
678 | if n.Specs != nil { | |
679 | cpy.Specs = make([]ast.Spec, len(n.Specs)) | |
680 | copy(cpy.Specs, n.Specs) | |
681 | } | |
682 | ||
683 | if cpy.Doc != nil { | |
684 | walkToReplace(v, cpy.Doc, func(r ast.Node) { | |
685 | cpy.Doc = r.(*ast.CommentGroup) | |
686 | }) | |
687 | } | |
688 | for i, s := range cpy.Specs { | |
689 | walkToReplace(v, s, func(r ast.Node) { | |
690 | cpy.Specs[i] = r.(ast.Spec) | |
691 | }) | |
692 | } | |
693 | ||
694 | replace(&cpy) | |
695 | case *ast.FuncDecl: | |
696 | cpy := *n | |
697 | ||
698 | if cpy.Doc != nil { | |
699 | walkToReplace(v, cpy.Doc, func(r ast.Node) { | |
700 | cpy.Doc = r.(*ast.CommentGroup) | |
701 | }) | |
702 | } | |
703 | if cpy.Recv != nil { | |
704 | walkToReplace(v, cpy.Recv, func(r ast.Node) { | |
705 | cpy.Recv = r.(*ast.FieldList) | |
706 | }) | |
707 | } | |
708 | walkToReplace(v, cpy.Name, func(r ast.Node) { | |
709 | cpy.Name = r.(*ast.Ident) | |
710 | }) | |
711 | walkToReplace(v, cpy.Type, func(r ast.Node) { | |
712 | cpy.Type = r.(*ast.FuncType) | |
713 | }) | |
714 | if cpy.Body != nil { | |
715 | walkToReplace(v, cpy.Body, func(r ast.Node) { | |
716 | cpy.Body = r.(*ast.BlockStmt) | |
717 | }) | |
718 | } | |
719 | ||
720 | // Files and packages | |
721 | replace(&cpy) | |
722 | case *ast.File: | |
723 | cpy := *n | |
724 | ||
725 | if cpy.Doc != nil { | |
726 | walkToReplace(v, cpy.Doc, func(r ast.Node) { | |
727 | cpy.Doc = r.(*ast.CommentGroup) | |
728 | }) | |
729 | } | |
730 | walkToReplace(v, cpy.Name, func(r ast.Node) { | |
731 | cpy.Name = r.(*ast.Ident) | |
732 | }) | |
733 | walkDeclList(v, cpy.Decls) | |
734 | // don't walk cpy.Comments - they have been | |
735 | // visited already through the individual | |
736 | // nodes | |
737 | ||
738 | replace(&cpy) | |
739 | case *ast.Package: | |
740 | cpy := *n | |
741 | cpy.Files = map[string]*ast.File{} | |
742 | ||
743 | for i, f := range n.Files { | |
744 | cpy.Files[i] = f | |
745 | walkToReplace(v, f, func(r ast.Node) { | |
746 | cpy.Files[i] = r.(*ast.File) | |
747 | }) | |
748 | } | |
749 | replace(&cpy) | |
750 | ||
751 | default: | |
752 | panic(fmt.Sprintf("walkToReplace: unexpected node type %T", n)) | |
753 | } | |
754 | ||
755 | if v != nil { | |
756 | v.Visit(nil, func(ast.Node) { panic("can't replace the go-up nil") }) | |
757 | } | |
758 | } |
0 | package main | |
1 | ||
2 | import ( | |
3 | "fmt" | |
4 | "go/ast" | |
5 | "go/token" | |
6 | ) | |
7 | ||
8 | type sourceContext struct { | |
9 | pkg *ast.Ident | |
10 | imports []*ast.ImportSpec | |
11 | interfaces []iface | |
12 | types []*ast.TypeSpec | |
13 | } | |
14 | ||
15 | func (sc *sourceContext) validate() error { | |
16 | if len(sc.interfaces) != 1 { | |
17 | return fmt.Errorf("found %d interfaces, expecting exactly 1", len(sc.interfaces)) | |
18 | } | |
19 | for _, i := range sc.interfaces { | |
20 | for _, m := range i.methods { | |
21 | if len(m.results) < 1 { | |
22 | return fmt.Errorf("method %q of interface %q has no result types", m.name, i.name) | |
23 | } | |
24 | } | |
25 | } | |
26 | return nil | |
27 | } | |
28 | ||
29 | func (sc *sourceContext) importDecls() (decls []ast.Decl) { | |
30 | have := map[string]struct{}{} | |
31 | notHave := func(is *ast.ImportSpec) bool { | |
32 | if _, has := have[is.Path.Value]; has { | |
33 | return false | |
34 | } | |
35 | have[is.Path.Value] = struct{}{} | |
36 | return true | |
37 | } | |
38 | ||
39 | for _, is := range sc.imports { | |
40 | if notHave(is) { | |
41 | decls = append(decls, importFor(is)) | |
42 | } | |
43 | } | |
44 | ||
45 | for _, is := range fetchImports() { | |
46 | if notHave(is) { | |
47 | decls = append(decls, &ast.GenDecl{Tok: token.IMPORT, Specs: []ast.Spec{is}}) | |
48 | } | |
49 | } | |
50 | ||
51 | return | |
52 | } |
0 | package foo | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "encoding/json" | |
5 | "errors" | |
6 | "net/http" | |
7 | ||
8 | "github.com/go-kit/kit/endpoint" | |
9 | httptransport "github.com/go-kit/kit/transport/http" | |
10 | ) | |
11 | ||
12 | type ExampleService struct { | |
13 | } | |
14 | ||
15 | type ExampleRequest struct { | |
16 | I int | |
17 | S string | |
18 | } | |
19 | type ExampleResponse struct { | |
20 | S string | |
21 | Err error | |
22 | } | |
23 | ||
24 | type Endpoints struct { | |
25 | ExampleEndpoint endpoint.Endpoint | |
26 | } | |
27 | ||
28 | func (f ExampleService) ExampleEndpoint(ctx context.Context, i int, s string) (string, error) { | |
29 | panic(errors.New("not implemented")) | |
30 | } | |
31 | ||
32 | func makeExampleEndpoint(f ExampleService) endpoint.Endpoint { | |
33 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
34 | req := request.(ExampleRequest) | |
35 | s, err := f.ExampleEndpoint(ctx, req.I, req.S) | |
36 | return ExampleResponse{S: s, Err: err}, nil | |
37 | } | |
38 | } | |
39 | ||
40 | func inlineHandlerBuilder(m *http.ServeMux, endpoints Endpoints) { | |
41 | m.Handle("/bar", httptransport.NewServer(endpoints.ExampleEndpoint, DecodeExampleRequest, EncodeExampleResponse)) | |
42 | } | |
43 | ||
44 | func NewHTTPHandler(endpoints Endpoints) http.Handler { | |
45 | m := http.NewServeMux() | |
46 | inlineHandlerBuilder(m, endpoints) | |
47 | return m | |
48 | } | |
49 | ||
50 | func DecodeExampleRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
51 | var req ExampleRequest | |
52 | err := json.NewDecoder(r.Body).Decode(&req) | |
53 | return req, err | |
54 | } | |
55 | ||
56 | func EncodeExampleResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
57 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
58 | return json.NewEncoder(w).Encode(response) | |
59 | } |
0 | package endpoints | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | ||
5 | "github.com/go-kit/kit/cmd/kitgen/testdata/anonfields/default/service" | |
6 | "github.com/go-kit/kit/endpoint" | |
7 | ) | |
8 | ||
9 | type FooRequest struct { | |
10 | I int | |
11 | S string | |
12 | } | |
13 | type FooResponse struct { | |
14 | I int | |
15 | Err error | |
16 | } | |
17 | ||
18 | func MakeFooEndpoint(s service.Service) endpoint.Endpoint { | |
19 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
20 | req := request.(FooRequest) | |
21 | i, err := s.Foo(ctx, req.I, req.S) | |
22 | return FooResponse{I: i, Err: err}, nil | |
23 | } | |
24 | } | |
25 | ||
26 | type Endpoints struct { | |
27 | Foo endpoint.Endpoint | |
28 | } |
0 | package http | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "encoding/json" | |
5 | "net/http" | |
6 | ||
7 | "github.com/go-kit/kit/cmd/kitgen/testdata/anonfields/default/endpoints" | |
8 | httptransport "github.com/go-kit/kit/transport/http" | |
9 | ) | |
10 | ||
11 | func NewHTTPHandler(endpoints endpoints.Endpoints) http.Handler { | |
12 | m := http.NewServeMux() | |
13 | m.Handle("/foo", httptransport.NewServer(endpoints.Foo, DecodeFooRequest, EncodeFooResponse)) | |
14 | return m | |
15 | } | |
16 | func DecodeFooRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
17 | var req endpoints.FooRequest | |
18 | err := json.NewDecoder(r.Body).Decode(&req) | |
19 | return req, err | |
20 | } | |
21 | func EncodeFooResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
22 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
23 | return json.NewEncoder(w).Encode(response) | |
24 | } |
0 | package service | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "errors" | |
5 | ) | |
6 | ||
7 | type Service struct { | |
8 | } | |
9 | ||
10 | func (s Service) Foo(ctx context.Context, i int, string1 string) (int, error) { | |
11 | panic(errors.New("not implemented")) | |
12 | } |
0 | package foo | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "encoding/json" | |
5 | "errors" | |
6 | "net/http" | |
7 | ||
8 | "github.com/go-kit/kit/endpoint" | |
9 | ||
10 | httptransport "github.com/go-kit/kit/transport/http" | |
11 | ) | |
12 | ||
13 | type Service struct { | |
14 | } | |
15 | ||
16 | func (s Service) Foo(ctx context.Context, i int, string1 string) (int, error) { | |
17 | panic(errors.New("not implemented")) | |
18 | } | |
19 | ||
20 | type FooRequest struct { | |
21 | I int | |
22 | S string | |
23 | } | |
24 | type FooResponse struct { | |
25 | I int | |
26 | Err error | |
27 | } | |
28 | ||
29 | func MakeFooEndpoint(s Service) endpoint.Endpoint { | |
30 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
31 | req := request.(FooRequest) | |
32 | i, err := s.Foo(ctx, req.I, req.S) | |
33 | return FooResponse{I: i, Err: err}, nil | |
34 | } | |
35 | } | |
36 | ||
37 | type Endpoints struct { | |
38 | Foo endpoint.Endpoint | |
39 | } | |
40 | ||
41 | func NewHTTPHandler(endpoints Endpoints) http.Handler { | |
42 | m := http.NewServeMux() | |
43 | m.Handle("/foo", httptransport.NewServer(endpoints.Foo, DecodeFooRequest, EncodeFooResponse)) | |
44 | return m | |
45 | } | |
46 | func DecodeFooRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
47 | var req FooRequest | |
48 | err := json.NewDecoder(r.Body).Decode(&req) | |
49 | return req, err | |
50 | } | |
51 | func EncodeFooResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
52 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
53 | return json.NewEncoder(w).Encode(response) | |
54 | } |
0 | package foo | |
1 | ||
2 | // from https://github.com/go-kit/kit/pull/589#issuecomment-319937530 | |
3 | type Service interface { | |
4 | Foo(context.Context, int, string) (int, error) | |
5 | } |
0 | package endpoints | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | ||
5 | "github.com/go-kit/kit/cmd/kitgen/testdata/foo/default/service" | |
6 | "github.com/go-kit/kit/endpoint" | |
7 | ) | |
8 | ||
9 | type BarRequest struct { | |
10 | I int | |
11 | S string | |
12 | } | |
13 | type BarResponse struct { | |
14 | S string | |
15 | Err error | |
16 | } | |
17 | ||
18 | func MakeBarEndpoint(f service.FooService) endpoint.Endpoint { | |
19 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
20 | req := request.(BarRequest) | |
21 | s, err := f.Bar(ctx, req.I, req.S) | |
22 | return BarResponse{S: s, Err: err}, nil | |
23 | } | |
24 | } | |
25 | ||
26 | type Endpoints struct { | |
27 | Bar endpoint.Endpoint | |
28 | } |
0 | package http | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "encoding/json" | |
5 | "net/http" | |
6 | ||
7 | "github.com/go-kit/kit/cmd/kitgen/testdata/foo/default/endpoints" | |
8 | httptransport "github.com/go-kit/kit/transport/http" | |
9 | ) | |
10 | ||
11 | func NewHTTPHandler(endpoints endpoints.Endpoints) http.Handler { | |
12 | m := http.NewServeMux() | |
13 | m.Handle("/bar", httptransport.NewServer(endpoints.Bar, DecodeBarRequest, EncodeBarResponse)) | |
14 | return m | |
15 | } | |
16 | func DecodeBarRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
17 | var req endpoints.BarRequest | |
18 | err := json.NewDecoder(r.Body).Decode(&req) | |
19 | return req, err | |
20 | } | |
21 | func EncodeBarResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
22 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
23 | return json.NewEncoder(w).Encode(response) | |
24 | } |
0 | package service | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "errors" | |
5 | ) | |
6 | ||
7 | type FooService struct { | |
8 | } | |
9 | ||
10 | func (f FooService) Bar(ctx context.Context, i int, s string) (string, error) { | |
11 | panic(errors.New("not implemented")) | |
12 | } |
0 | package foo | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "encoding/json" | |
5 | "errors" | |
6 | "net/http" | |
7 | ||
8 | "github.com/go-kit/kit/endpoint" | |
9 | ||
10 | httptransport "github.com/go-kit/kit/transport/http" | |
11 | ) | |
12 | ||
13 | type FooService struct { | |
14 | } | |
15 | ||
16 | func (f FooService) Bar(ctx context.Context, i int, s string) (string, error) { | |
17 | panic(errors.New("not implemented")) | |
18 | } | |
19 | ||
20 | type BarRequest struct { | |
21 | I int | |
22 | S string | |
23 | } | |
24 | type BarResponse struct { | |
25 | S string | |
26 | Err error | |
27 | } | |
28 | ||
29 | func MakeBarEndpoint(f FooService) endpoint.Endpoint { | |
30 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
31 | req := request.(BarRequest) | |
32 | s, err := f.Bar(ctx, req.I, req.S) | |
33 | return BarResponse{S: s, Err: err}, nil | |
34 | } | |
35 | } | |
36 | ||
37 | type Endpoints struct { | |
38 | Bar endpoint.Endpoint | |
39 | } | |
40 | ||
41 | func NewHTTPHandler(endpoints Endpoints) http.Handler { | |
42 | m := http.NewServeMux() | |
43 | m.Handle("/bar", httptransport.NewServer(endpoints.Bar, DecodeBarRequest, EncodeBarResponse)) | |
44 | return m | |
45 | } | |
46 | func DecodeBarRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
47 | var req BarRequest | |
48 | err := json.NewDecoder(r.Body).Decode(&req) | |
49 | return req, err | |
50 | } | |
51 | func EncodeBarResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
52 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
53 | return json.NewEncoder(w).Encode(response) | |
54 | } |
0 | package foo | |
1 | ||
2 | type FooService interface { | |
3 | Bar(ctx context.Context, i int, s string) (string, error) | |
4 | } |
0 | package endpoints | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | ||
5 | "github.com/go-kit/kit/cmd/kitgen/testdata/profilesvc/default/service" | |
6 | "github.com/go-kit/kit/endpoint" | |
7 | ) | |
8 | ||
9 | type PostProfileRequest struct { | |
10 | P service.Profile | |
11 | } | |
12 | type PostProfileResponse struct { | |
13 | Err error | |
14 | } | |
15 | ||
16 | func MakePostProfileEndpoint(s service.Service) endpoint.Endpoint { | |
17 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
18 | req := request.(PostProfileRequest) | |
19 | err := s.PostProfile(ctx, req.P) | |
20 | return PostProfileResponse{Err: err}, nil | |
21 | } | |
22 | } | |
23 | ||
24 | type GetProfileRequest struct { | |
25 | Id string | |
26 | } | |
27 | type GetProfileResponse struct { | |
28 | P service.Profile | |
29 | Err error | |
30 | } | |
31 | ||
32 | func MakeGetProfileEndpoint(s service.Service) endpoint.Endpoint { | |
33 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
34 | req := request.(GetProfileRequest) | |
35 | P, err := s.GetProfile(ctx, req.Id) | |
36 | return GetProfileResponse{P: P, Err: err}, nil | |
37 | } | |
38 | } | |
39 | ||
40 | type PutProfileRequest struct { | |
41 | Id string | |
42 | P service.Profile | |
43 | } | |
44 | type PutProfileResponse struct { | |
45 | Err error | |
46 | } | |
47 | ||
48 | func MakePutProfileEndpoint(s service.Service) endpoint.Endpoint { | |
49 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
50 | req := request.(PutProfileRequest) | |
51 | err := s.PutProfile(ctx, req.Id, req.P) | |
52 | return PutProfileResponse{Err: err}, nil | |
53 | } | |
54 | } | |
55 | ||
56 | type PatchProfileRequest struct { | |
57 | Id string | |
58 | P service.Profile | |
59 | } | |
60 | type PatchProfileResponse struct { | |
61 | Err error | |
62 | } | |
63 | ||
64 | func MakePatchProfileEndpoint(s service.Service) endpoint.Endpoint { | |
65 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
66 | req := request.(PatchProfileRequest) | |
67 | err := s.PatchProfile(ctx, req.Id, req.P) | |
68 | return PatchProfileResponse{Err: err}, nil | |
69 | } | |
70 | } | |
71 | ||
72 | type DeleteProfileRequest struct { | |
73 | Id string | |
74 | } | |
75 | type DeleteProfileResponse struct { | |
76 | Err error | |
77 | } | |
78 | ||
79 | func MakeDeleteProfileEndpoint(s service.Service) endpoint.Endpoint { | |
80 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
81 | req := request.(DeleteProfileRequest) | |
82 | err := s.DeleteProfile(ctx, req.Id) | |
83 | return DeleteProfileResponse{Err: err}, nil | |
84 | } | |
85 | } | |
86 | ||
87 | type GetAddressesRequest struct { | |
88 | ProfileID string | |
89 | } | |
90 | type GetAddressesResponse struct { | |
91 | S []service.Address | |
92 | Err error | |
93 | } | |
94 | ||
95 | func MakeGetAddressesEndpoint(s service.Service) endpoint.Endpoint { | |
96 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
97 | req := request.(GetAddressesRequest) | |
98 | slice1, err := s.GetAddresses(ctx, req.ProfileID) | |
99 | return GetAddressesResponse{S: slice1, Err: err}, nil | |
100 | } | |
101 | } | |
102 | ||
103 | type GetAddressRequest struct { | |
104 | ProfileID string | |
105 | AddressID string | |
106 | } | |
107 | type GetAddressResponse struct { | |
108 | A service.Address | |
109 | Err error | |
110 | } | |
111 | ||
112 | func MakeGetAddressEndpoint(s service.Service) endpoint.Endpoint { | |
113 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
114 | req := request.(GetAddressRequest) | |
115 | A, err := s.GetAddress(ctx, req.ProfileID, req.AddressID) | |
116 | return GetAddressResponse{A: A, Err: err}, nil | |
117 | } | |
118 | } | |
119 | ||
120 | type PostAddressRequest struct { | |
121 | ProfileID string | |
122 | A service.Address | |
123 | } | |
124 | type PostAddressResponse struct { | |
125 | Err error | |
126 | } | |
127 | ||
128 | func MakePostAddressEndpoint(s service.Service) endpoint.Endpoint { | |
129 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
130 | req := request.(PostAddressRequest) | |
131 | err := s.PostAddress(ctx, req.ProfileID, req.A) | |
132 | return PostAddressResponse{Err: err}, nil | |
133 | } | |
134 | } | |
135 | ||
136 | type DeleteAddressRequest struct { | |
137 | ProfileID string | |
138 | AddressID string | |
139 | } | |
140 | type DeleteAddressResponse struct { | |
141 | Err error | |
142 | } | |
143 | ||
144 | func MakeDeleteAddressEndpoint(s service.Service) endpoint.Endpoint { | |
145 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
146 | req := request.(DeleteAddressRequest) | |
147 | err := s.DeleteAddress(ctx, req.ProfileID, req.AddressID) | |
148 | return DeleteAddressResponse{Err: err}, nil | |
149 | } | |
150 | } | |
151 | ||
152 | type Endpoints struct { | |
153 | PostProfile endpoint.Endpoint | |
154 | GetProfile endpoint.Endpoint | |
155 | PutProfile endpoint.Endpoint | |
156 | PatchProfile endpoint.Endpoint | |
157 | DeleteProfile endpoint.Endpoint | |
158 | GetAddresses endpoint.Endpoint | |
159 | GetAddress endpoint.Endpoint | |
160 | PostAddress endpoint.Endpoint | |
161 | DeleteAddress endpoint.Endpoint | |
162 | } |
0 | package http | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "encoding/json" | |
5 | "net/http" | |
6 | ||
7 | "github.com/go-kit/kit/cmd/kitgen/testdata/profilesvc/default/endpoints" | |
8 | httptransport "github.com/go-kit/kit/transport/http" | |
9 | ) | |
10 | ||
11 | func NewHTTPHandler(endpoints endpoints.Endpoints) http.Handler { | |
12 | m := http.NewServeMux() | |
13 | m.Handle("/postprofile", httptransport.NewServer(endpoints.PostProfile, DecodePostProfileRequest, EncodePostProfileResponse)) | |
14 | m.Handle("/getprofile", httptransport.NewServer(endpoints.GetProfile, DecodeGetProfileRequest, EncodeGetProfileResponse)) | |
15 | m.Handle("/putprofile", httptransport.NewServer(endpoints.PutProfile, DecodePutProfileRequest, EncodePutProfileResponse)) | |
16 | m.Handle("/patchprofile", httptransport.NewServer(endpoints.PatchProfile, DecodePatchProfileRequest, EncodePatchProfileResponse)) | |
17 | m.Handle("/deleteprofile", httptransport.NewServer(endpoints.DeleteProfile, DecodeDeleteProfileRequest, EncodeDeleteProfileResponse)) | |
18 | m.Handle("/getaddresses", httptransport.NewServer(endpoints.GetAddresses, DecodeGetAddressesRequest, EncodeGetAddressesResponse)) | |
19 | m.Handle("/getaddress", httptransport.NewServer(endpoints.GetAddress, DecodeGetAddressRequest, EncodeGetAddressResponse)) | |
20 | m.Handle("/postaddress", httptransport.NewServer(endpoints.PostAddress, DecodePostAddressRequest, EncodePostAddressResponse)) | |
21 | m.Handle("/deleteaddress", httptransport.NewServer(endpoints.DeleteAddress, DecodeDeleteAddressRequest, EncodeDeleteAddressResponse)) | |
22 | return m | |
23 | } | |
24 | func DecodePostProfileRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
25 | var req endpoints.PostProfileRequest | |
26 | err := json.NewDecoder(r.Body).Decode(&req) | |
27 | return req, err | |
28 | } | |
29 | func EncodePostProfileResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
30 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
31 | return json.NewEncoder(w).Encode(response) | |
32 | } | |
33 | func DecodeGetProfileRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
34 | var req endpoints.GetProfileRequest | |
35 | err := json.NewDecoder(r.Body).Decode(&req) | |
36 | return req, err | |
37 | } | |
38 | func EncodeGetProfileResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
39 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
40 | return json.NewEncoder(w).Encode(response) | |
41 | } | |
42 | func DecodePutProfileRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
43 | var req endpoints.PutProfileRequest | |
44 | err := json.NewDecoder(r.Body).Decode(&req) | |
45 | return req, err | |
46 | } | |
47 | func EncodePutProfileResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
48 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
49 | return json.NewEncoder(w).Encode(response) | |
50 | } | |
51 | func DecodePatchProfileRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
52 | var req endpoints.PatchProfileRequest | |
53 | err := json.NewDecoder(r.Body).Decode(&req) | |
54 | return req, err | |
55 | } | |
56 | func EncodePatchProfileResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
57 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
58 | return json.NewEncoder(w).Encode(response) | |
59 | } | |
60 | func DecodeDeleteProfileRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
61 | var req endpoints.DeleteProfileRequest | |
62 | err := json.NewDecoder(r.Body).Decode(&req) | |
63 | return req, err | |
64 | } | |
65 | func EncodeDeleteProfileResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
66 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
67 | return json.NewEncoder(w).Encode(response) | |
68 | } | |
69 | func DecodeGetAddressesRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
70 | var req endpoints.GetAddressesRequest | |
71 | err := json.NewDecoder(r.Body).Decode(&req) | |
72 | return req, err | |
73 | } | |
74 | func EncodeGetAddressesResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
75 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
76 | return json.NewEncoder(w).Encode(response) | |
77 | } | |
78 | func DecodeGetAddressRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
79 | var req endpoints.GetAddressRequest | |
80 | err := json.NewDecoder(r.Body).Decode(&req) | |
81 | return req, err | |
82 | } | |
83 | func EncodeGetAddressResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
84 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
85 | return json.NewEncoder(w).Encode(response) | |
86 | } | |
87 | func DecodePostAddressRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
88 | var req endpoints.PostAddressRequest | |
89 | err := json.NewDecoder(r.Body).Decode(&req) | |
90 | return req, err | |
91 | } | |
92 | func EncodePostAddressResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
93 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
94 | return json.NewEncoder(w).Encode(response) | |
95 | } | |
96 | func DecodeDeleteAddressRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
97 | var req endpoints.DeleteAddressRequest | |
98 | err := json.NewDecoder(r.Body).Decode(&req) | |
99 | return req, err | |
100 | } | |
101 | func EncodeDeleteAddressResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
102 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
103 | return json.NewEncoder(w).Encode(response) | |
104 | } |
0 | package service | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "errors" | |
5 | ) | |
6 | ||
7 | type Profile struct { | |
8 | ID string `json:"id"` | |
9 | Name string `json:"name,omitempty"` | |
10 | Addresses []Address `json:"addresses,omitempty"` | |
11 | } | |
12 | type Address struct { | |
13 | ID string `json:"id"` | |
14 | Location string `json:"location,omitempty"` | |
15 | } | |
16 | type Service struct { | |
17 | } | |
18 | ||
19 | func (s Service) PostProfile(ctx context.Context, p Profile) error { | |
20 | panic(errors.New("not implemented")) | |
21 | } | |
22 | func (s Service) GetProfile(ctx context.Context, id string) (Profile, error) { | |
23 | panic(errors.New("not implemented")) | |
24 | } | |
25 | func (s Service) PutProfile(ctx context.Context, id string, p Profile) error { | |
26 | panic(errors.New("not implemented")) | |
27 | } | |
28 | func (s Service) PatchProfile(ctx context.Context, id string, p Profile) error { | |
29 | panic(errors.New("not implemented")) | |
30 | } | |
31 | func (s Service) DeleteProfile(ctx context.Context, id string) error { | |
32 | panic(errors.New("not implemented")) | |
33 | } | |
34 | func (s Service) GetAddresses(ctx context.Context, profileID string) ([]Address, error) { | |
35 | panic(errors.New("not implemented")) | |
36 | } | |
37 | func (s Service) GetAddress(ctx context.Context, profileID string, addressID string) (Address, error) { | |
38 | panic(errors.New("not implemented")) | |
39 | } | |
40 | func (s Service) PostAddress(ctx context.Context, profileID string, a Address) error { | |
41 | panic(errors.New("not implemented")) | |
42 | } | |
43 | func (s Service) DeleteAddress(ctx context.Context, profileID string, addressID string) error { | |
44 | panic(errors.New("not implemented")) | |
45 | } |
0 | package profilesvc | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "encoding/json" | |
5 | "errors" | |
6 | "net/http" | |
7 | ||
8 | "github.com/go-kit/kit/endpoint" | |
9 | ||
10 | httptransport "github.com/go-kit/kit/transport/http" | |
11 | ) | |
12 | ||
13 | type Profile struct { | |
14 | ID string `json:"id"` | |
15 | Name string `json:"name,omitempty"` | |
16 | Addresses []Address `json:"addresses,omitempty"` | |
17 | } | |
18 | type Address struct { | |
19 | ID string `json:"id"` | |
20 | Location string `json:"location,omitempty"` | |
21 | } | |
22 | type Service struct { | |
23 | } | |
24 | ||
25 | func (s Service) PostProfile(ctx context.Context, p Profile) error { | |
26 | panic(errors.New("not implemented")) | |
27 | } | |
28 | ||
29 | type PostProfileRequest struct { | |
30 | P Profile | |
31 | } | |
32 | type PostProfileResponse struct { | |
33 | Err error | |
34 | } | |
35 | ||
36 | func MakePostProfileEndpoint(s Service) endpoint.Endpoint { | |
37 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
38 | req := request.(PostProfileRequest) | |
39 | err := s.PostProfile(ctx, req.P) | |
40 | return PostProfileResponse{Err: err}, nil | |
41 | } | |
42 | } | |
43 | func (s Service) GetProfile(ctx context.Context, id string) (Profile, error) { | |
44 | panic(errors.New("not implemented")) | |
45 | } | |
46 | ||
47 | type GetProfileRequest struct { | |
48 | Id string | |
49 | } | |
50 | type GetProfileResponse struct { | |
51 | P Profile | |
52 | Err error | |
53 | } | |
54 | ||
55 | func MakeGetProfileEndpoint(s Service) endpoint.Endpoint { | |
56 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
57 | req := request.(GetProfileRequest) | |
58 | P, err := s.GetProfile(ctx, req.Id) | |
59 | return GetProfileResponse{P: P, Err: err}, nil | |
60 | } | |
61 | } | |
62 | func (s Service) PutProfile(ctx context.Context, id string, p Profile) error { | |
63 | panic(errors.New("not implemented")) | |
64 | } | |
65 | ||
66 | type PutProfileRequest struct { | |
67 | Id string | |
68 | P Profile | |
69 | } | |
70 | type PutProfileResponse struct { | |
71 | Err error | |
72 | } | |
73 | ||
74 | func MakePutProfileEndpoint(s Service) endpoint.Endpoint { | |
75 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
76 | req := request.(PutProfileRequest) | |
77 | err := s.PutProfile(ctx, req.Id, req.P) | |
78 | return PutProfileResponse{Err: err}, nil | |
79 | } | |
80 | } | |
81 | func (s Service) PatchProfile(ctx context.Context, id string, p Profile) error { | |
82 | panic(errors.New("not implemented")) | |
83 | } | |
84 | ||
85 | type PatchProfileRequest struct { | |
86 | Id string | |
87 | P Profile | |
88 | } | |
89 | type PatchProfileResponse struct { | |
90 | Err error | |
91 | } | |
92 | ||
93 | func MakePatchProfileEndpoint(s Service) endpoint.Endpoint { | |
94 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
95 | req := request.(PatchProfileRequest) | |
96 | err := s.PatchProfile(ctx, req.Id, req.P) | |
97 | return PatchProfileResponse{Err: err}, nil | |
98 | } | |
99 | } | |
100 | func (s Service) DeleteProfile(ctx context.Context, id string) error { | |
101 | panic(errors.New("not implemented")) | |
102 | } | |
103 | ||
104 | type DeleteProfileRequest struct { | |
105 | Id string | |
106 | } | |
107 | type DeleteProfileResponse struct { | |
108 | Err error | |
109 | } | |
110 | ||
111 | func MakeDeleteProfileEndpoint(s Service) endpoint.Endpoint { | |
112 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
113 | req := request.(DeleteProfileRequest) | |
114 | err := s.DeleteProfile(ctx, req.Id) | |
115 | return DeleteProfileResponse{Err: err}, nil | |
116 | } | |
117 | } | |
118 | func (s Service) GetAddresses(ctx context.Context, profileID string) ([]Address, error) { | |
119 | panic(errors.New("not implemented")) | |
120 | } | |
121 | ||
122 | type GetAddressesRequest struct { | |
123 | ProfileID string | |
124 | } | |
125 | type GetAddressesResponse struct { | |
126 | S []Address | |
127 | Err error | |
128 | } | |
129 | ||
130 | func MakeGetAddressesEndpoint(s Service) endpoint.Endpoint { | |
131 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
132 | req := request.(GetAddressesRequest) | |
133 | slice1, err := s.GetAddresses(ctx, req.ProfileID) | |
134 | return GetAddressesResponse{S: slice1, Err: err}, nil | |
135 | } | |
136 | } | |
137 | func (s Service) GetAddress(ctx context.Context, profileID string, addressID string) (Address, error) { | |
138 | panic(errors.New("not implemented")) | |
139 | } | |
140 | ||
141 | type GetAddressRequest struct { | |
142 | ProfileID string | |
143 | AddressID string | |
144 | } | |
145 | type GetAddressResponse struct { | |
146 | A Address | |
147 | Err error | |
148 | } | |
149 | ||
150 | func MakeGetAddressEndpoint(s Service) endpoint.Endpoint { | |
151 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
152 | req := request.(GetAddressRequest) | |
153 | A, err := s.GetAddress(ctx, req.ProfileID, req.AddressID) | |
154 | return GetAddressResponse{A: A, Err: err}, nil | |
155 | } | |
156 | } | |
157 | func (s Service) PostAddress(ctx context.Context, profileID string, a Address) error { | |
158 | panic(errors.New("not implemented")) | |
159 | } | |
160 | ||
161 | type PostAddressRequest struct { | |
162 | ProfileID string | |
163 | A Address | |
164 | } | |
165 | type PostAddressResponse struct { | |
166 | Err error | |
167 | } | |
168 | ||
169 | func MakePostAddressEndpoint(s Service) endpoint.Endpoint { | |
170 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
171 | req := request.(PostAddressRequest) | |
172 | err := s.PostAddress(ctx, req.ProfileID, req.A) | |
173 | return PostAddressResponse{Err: err}, nil | |
174 | } | |
175 | } | |
176 | func (s Service) DeleteAddress(ctx context.Context, profileID string, addressID string) error { | |
177 | panic(errors.New("not implemented")) | |
178 | } | |
179 | ||
180 | type DeleteAddressRequest struct { | |
181 | ProfileID string | |
182 | AddressID string | |
183 | } | |
184 | type DeleteAddressResponse struct { | |
185 | Err error | |
186 | } | |
187 | ||
188 | func MakeDeleteAddressEndpoint(s Service) endpoint.Endpoint { | |
189 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
190 | req := request.(DeleteAddressRequest) | |
191 | err := s.DeleteAddress(ctx, req.ProfileID, req.AddressID) | |
192 | return DeleteAddressResponse{Err: err}, nil | |
193 | } | |
194 | } | |
195 | ||
196 | type Endpoints struct { | |
197 | PostProfile endpoint.Endpoint | |
198 | GetProfile endpoint.Endpoint | |
199 | PutProfile endpoint.Endpoint | |
200 | PatchProfile endpoint.Endpoint | |
201 | DeleteProfile endpoint.Endpoint | |
202 | GetAddresses endpoint.Endpoint | |
203 | GetAddress endpoint.Endpoint | |
204 | PostAddress endpoint.Endpoint | |
205 | DeleteAddress endpoint.Endpoint | |
206 | } | |
207 | ||
208 | func NewHTTPHandler(endpoints Endpoints) http.Handler { | |
209 | m := http.NewServeMux() | |
210 | m.Handle("/postprofile", httptransport.NewServer(endpoints.PostProfile, DecodePostProfileRequest, EncodePostProfileResponse)) | |
211 | m.Handle("/getprofile", httptransport.NewServer(endpoints.GetProfile, DecodeGetProfileRequest, EncodeGetProfileResponse)) | |
212 | m.Handle("/putprofile", httptransport.NewServer(endpoints.PutProfile, DecodePutProfileRequest, EncodePutProfileResponse)) | |
213 | m.Handle("/patchprofile", httptransport.NewServer(endpoints.PatchProfile, DecodePatchProfileRequest, EncodePatchProfileResponse)) | |
214 | m.Handle("/deleteprofile", httptransport.NewServer(endpoints.DeleteProfile, DecodeDeleteProfileRequest, EncodeDeleteProfileResponse)) | |
215 | m.Handle("/getaddresses", httptransport.NewServer(endpoints.GetAddresses, DecodeGetAddressesRequest, EncodeGetAddressesResponse)) | |
216 | m.Handle("/getaddress", httptransport.NewServer(endpoints.GetAddress, DecodeGetAddressRequest, EncodeGetAddressResponse)) | |
217 | m.Handle("/postaddress", httptransport.NewServer(endpoints.PostAddress, DecodePostAddressRequest, EncodePostAddressResponse)) | |
218 | m.Handle("/deleteaddress", httptransport.NewServer(endpoints.DeleteAddress, DecodeDeleteAddressRequest, EncodeDeleteAddressResponse)) | |
219 | return m | |
220 | } | |
221 | func DecodePostProfileRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
222 | var req PostProfileRequest | |
223 | err := json.NewDecoder(r.Body).Decode(&req) | |
224 | return req, err | |
225 | } | |
226 | func EncodePostProfileResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
227 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
228 | return json.NewEncoder(w).Encode(response) | |
229 | } | |
230 | func DecodeGetProfileRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
231 | var req GetProfileRequest | |
232 | err := json.NewDecoder(r.Body).Decode(&req) | |
233 | return req, err | |
234 | } | |
235 | func EncodeGetProfileResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
236 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
237 | return json.NewEncoder(w).Encode(response) | |
238 | } | |
239 | func DecodePutProfileRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
240 | var req PutProfileRequest | |
241 | err := json.NewDecoder(r.Body).Decode(&req) | |
242 | return req, err | |
243 | } | |
244 | func EncodePutProfileResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
245 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
246 | return json.NewEncoder(w).Encode(response) | |
247 | } | |
248 | func DecodePatchProfileRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
249 | var req PatchProfileRequest | |
250 | err := json.NewDecoder(r.Body).Decode(&req) | |
251 | return req, err | |
252 | } | |
253 | func EncodePatchProfileResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
254 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
255 | return json.NewEncoder(w).Encode(response) | |
256 | } | |
257 | func DecodeDeleteProfileRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
258 | var req DeleteProfileRequest | |
259 | err := json.NewDecoder(r.Body).Decode(&req) | |
260 | return req, err | |
261 | } | |
262 | func EncodeDeleteProfileResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
263 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
264 | return json.NewEncoder(w).Encode(response) | |
265 | } | |
266 | func DecodeGetAddressesRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
267 | var req GetAddressesRequest | |
268 | err := json.NewDecoder(r.Body).Decode(&req) | |
269 | return req, err | |
270 | } | |
271 | func EncodeGetAddressesResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
272 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
273 | return json.NewEncoder(w).Encode(response) | |
274 | } | |
275 | func DecodeGetAddressRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
276 | var req GetAddressRequest | |
277 | err := json.NewDecoder(r.Body).Decode(&req) | |
278 | return req, err | |
279 | } | |
280 | func EncodeGetAddressResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
281 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
282 | return json.NewEncoder(w).Encode(response) | |
283 | } | |
284 | func DecodePostAddressRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
285 | var req PostAddressRequest | |
286 | err := json.NewDecoder(r.Body).Decode(&req) | |
287 | return req, err | |
288 | } | |
289 | func EncodePostAddressResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
290 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
291 | return json.NewEncoder(w).Encode(response) | |
292 | } | |
293 | func DecodeDeleteAddressRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
294 | var req DeleteAddressRequest | |
295 | err := json.NewDecoder(r.Body).Decode(&req) | |
296 | return req, err | |
297 | } | |
298 | func EncodeDeleteAddressResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
299 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
300 | return json.NewEncoder(w).Encode(response) | |
301 | } |
0 | package profilesvc | |
1 | ||
2 | type Service interface { | |
3 | PostProfile(ctx context.Context, p Profile) error | |
4 | GetProfile(ctx context.Context, id string) (Profile, error) | |
5 | PutProfile(ctx context.Context, id string, p Profile) error | |
6 | PatchProfile(ctx context.Context, id string, p Profile) error | |
7 | DeleteProfile(ctx context.Context, id string) error | |
8 | GetAddresses(ctx context.Context, profileID string) ([]Address, error) | |
9 | GetAddress(ctx context.Context, profileID string, addressID string) (Address, error) | |
10 | PostAddress(ctx context.Context, profileID string, a Address) error | |
11 | DeleteAddress(ctx context.Context, profileID string, addressID string) error | |
12 | } | |
13 | ||
14 | type Profile struct { | |
15 | ID string `json:"id"` | |
16 | Name string `json:"name,omitempty"` | |
17 | Addresses []Address `json:"addresses,omitempty"` | |
18 | } | |
19 | ||
20 | type Address struct { | |
21 | ID string `json:"id"` | |
22 | Location string `json:"location,omitempty"` | |
23 | } |
0 | package endpoints | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | ||
5 | "github.com/go-kit/kit/cmd/kitgen/testdata/stringservice/default/service" | |
6 | "github.com/go-kit/kit/endpoint" | |
7 | ) | |
8 | ||
9 | type ConcatRequest struct { | |
10 | A string | |
11 | B string | |
12 | } | |
13 | type ConcatResponse struct { | |
14 | S string | |
15 | Err error | |
16 | } | |
17 | ||
18 | func MakeConcatEndpoint(s service.Service) endpoint.Endpoint { | |
19 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
20 | req := request.(ConcatRequest) | |
21 | string1, err := s.Concat(ctx, req.A, req.B) | |
22 | return ConcatResponse{S: string1, Err: err}, nil | |
23 | } | |
24 | } | |
25 | ||
26 | type CountRequest struct { | |
27 | S string | |
28 | } | |
29 | type CountResponse struct { | |
30 | Count int | |
31 | } | |
32 | ||
33 | func MakeCountEndpoint(s service.Service) endpoint.Endpoint { | |
34 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
35 | req := request.(CountRequest) | |
36 | count := s.Count(ctx, req.S) | |
37 | return CountResponse{Count: count}, nil | |
38 | } | |
39 | } | |
40 | ||
41 | type Endpoints struct { | |
42 | Concat endpoint.Endpoint | |
43 | Count endpoint.Endpoint | |
44 | } |
0 | package http | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "encoding/json" | |
5 | "net/http" | |
6 | ||
7 | "github.com/go-kit/kit/cmd/kitgen/testdata/stringservice/default/endpoints" | |
8 | httptransport "github.com/go-kit/kit/transport/http" | |
9 | ) | |
10 | ||
11 | func NewHTTPHandler(endpoints endpoints.Endpoints) http.Handler { | |
12 | m := http.NewServeMux() | |
13 | m.Handle("/concat", httptransport.NewServer(endpoints.Concat, DecodeConcatRequest, EncodeConcatResponse)) | |
14 | m.Handle("/count", httptransport.NewServer(endpoints.Count, DecodeCountRequest, EncodeCountResponse)) | |
15 | return m | |
16 | } | |
17 | func DecodeConcatRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
18 | var req endpoints.ConcatRequest | |
19 | err := json.NewDecoder(r.Body).Decode(&req) | |
20 | return req, err | |
21 | } | |
22 | func EncodeConcatResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
23 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
24 | return json.NewEncoder(w).Encode(response) | |
25 | } | |
26 | func DecodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
27 | var req endpoints.CountRequest | |
28 | err := json.NewDecoder(r.Body).Decode(&req) | |
29 | return req, err | |
30 | } | |
31 | func EncodeCountResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
32 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
33 | return json.NewEncoder(w).Encode(response) | |
34 | } |
0 | package service | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "errors" | |
5 | ) | |
6 | ||
7 | type Service struct { | |
8 | } | |
9 | ||
10 | func (s Service) Concat(ctx context.Context, a string, b string) (string, error) { | |
11 | panic(errors.New("not implemented")) | |
12 | } | |
13 | func (s Service) Count(ctx context.Context, string1 string) int { | |
14 | panic(errors.New("not implemented")) | |
15 | } |
0 | package foo | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "encoding/json" | |
5 | "errors" | |
6 | "net/http" | |
7 | ||
8 | "github.com/go-kit/kit/endpoint" | |
9 | ||
10 | httptransport "github.com/go-kit/kit/transport/http" | |
11 | ) | |
12 | ||
13 | type Service struct { | |
14 | } | |
15 | ||
16 | func (s Service) Concat(ctx context.Context, a string, b string) (string, error) { | |
17 | panic(errors.New("not implemented")) | |
18 | } | |
19 | ||
20 | type ConcatRequest struct { | |
21 | A string | |
22 | B string | |
23 | } | |
24 | type ConcatResponse struct { | |
25 | S string | |
26 | Err error | |
27 | } | |
28 | ||
29 | func MakeConcatEndpoint(s Service) endpoint.Endpoint { | |
30 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
31 | req := request.(ConcatRequest) | |
32 | string1, err := s.Concat(ctx, req.A, req.B) | |
33 | return ConcatResponse{S: string1, Err: err}, nil | |
34 | } | |
35 | } | |
36 | func (s Service) Count(ctx context.Context, string1 string) int { | |
37 | panic(errors.New("not implemented")) | |
38 | } | |
39 | ||
40 | type CountRequest struct { | |
41 | S string | |
42 | } | |
43 | type CountResponse struct { | |
44 | Count int | |
45 | } | |
46 | ||
47 | func MakeCountEndpoint(s Service) endpoint.Endpoint { | |
48 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
49 | req := request.(CountRequest) | |
50 | count := s.Count(ctx, req.S) | |
51 | return CountResponse{Count: count}, nil | |
52 | } | |
53 | } | |
54 | ||
55 | type Endpoints struct { | |
56 | Concat endpoint.Endpoint | |
57 | Count endpoint.Endpoint | |
58 | } | |
59 | ||
60 | func NewHTTPHandler(endpoints Endpoints) http.Handler { | |
61 | m := http.NewServeMux() | |
62 | m.Handle("/concat", httptransport.NewServer(endpoints.Concat, DecodeConcatRequest, EncodeConcatResponse)) | |
63 | m.Handle("/count", httptransport.NewServer(endpoints.Count, DecodeCountRequest, EncodeCountResponse)) | |
64 | return m | |
65 | } | |
66 | func DecodeConcatRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
67 | var req ConcatRequest | |
68 | err := json.NewDecoder(r.Body).Decode(&req) | |
69 | return req, err | |
70 | } | |
71 | func EncodeConcatResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
72 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
73 | return json.NewEncoder(w).Encode(response) | |
74 | } | |
75 | func DecodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
76 | var req CountRequest | |
77 | err := json.NewDecoder(r.Body).Decode(&req) | |
78 | return req, err | |
79 | } | |
80 | func EncodeCountResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
81 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
82 | return json.NewEncoder(w).Encode(response) | |
83 | } |
0 | package foo | |
1 | ||
2 | type Service interface { | |
3 | Concat(ctx context.Context, a, b string) (string, error) | |
4 | Count(ctx context.Context, s string) (count int) | |
5 | } |
0 | package endpoints | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | ||
5 | "github.com/go-kit/kit/cmd/kitgen/testdata/underscores/default/service" | |
6 | "github.com/go-kit/kit/endpoint" | |
7 | ) | |
8 | ||
9 | type FooRequest struct { | |
10 | I int | |
11 | } | |
12 | type FooResponse struct { | |
13 | I int | |
14 | Err error | |
15 | } | |
16 | ||
17 | func MakeFooEndpoint(s service.Service) endpoint.Endpoint { | |
18 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
19 | req := request.(FooRequest) | |
20 | i, err := s.Foo(ctx, req.I) | |
21 | return FooResponse{I: i, Err: err}, nil | |
22 | } | |
23 | } | |
24 | ||
25 | type Endpoints struct { | |
26 | Foo endpoint.Endpoint | |
27 | } |
0 | package http | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "encoding/json" | |
5 | "net/http" | |
6 | ||
7 | "github.com/go-kit/kit/cmd/kitgen/testdata/underscores/default/endpoints" | |
8 | httptransport "github.com/go-kit/kit/transport/http" | |
9 | ) | |
10 | ||
11 | func NewHTTPHandler(endpoints endpoints.Endpoints) http.Handler { | |
12 | m := http.NewServeMux() | |
13 | m.Handle("/foo", httptransport.NewServer(endpoints.Foo, DecodeFooRequest, EncodeFooResponse)) | |
14 | return m | |
15 | } | |
16 | func DecodeFooRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
17 | var req endpoints.FooRequest | |
18 | err := json.NewDecoder(r.Body).Decode(&req) | |
19 | return req, err | |
20 | } | |
21 | func EncodeFooResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
22 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
23 | return json.NewEncoder(w).Encode(response) | |
24 | } |
0 | package service | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "errors" | |
5 | ) | |
6 | ||
7 | type Service struct { | |
8 | } | |
9 | ||
10 | func (s Service) Foo(ctx context.Context, i int) (int, error) { | |
11 | panic(errors.New("not implemented")) | |
12 | } |
0 | package underscores | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "encoding/json" | |
5 | "errors" | |
6 | "net/http" | |
7 | ||
8 | "github.com/go-kit/kit/endpoint" | |
9 | ||
10 | httptransport "github.com/go-kit/kit/transport/http" | |
11 | ) | |
12 | ||
13 | type Service struct { | |
14 | } | |
15 | ||
16 | func (s Service) Foo(ctx context.Context, i int) (int, error) { | |
17 | panic(errors.New("not implemented")) | |
18 | } | |
19 | ||
20 | type FooRequest struct { | |
21 | I int | |
22 | } | |
23 | type FooResponse struct { | |
24 | I int | |
25 | Err error | |
26 | } | |
27 | ||
28 | func MakeFooEndpoint(s Service) endpoint.Endpoint { | |
29 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
30 | req := request.(FooRequest) | |
31 | i, err := s.Foo(ctx, req.I) | |
32 | return FooResponse{I: i, Err: err}, nil | |
33 | } | |
34 | } | |
35 | ||
36 | type Endpoints struct { | |
37 | Foo endpoint.Endpoint | |
38 | } | |
39 | ||
40 | func NewHTTPHandler(endpoints Endpoints) http.Handler { | |
41 | m := http.NewServeMux() | |
42 | m.Handle("/foo", httptransport.NewServer(endpoints.Foo, DecodeFooRequest, EncodeFooResponse)) | |
43 | return m | |
44 | } | |
45 | func DecodeFooRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
46 | var req FooRequest | |
47 | err := json.NewDecoder(r.Body).Decode(&req) | |
48 | return req, err | |
49 | } | |
50 | func EncodeFooResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
51 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
52 | return json.NewEncoder(w).Encode(response) | |
53 | } |
0 | package underscores | |
1 | ||
2 | import "context" | |
3 | ||
4 | type Service interface { | |
5 | Foo(_ context.Context, _ int) (int, error) | |
6 | } |
0 | package main | |
1 | ||
2 | import ( | |
3 | "bytes" | |
4 | "fmt" | |
5 | "go/ast" | |
6 | "go/format" | |
7 | "go/token" | |
8 | "io" | |
9 | "os" | |
10 | "path/filepath" | |
11 | "sort" | |
12 | "strings" | |
13 | ||
14 | "github.com/davecgh/go-spew/spew" | |
15 | "github.com/pkg/errors" | |
16 | ||
17 | "golang.org/x/tools/imports" | |
18 | ) | |
19 | ||
20 | type ( | |
21 | files map[string]io.Reader | |
22 | layout interface { | |
23 | transformAST(ctx *sourceContext) (files, error) | |
24 | } | |
25 | outputTree map[string]*ast.File | |
26 | ) | |
27 | ||
28 | func (ot outputTree) addFile(path, pkgname string) *ast.File { | |
29 | file := &ast.File{ | |
30 | Name: id(pkgname), | |
31 | Decls: []ast.Decl{}, | |
32 | } | |
33 | ot[path] = file | |
34 | return file | |
35 | } | |
36 | ||
37 | func getGopath() string { | |
38 | gopath, set := os.LookupEnv("GOPATH") | |
39 | if !set { | |
40 | return filepath.Join(os.Getenv("HOME"), "go") | |
41 | } | |
42 | return gopath | |
43 | } | |
44 | ||
45 | func importPath(targetDir, gopath string) (string, error) { | |
46 | if !filepath.IsAbs(targetDir) { | |
47 | return "", fmt.Errorf("%q is not an absolute path", targetDir) | |
48 | } | |
49 | ||
50 | for _, dir := range filepath.SplitList(gopath) { | |
51 | abspath, err := filepath.Abs(dir) | |
52 | if err != nil { | |
53 | continue | |
54 | } | |
55 | srcPath := filepath.Join(abspath, "src") | |
56 | ||
57 | res, err := filepath.Rel(srcPath, targetDir) | |
58 | if err != nil { | |
59 | continue | |
60 | } | |
61 | if strings.Index(res, "..") == -1 { | |
62 | return res, nil | |
63 | } | |
64 | } | |
65 | return "", fmt.Errorf("%q is not in GOPATH (%s)", targetDir, gopath) | |
66 | ||
67 | } | |
68 | ||
69 | func selectify(file *ast.File, pkgName, identName, importPath string) *ast.File { | |
70 | if file.Name.Name == pkgName { | |
71 | return file | |
72 | } | |
73 | ||
74 | selector := sel(id(pkgName), id(identName)) | |
75 | var did bool | |
76 | if file, did = selectifyIdent(identName, file, selector); did { | |
77 | addImport(file, importPath) | |
78 | } | |
79 | return file | |
80 | } | |
81 | ||
82 | type selIdentFn func(ast.Node, func(ast.Node)) Visitor | |
83 | ||
84 | func (f selIdentFn) Visit(node ast.Node, r func(ast.Node)) Visitor { | |
85 | return f(node, r) | |
86 | } | |
87 | ||
88 | func selectifyIdent(identName string, file *ast.File, selector ast.Expr) (*ast.File, bool) { | |
89 | var replaced bool | |
90 | var r selIdentFn | |
91 | r = selIdentFn(func(node ast.Node, replaceWith func(ast.Node)) Visitor { | |
92 | switch id := node.(type) { | |
93 | case *ast.SelectorExpr: | |
94 | return nil | |
95 | case *ast.Ident: | |
96 | if id.Name == identName { | |
97 | replaced = true | |
98 | replaceWith(selector) | |
99 | } | |
100 | } | |
101 | return r | |
102 | }) | |
103 | return WalkReplace(r, file).(*ast.File), replaced | |
104 | } | |
105 | ||
106 | func formatNode(fname string, node ast.Node) (*bytes.Buffer, error) { | |
107 | if file, is := node.(*ast.File); is { | |
108 | sort.Stable(sortableDecls(file.Decls)) | |
109 | } | |
110 | outfset := token.NewFileSet() | |
111 | buf := &bytes.Buffer{} | |
112 | err := format.Node(buf, outfset, node) | |
113 | if err != nil { | |
114 | return nil, err | |
115 | } | |
116 | imps, err := imports.Process(fname, buf.Bytes(), nil) | |
117 | if err != nil { | |
118 | return nil, err | |
119 | } | |
120 | return bytes.NewBuffer(imps), nil | |
121 | } | |
122 | ||
123 | type sortableDecls []ast.Decl | |
124 | ||
125 | func (sd sortableDecls) Len() int { | |
126 | return len(sd) | |
127 | } | |
128 | ||
129 | func (sd sortableDecls) Less(i int, j int) bool { | |
130 | switch left := sd[i].(type) { | |
131 | case *ast.GenDecl: | |
132 | switch right := sd[j].(type) { | |
133 | default: | |
134 | return left.Tok == token.IMPORT | |
135 | case *ast.GenDecl: | |
136 | return left.Tok == token.IMPORT && right.Tok != token.IMPORT | |
137 | } | |
138 | } | |
139 | return false | |
140 | } | |
141 | ||
142 | func (sd sortableDecls) Swap(i int, j int) { | |
143 | sd[i], sd[j] = sd[j], sd[i] | |
144 | } | |
145 | ||
146 | func formatNodes(nodes outputTree) (files, error) { | |
147 | res := files{} | |
148 | var err error | |
149 | for fn, node := range nodes { | |
150 | res[fn], err = formatNode(fn, node) | |
151 | if err != nil { | |
152 | return nil, errors.Wrapf(err, "formatNodes") | |
153 | } | |
154 | } | |
155 | return res, nil | |
156 | } | |
157 | ||
158 | // XXX debug | |
159 | func spewDecls(f *ast.File) { | |
160 | for _, d := range f.Decls { | |
161 | switch dcl := d.(type) { | |
162 | default: | |
163 | spew.Dump(dcl) | |
164 | case *ast.GenDecl: | |
165 | spew.Dump(dcl.Tok) | |
166 | case *ast.FuncDecl: | |
167 | spew.Dump(dcl.Name.Name) | |
168 | } | |
169 | } | |
170 | } | |
171 | ||
172 | func addImports(root *ast.File, ctx *sourceContext) { | |
173 | root.Decls = append(root.Decls, ctx.importDecls()...) | |
174 | } | |
175 | ||
176 | func addImport(root *ast.File, path string) { | |
177 | for _, d := range root.Decls { | |
178 | if imp, is := d.(*ast.GenDecl); is && imp.Tok == token.IMPORT { | |
179 | for _, s := range imp.Specs { | |
180 | if s.(*ast.ImportSpec).Path.Value == `"`+filepath.ToSlash(path)+`"` { | |
181 | return // already have one | |
182 | // xxx aliased imports? | |
183 | } | |
184 | } | |
185 | } | |
186 | } | |
187 | root.Decls = append(root.Decls, importFor(importSpec(path))) | |
188 | } | |
189 | ||
190 | func addStubStruct(root *ast.File, iface iface) { | |
191 | root.Decls = append(root.Decls, iface.stubStructDecl()) | |
192 | } | |
193 | ||
194 | func addType(root *ast.File, typ *ast.TypeSpec) { | |
195 | root.Decls = append(root.Decls, typeDecl(typ)) | |
196 | } | |
197 | ||
198 | func addMethod(root *ast.File, iface iface, meth method) { | |
199 | def := meth.definition(iface) | |
200 | root.Decls = append(root.Decls, def) | |
201 | } | |
202 | ||
203 | func addRequestStruct(root *ast.File, meth method) { | |
204 | root.Decls = append(root.Decls, meth.requestStruct()) | |
205 | } | |
206 | ||
207 | func addResponseStruct(root *ast.File, meth method) { | |
208 | root.Decls = append(root.Decls, meth.responseStruct()) | |
209 | } | |
210 | ||
211 | func addEndpointMaker(root *ast.File, ifc iface, meth method) { | |
212 | root.Decls = append(root.Decls, meth.endpointMaker(ifc)) | |
213 | } | |
214 | ||
215 | func addEndpointsStruct(root *ast.File, ifc iface) { | |
216 | root.Decls = append(root.Decls, ifc.endpointsStruct()) | |
217 | } | |
218 | ||
219 | func addHTTPHandler(root *ast.File, ifc iface) { | |
220 | root.Decls = append(root.Decls, ifc.httpHandler()) | |
221 | } | |
222 | ||
223 | func addDecoder(root *ast.File, meth method) { | |
224 | root.Decls = append(root.Decls, meth.decoderFunc()) | |
225 | } | |
226 | ||
227 | func addEncoder(root *ast.File, meth method) { | |
228 | root.Decls = append(root.Decls, meth.encoderFunc()) | |
229 | } |
4 | 4 | require ( |
5 | 5 | github.com/VividCortex/gohistogram v1.0.0 |
6 | 6 | github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 |
7 | github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a | |
8 | 7 | github.com/aws/aws-sdk-go v1.38.68 |
9 | 8 | github.com/aws/aws-sdk-go-v2 v1.7.0 |
10 | 9 | github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.5.0 |
12 | 11 | github.com/cenkalti/backoff v2.2.1+incompatible // indirect |
13 | 12 | github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec // indirect |
14 | 13 | github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect |
15 | github.com/davecgh/go-spew v1.1.1 | |
16 | 14 | github.com/dgrijalva/jwt-go v3.2.0+incompatible |
17 | 15 | github.com/edsrzf/mmap-go v1.0.0 // indirect |
18 | 16 | github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db // indirect |
29 | 27 | github.com/opentracing/opentracing-go v1.2.0 |
30 | 28 | github.com/openzipkin/zipkin-go v0.2.5 |
31 | 29 | github.com/performancecopilot/speed v3.0.0+incompatible |
32 | github.com/pkg/errors v0.9.1 | |
33 | 30 | github.com/prometheus/client_golang v1.11.0 |
34 | 31 | github.com/sirupsen/logrus v1.8.1 |
35 | 32 | github.com/smartystreets/goconvey v1.6.4 // indirect |
43 | 40 | go.uber.org/zap v1.17.0 |
44 | 41 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c |
45 | 42 | golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 |
46 | golang.org/x/tools v0.1.4 | |
47 | 43 | google.golang.org/grpc v1.38.0 |
48 | 44 | google.golang.org/protobuf v1.27.1 |
49 | 45 | gopkg.in/gcfg.v1 v1.2.3 // indirect |
19 | 19 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= |
20 | 20 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= |
21 | 21 | github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= |
22 | github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a h1:pv34s756C4pEXnjgPfGYgdhg/ZdajGhyOvzx8k+23nw= | |
23 | github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= | |
24 | 22 | github.com/aws/aws-sdk-go v1.38.68 h1:aOG8geU4SohNp659eKBHRBgbqSrZ6jNZlfimIuJAwL8= |
25 | 23 | github.com/aws/aws-sdk-go v1.38.68/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= |
26 | 24 | github.com/aws/aws-sdk-go-v2 v1.7.0 h1:UYGnoIPIzed+ycmgw8Snb/0HK+KlMD+SndLTneG8ncE= |
357 | 355 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= |
358 | 356 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= |
359 | 357 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= |
360 | golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= | |
361 | 358 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= |
362 | 359 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
363 | 360 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
442 | 439 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= |
443 | 440 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= |
444 | 441 | golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= |
445 | golang.org/x/tools v0.1.4 h1:cVngSRcfgyZCzys3KYOpCFa+4dqX/Oub9tAq00ttGVs= | |
446 | golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= | |
447 | 442 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
448 | 443 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
449 | 444 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |