Skip to content

Commit 01fc3e5

Browse files
authored
Merge pull request #31 from devimteam/develop
0.5.0
2 parents 07f0bf7 + 6522e8b commit 01fc3e5

19 files changed

Lines changed: 445 additions & 182 deletions

Gopkg.lock

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

Gopkg.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@
2626

2727
[[constraint]]
2828
name = "github.com/vetcher/godecl"
29-
version = "^0.2.0"
29+
version = "^1.0.0"

README.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ The goal is to generate code for service which not fun to write but it should be
88
go get -u github.com/devimteam/microgen/cmd/microgen
99
```
1010

11+
Note: If you have problems with building microgen, please, install [dep](https://github.com/golang/dep) and use `dep ensure` command to install correct versions of dependencies ([#29](https://github.com/devimteam/microgen/issues/29)).
12+
1113
## Usage
1214
``` sh
1315
microgen [OPTIONS]
@@ -33,7 +35,7 @@ Typical syntax is: `// @<tag-name>:`
3335

3436
#### @microgen
3537
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:`
38+
To add templates for generation, add their [tags](#tags), separated by comma after `@microgen:`
3739
Example:
3840
```go
3941
// @microgen middleware, logging
@@ -82,13 +84,26 @@ type FileService interface {
8284
}
8385
```
8486

87+
#### @logs-len
88+
This tag is used for logging middleware. It prints length of parameters.
89+
Example:
90+
```go
91+
// @microgen logging
92+
type FileService interface {
93+
// @logs-ignore data
94+
// @logs-len data
95+
UploadFile(ctx context.Context, name string, data []byte) (link string, err error)
96+
}
97+
```
98+
8599
### Tags
86100
All allowed tags for customize generation provided here.
87101

88102
| Tag | Description |
89103
|:------------|:------------------------------------------------------------------------------------------------------------------------------|
90104
| middleware | General application middleware interface. Generates every time. |
91105
| logging | Middleware that writes to logger all request/response information with handled time. Generates every time. |
106+
| recover | Middleware that recovers panics and writes errors to logger. Generates every time. |
92107
| grpc-client | Generates client for grpc transport with request/response encoders/decoders. Do not generates again if file exist. |
93108
| grpc-server | Generates server for grpc transport with request/response encoders/decoders. Do not generates again if file exist. |
94109
| grpc | Generates client and server for grpc transport with request/response encoders/decoders. Do not generates again if file exist. |
@@ -148,3 +163,22 @@ For correct generation, please, follow rules below.
148163
* Function names in _protobuf_ should be the same, as in interface.
149164
* Message names in _protobuf_ should be named `<FunctionName>Request` or `<FunctionName>Response` for request/response message respectively.
150165
* Field names in _protobuf_ messages should be the same, as in interface methods (_protobuf_ - snake_case, interface - camelCase).
166+
167+
## Dependency
168+
After generation your service may depend on this packages:
169+
```
170+
"net/http" // for http purposes
171+
"bytes"
172+
"encoding/json" // for http purposes
173+
"io/ioutil"
174+
"strings"
175+
"net/url" // for http purposes
176+
"fmt"
177+
"context"
178+
"time" // for logging
179+
180+
"google.golang.org/grpc" // for grpc purposes
181+
"golang.org/x/net/context"
182+
"github.com/go-kit/kit" // for grpc purposes
183+
"github.com/golang/protobuf/ptypes/empty" // for grpc purposes
184+
```

cmd/microgen/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"github.com/vetcher/godecl/types"
1313
)
1414

15+
const Version = generator.Version
16+
1517
var (
1618
flagFileName = flag.String("file", "service.go", "Name of file where described interface definition")
1719
flagOutputDir = flag.String("out", ".", "Output directory")
@@ -24,6 +26,7 @@ func init() {
2426
}
2527

2628
func main() {
29+
fmt.Println("@microgen", Version)
2730
if *flagHelp || *flagFileName == "" {
2831
flag.Usage()
2932
os.Exit(0)

example/svc/svc.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ import (
66
"github.com/devimteam/microgen/example/svc/entity"
77
)
88

9-
// @microgen middleware, logging, grpc, http
9+
// @microgen middleware, logging, grpc, http, recover
1010
// @grpc-addr devim.string.team
1111
// @protobuf github.com/devimteam/protobuf/stringsvc
1212
type StringService interface {
1313
// @logs-ignore ans, err
14-
Uppercase(ctx context.Context, str string) (ans string, err error)
14+
Uppercase(ctx context.Context, str ...map[string]interface{}) (ans string, err error)
1515
Count(ctx context.Context, text string, symbol string) (count int, positions []int, err error)
1616
// @logs-len comments
1717
TestCase(ctx context.Context, comments []*entity.Comment) (tree map[string]int, err error)

generator/decide.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ func tagToTemplate(tag string, info *template.GenerationInfo) (tmpls []template.
122122
template.NewHttpClientTemplate(info),
123123
template.NewHttpConverterTemplate(info),
124124
)
125+
case "recover":
126+
return append(tmpls, template.NewRecoverTemplate(info))
125127
}
126128
return nil
127129
}

generator/generator.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"github.com/devimteam/microgen/generator/write_strategy"
99
)
1010

11+
const Version = template.Version
12+
1113
var (
1214
EmptyTemplateError = errors.New("empty template")
1315
EmptyStrategyError = errors.New("empty strategy")

generator/template/common.go

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package template
22

33
import (
4+
"strings"
5+
46
. "github.com/dave/jennifer/jen"
57
"github.com/devimteam/microgen/util"
68
"github.com/vetcher/godecl/types"
@@ -23,11 +25,13 @@ const (
2325
PackagePathStrings = "strings"
2426
PackagePathUrl = "net/url"
2527
PackagePathEmptyProtobuf = "github.com/golang/protobuf/ptypes/empty"
28+
PackagePathFmt = "fmt"
2629

2730
TagMark = "// @"
2831
ForceTag = "force"
2932

30-
FileHeader = `This file was automatically generated by "microgen" utility.`
33+
Version = "0.5.0"
34+
FileHeader = `This file was automatically generated by "microgen ` + Version + `" utility.`
3135
)
3236

3337
type WriteStrategyState int
@@ -76,10 +80,11 @@ func removeContextIfFirst(fields []types.Variable) []types.Variable {
7680
}
7781

7882
func IsContextFirst(fields []types.Variable) bool {
79-
return len(fields) > 0 &&
80-
fields[0].Type.Import != nil &&
81-
fields[0].Type.Import.Package == PackagePathContext &&
82-
fields[0].Type.Name == "Context"
83+
name := types.TypeName(fields[0].Type)
84+
return name != nil && len(fields) > 0 &&
85+
types.TypeImport(fields[0].Type) != nil &&
86+
types.TypeImport(fields[0].Type).Package == PackagePathContext &&
87+
*name == "Context"
8388
}
8489

8590
// Remove from function fields error if it is last in slice
@@ -91,9 +96,10 @@ func removeErrorIfLast(fields []types.Variable) []types.Variable {
9196
}
9297

9398
func IsErrorLast(fields []types.Variable) bool {
94-
return len(fields) > 0 &&
95-
fields[len(fields)-1].Type.Import == nil &&
96-
fields[len(fields)-1].Type.Name == "error"
99+
name := types.TypeName(fields[len(fields)-1].Type)
100+
return name != nil && len(fields) > 0 &&
101+
types.TypeImport(fields[len(fields)-1].Type) == nil &&
102+
*name == "error"
97103
}
98104

99105
// Return name of error, if error is last result, else return `err`
@@ -110,8 +116,11 @@ func nameOfLastResultError(fn *types.Function) string {
110116
//
111117
func structField(field *types.Variable) *Statement {
112118
s := structFieldName(field)
113-
s.Add(fieldType(&field.Type))
119+
s.Add(fieldType(field.Type, false))
114120
s.Tag(map[string]string{"json": util.ToSnakeCase(field.Name)})
121+
if types.IsEllipsis(field.Type) {
122+
s.Comment("This field was defined with ellipsis (...).")
123+
}
115124
return s
116125
}
117126

@@ -123,7 +132,7 @@ func funcDefinitionParams(fields []types.Variable) *Statement {
123132
c := &Statement{}
124133
c.ListFunc(func(g *Group) {
125134
for _, field := range fields {
126-
g.Id(util.ToLowerFirst(field.Name)).Add(fieldType(&field.Type))
135+
g.Id(util.ToLowerFirst(field.Name)).Add(fieldType(field.Type, true))
127136
}
128137
})
129138
return c
@@ -133,26 +142,52 @@ func funcDefinitionParams(fields []types.Variable) *Statement {
133142
//
134143
// *repository.Visit
135144
//
136-
func fieldType(field *types.Type) *Statement {
145+
func fieldType(field types.Type, useEllipsis bool) *Statement {
137146
c := &Statement{}
138-
if field.IsArray {
139-
c.Index()
147+
for field != nil {
148+
switch f := field.(type) {
149+
case types.TImport:
150+
if f.Import != nil {
151+
c.Qual(f.Import.Package, "")
152+
}
153+
field = f.Next
154+
case types.TName:
155+
c.Id(f.TypeName)
156+
field = nil
157+
case types.TArray:
158+
if f.IsSlice {
159+
c.Index()
160+
} else if f.ArrayLen > 0 {
161+
c.Index(Lit(f.ArrayLen))
162+
}
163+
field = f.Next
164+
case types.TMap:
165+
return c.Map(fieldType(f.Key, false)).Add(fieldType(f.Value, false))
166+
case types.TPointer:
167+
c.Op(strings.Repeat("*", f.NumberOfPointers))
168+
field = f.Next
169+
case types.TInterface:
170+
mhds := interfaceType(f.Interface)
171+
return c.Interface(mhds...)
172+
case types.TEllipsis:
173+
if useEllipsis {
174+
c.Op("...")
175+
} else {
176+
c.Index()
177+
}
178+
field = f.Next
179+
default:
180+
return c
181+
}
140182
}
183+
return c
184+
}
141185

142-
if field.IsPointer {
143-
c.Op("*")
144-
}
145-
if field.IsMap {
146-
m := field.Map
147-
return c.Map(fieldType(&m.Key)).Add(fieldType(&m.Value))
186+
func interfaceType(p *types.Interface) (code []Code) {
187+
for _, x := range p.Methods {
188+
code = append(code, functionDefinition(x))
148189
}
149-
if field.Import != nil {
150-
c.Qual(field.Import.Package, field.Name)
151-
} else {
152-
c.Id(field.Name)
153-
}
154-
155-
return c
190+
return
156191
}
157192

158193
// Renders key/value pairs wrapped in Dict for provided fields.
@@ -175,7 +210,11 @@ func dictByVariables(fields []types.Variable) Dict {
175210
func paramNames(fields []types.Variable) *Statement {
176211
var list []Code
177212
for _, field := range fields {
178-
list = append(list, Id(util.ToLowerFirst(field.Name)))
213+
v := Id(util.ToLowerFirst(field.Name))
214+
if types.IsEllipsis(field.Type) {
215+
v.Op("...")
216+
}
217+
list = append(list, v)
179218
}
180219
return List(list...)
181220
}
@@ -187,7 +226,15 @@ func paramNames(fields []types.Variable) *Statement {
187226
func methodDefinition(obj string, signature *types.Function) *Statement {
188227
return Func().
189228
Params(Id(util.LastUpperOrFirst(obj)).Op("*").Id(obj)).
190-
Id(signature.Name).
229+
Add(functionDefinition(signature))
230+
}
231+
232+
// Render full method definition with receiver, method name, args and results.
233+
//
234+
// func Count(ctx context.Context, text string, symbol string) (count int)
235+
//
236+
func functionDefinition(signature *types.Function) *Statement {
237+
return Id(signature.Name).
191238
Params(funcDefinitionParams(signature.Args)).
192239
Params(funcDefinitionParams(signature.Results))
193240
}

generator/template/endpoints.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,11 @@ func createEndpointBody(signature *types.Function) *Statement {
189189
CallFunc(func(g *Group) {
190190
g.Add(Id(firstArgName(signature)))
191191
for _, field := range methodParams {
192-
g.Add(Id("_req").Dot(util.ToUpperFirst(field.Name)))
192+
v := Dot(util.ToUpperFirst(field.Name))
193+
if types.IsEllipsis(field.Type) {
194+
v.Op("...")
195+
}
196+
g.Add(Id("_req").Add(v))
193197
}
194198
}))
195199

0 commit comments

Comments
 (0)