Skip to content

Commit 2de9c7e

Browse files
authored
Merge pull request #18 from devimteam/feature/generator-refactor
Feature/generator refactor
2 parents 9db0ac4 + 36ddfe2 commit 2de9c7e

41 files changed

Lines changed: 1753 additions & 1119 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ _testmain.go
2424
*.exe
2525
*.test
2626
*.prof
27+
28+
vendor/

Gopkg.lock

Lines changed: 14 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Gopkg.toml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,10 @@
2020
# name = "github.com/x/y"
2121
# version = "2.4.0"
2222

23-
2423
[[constraint]]
25-
name = "github.com/davecgh/go-spew"
26-
version = "1.1.0"
24+
name = "github.com/devimteam/jennifer"
25+
version = "0.15.0"
2726

2827
[[constraint]]
29-
name = "github.com/dave/jennifer"
30-
version = "^0.14.0"
28+
name = "github.com/vetcher/godecl"
29+
version = "^0.2.0"

README.md

Lines changed: 91 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Microgen
22

3-
Tool to generate microservices, based on [go-kit](https://gokit.io/), by specified service interface.
3+
Tool to generate microservices, based on [go-kit](https://gokit.io/), by specified service interface.
4+
The goal is to generate code for service which not fun to write but it should be written.
45

56
## Install
67
```
@@ -11,42 +12,108 @@ go get -u github.com/devimteam/microgen/cmd/microgen
1112
``` sh
1213
microgen [OPTIONS]
1314
```
14-
microgen is stable, so you can generate without flag `-init` any time your interface changed (e.g. added new method)
15+
microgen tool search in file first `type * interface` with docs, that contains `// @microgen`.
16+
17+
generation parameters provides through ["tags"](#tags) in interface docs after general `// @microgen` tag (space before @ __required__).
18+
1519
### Options
1620

17-
| Name | Default | Description |
18-
|:------------|:-----------------|:------------------------------------------------------------------------------|
19-
| -file | service.go | Relative path to source file with service interface |
20-
| -interface* | | Service interface name in source file |
21-
| -out | writes to stdout | Relative or absolute path to directory, where you want to see generated files |
22-
| -package* | | Package path of your service interface source file |
23-
| -debug | false | Display some debug information |
24-
| -grpc | false | Render client, server and converters for gRPC protocol |
25-
| -init | false | With flag `-grpc` generate stub methods for converters |
21+
| Name | Default | Description |
22+
|:------ |:-----------|:------------------------------------------------------------------------------|
23+
| -file | service.go | Relative path to source file with service interface |
24+
| -out | . | Relative or absolute path to directory, where you want to see generated files |
25+
| -force | false | With flag generate stub methods. |
26+
| -help | false | Print usage information |
2627

2728
\* __Required option__
2829

30+
### Markers
31+
Markers is a general tags, that participate in generation process.
32+
Typical syntax is: `// @<tag-name>:`
33+
34+
#### @microgen
35+
Main tag for microgen tool. Microgen scan file for the first interface which docs contains this tag.
36+
To add templates for generation, add their tags, separated by comma after `@microgen:`
37+
Example:
38+
```go
39+
// @microgen middleware, logging
40+
type StringService interface {
41+
ServiceMethod()
42+
}
43+
```
44+
#### @protobuf
45+
Protobuf tag is used for package declaration of compiled with `protoc` grpc package.
46+
Example:
47+
```go
48+
// @microgen grpc-server
49+
// @protobuf github.com/user/repo/path/to/protobuf
50+
type StringService interface {
51+
ServiceMethod()
52+
}
53+
```
54+
`@protobuf` tag is optional, but required for `grpc`, `grpc-server`, `grpc-client` generation.
55+
#### @grpc-addr
56+
gRPC address tag is used for gRPC go-kit-based client generation.
57+
Example:
58+
```go
59+
// @microgen grpc
60+
// @protobuf github.com/user/repo/path/to/protobuf
61+
// @grpc-addr some.service.address
62+
type StringService interface {
63+
ServiceMethod()
64+
}
65+
```
66+
`@grpc-addr` tag is optional, but required for `grpc-client` generation.
67+
68+
### Method's tags
69+
#### @logs-ignore
70+
This tag is used for logging middleware, when some arguments or results should not be logged, e.g. passwords or files.
71+
If `context.Context` is first, it ignored by default.
72+
Provide parameters names, separated by comma, to exclude them from logs.
73+
Example:
74+
```go
75+
// @microgen logging
76+
type FileService interface {
77+
// @logs-ignore data
78+
UploadFile(ctx context.Context, name string, data []byte) (link string, err error)
79+
}
80+
```
81+
82+
### Tags
83+
All allowed tags for customize generation provided here.
84+
85+
| Tag | Description |
86+
|:------------|:---------------------------------------------------------------------------------------|
87+
| middleware | General application middleware interface |
88+
| logging | Middleware that writes to logger all request/response information with handled time |
89+
| grpc-client | Generates client for grpc transport with request/response encoders/decoders |
90+
| grpc-server | Generates server for grpc transport with request/response encoders/decoders |
91+
| grpc | Generates client and server for grpc transport with request/response encoders/decoders |
92+
2993
## Example
3094
Follow this short guide to try microgen tool.
3195

3296
1. Create file `service.go` inside GOPATH and add code below.
33-
``` golang
97+
```go
3498
package stringsvc
3599

36100
import (
37-
"context"
101+
"context"
38102

39-
drive "google.golang.org/api/drive/v3"
103+
drive "google.golang.org/api/drive/v3"
40104
)
41105

106+
// @microgen grpc, middleware, logging
107+
// @protobuf github.com/devimteam/proto-utils
108+
// @grpc-addr test.address
42109
type StringService interface {
43-
Uppercase(ctx context.Context, str string) (ans string, err error)
44-
Count(ctx context.Context, text string, symbol string) (count int, positions []int)
45-
TestCase(ctx context.Context, comments []*drive.Comment) (err error)
110+
Uppercase(ctx context.Context, str string) (ans string, err error)
111+
Count(ctx context.Context, text string, symbol string) (count int, positions []int)
112+
TestCase(ctx context.Context, comments []*drive.Comment) (err error)
46113
}
47114
```
48115
2. Open command line next to your `service.go`.
49-
3. Enter `microgen -file ./service.go -interface StringService -out . -grpc -init`. __*__
116+
3. Enter `microgen`. __*__
50117
4. You should see something like that:
51118
```
52119
exchanges.go
@@ -65,23 +132,14 @@ All files successfully generated
65132

66133
__*__ `GOPATH/bin` should be in your PATH.
67134

68-
### Interface declaration rules
135+
## Interface declaration rules
69136
For correct generation, please, follow rules below.
70137

71138
* All interface method's arguments and results should be named and should be different (name duplicating unacceptable).
72139
* First argument of each method should be of type `context.Context` (from [standard library](https://golang.org/pkg/context/)).
73-
* Method results should contain at least one variable of `error` type.
74-
* [Some names](#not-allowed-names) are not allowed to be an argument or result.
140+
* Last result should should be `error` type.
141+
---
142+
* Name of _protobuf_ service should be the same, as interface name.
143+
* Function names in _protobuf_ should be the same, as in interface.
144+
* Message names in _protobuf_ should be named `<FunctionName>Request` or `<FunctionName>Response` for request/response message respectively.
75145
* Field names in _protobuf_ messages should be the same, as in interface methods (_protobuf_ - snake_case, interface - camelCase).
76-
77-
#### Not allowed names:
78-
```
79-
req
80-
request
81-
resp
82-
response
83-
```
84-
85-
### Misc
86-
87-
Microgen uses __0.9.*__ version of [devimteam/go-kit](https://github.com/devimteam/go-kit)

cmd/microgen/main.go

Lines changed: 36 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -3,116 +3,77 @@ package main
33
import (
44
"flag"
55
"fmt"
6-
"go/ast"
7-
astparser "go/parser"
8-
"go/token"
96
"os"
10-
"path/filepath"
117
"strings"
128

13-
"github.com/davecgh/go-spew/spew"
149
"github.com/devimteam/microgen/generator"
15-
"github.com/devimteam/microgen/generator/template"
16-
"github.com/devimteam/microgen/parser"
10+
"github.com/devimteam/microgen/util"
11+
"github.com/vetcher/godecl/types"
1712
)
1813

1914
var (
2015
flagFileName = flag.String("file", "service.go", "Name of file where described interface definition")
21-
flagIfaceName = flag.String("interface", "", "Interface name")
22-
flagOutputDir = flag.String("out", "", "Output directory")
23-
flagGRPC = flag.Bool("grpc", false, "Render gRPC transport")
24-
flagDebug = flag.Bool("debug", false, "Debug mode")
16+
flagOutputDir = flag.String("out", ".", "Output directory")
2517
flagHelp = flag.Bool("help", false, "Show help")
26-
flagInit = flag.Bool("init", false, "With flag `-grpc` generate stub methods for converters")
18+
flagForce = flag.Bool("force", false, "Overwrite all files, as it generates for the first time")
2719
)
2820

2921
func init() {
3022
flag.Parse()
3123
}
3224

3325
func main() {
34-
if *flagHelp || *flagFileName == "" || *flagIfaceName == "" {
26+
if *flagHelp || *flagFileName == "" {
3527
flag.Usage()
3628
os.Exit(0)
3729
}
3830

39-
currentDir, err := os.Getwd()
31+
info, err := util.ParseFile(*flagFileName)
4032
if err != nil {
41-
fmt.Println(err)
33+
fmt.Println("fatal:", err)
4234
os.Exit(1)
4335
}
4436

45-
path := filepath.Join(currentDir, *flagFileName)
46-
fset := token.NewFileSet()
47-
f, err := astparser.ParseFile(fset, path, nil, astparser.ParseComments)
48-
if err != nil {
49-
fmt.Printf("error when parse file: %v\n", err)
50-
os.Exit(1)
51-
}
52-
i, err := parser.ParseInterface(f, *flagIfaceName)
53-
if err != nil {
54-
fmt.Printf("error when parse interface from file : %v\n", err)
37+
i := findInterface(info)
38+
if i == nil {
39+
fmt.Println("fatal: could not find interface with @microgen tag")
5540
os.Exit(1)
5641
}
5742

58-
if *flagDebug {
59-
ast.Print(fset, f)
60-
spew.Dump(i)
61-
}
62-
63-
var strategy generator.Strategy
64-
if *flagOutputDir == "" {
65-
strategy = generator.NewWriterStrategy(os.Stdout)
66-
} else {
67-
strategy = generator.NewFileStrategy(*flagOutputDir)
43+
if err := generator.ValidateInterface(i); err != nil {
44+
fmt.Println("validation:", err)
45+
os.Exit(1)
6846
}
6947

70-
packagePath := resolvePackagePath(*flagOutputDir)
71-
templates := []generator.Template{
72-
&template.ExchangeTemplate{},
73-
&template.EndpointsTemplate{},
74-
&template.ClientTemplate{},
75-
&template.MiddlewareTemplate{PackagePath: packagePath},
76-
&template.LoggingTemplate{PackagePath: packagePath},
48+
units, err := generator.ListTemplatesForGen(i, *flagForce, info.Name, *flagOutputDir, *flagFileName)
49+
if err != nil {
50+
fmt.Println("fatal:", err)
51+
os.Exit(1)
7752
}
78-
if *flagGRPC {
79-
templates = append(templates,
80-
&template.GRPCServerTemplate{},
81-
&template.GRPCClientTemplate{PackagePath: packagePath},
82-
&template.GRPCEndpointConverterTemplate{PackagePath: packagePath},
83-
)
84-
if *flagInit {
85-
templates = append(templates,
86-
&template.StubGRPCTypeConverterTemplate{PackagePath: packagePath},
87-
)
53+
for _, unit := range units {
54+
err := unit.Generate()
55+
if err != nil {
56+
fmt.Println("fatal:", err)
57+
os.Exit(1)
8858
}
8959
}
90-
91-
gen := generator.NewGenerator(templates, i, strategy)
92-
err = gen.Generate()
93-
if err != nil {
94-
fmt.Println(err.Error())
95-
}
60+
fmt.Println("All files successfully generated")
9661
}
9762

98-
func resolvePackagePath(outPath string) string {
99-
gopath := os.Getenv("GOPATH")
100-
if gopath == "" {
101-
fmt.Println("GOPATH is empty")
102-
os.Exit(1)
103-
}
104-
105-
absOutPath, err := filepath.Abs(outPath)
106-
if err != nil {
107-
fmt.Println(err.Error())
108-
os.Exit(1)
63+
func findInterface(file *types.File) *types.Interface {
64+
for i := range file.Interfaces {
65+
if docsContainMicrogenTag(file.Interfaces[i].Docs) {
66+
return &file.Interfaces[i]
67+
}
10968
}
69+
return nil
70+
}
11071

111-
gopathSrc := filepath.Join(gopath, "src")
112-
if !strings.HasPrefix(absOutPath, gopathSrc) {
113-
fmt.Println("-out not in GOPATH")
114-
os.Exit(1)
72+
func docsContainMicrogenTag(strs []string) bool {
73+
for _, str := range strs {
74+
if strings.HasPrefix(str, generator.TagMark+generator.MicrogenGeneralTag) {
75+
return true
76+
}
11577
}
116-
117-
return absOutPath[len(gopathSrc)+1:]
78+
return false
11879
}

example/svc/entity/comment.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package entity
2+
3+
import "time"
4+
5+
// Example structure
6+
type Comment struct {
7+
Text string
8+
Relates *Comment
9+
PostedAt time.Time
10+
}

0 commit comments

Comments
 (0)