Skip to content

Commit 59164c0

Browse files
Fixes #1
- 生成代码时,加上 imports 信息 - 支持更多的结构体字段类型 - gkit 依赖升级至 v0.0.4
1 parent d8e9541 commit 59164c0

File tree

8 files changed

+290
-11
lines changed

8 files changed

+290
-11
lines changed

cmd/optioner/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func main() {
5454
log.Printf("Target \"[%s]\" is not be found\n", g.StructInfo.StructName)
5555
os.Exit(1)
5656
}
57-
57+
fmt.Println(g.StructInfo.OptionalFields)
5858
g.GenerateCodeByTemplate()
5959
g.OutputToFile()
6060
}

example/opt_user_gen.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Generated by optioner -type User; DO NOT EDIT
2+
// If you have any questions, please create issues and submit contributions at:
3+
// https://github.com/chenmingyong0423/go-optioner
4+
5+
package example
6+
7+
import (
8+
"github.com/chenmingyong0423/go-optioner/example/third_party"
9+
)
10+
11+
type UserOption func(*User)
12+
13+
func NewUser(opts ...UserOption) *User {
14+
user := &User{}
15+
16+
for _, opt := range opts {
17+
opt(user)
18+
}
19+
20+
return user
21+
}
22+
23+
func WithUsername(username string) UserOption {
24+
return func(user *User) {
25+
user.Username = username
26+
}
27+
}
28+
29+
func WithEmail(email string) UserOption {
30+
return func(user *User) {
31+
user.Email = email
32+
}
33+
}
34+
35+
func WithAddress(address Address) UserOption {
36+
return func(user *User) {
37+
user.Address = address
38+
}
39+
}
40+
41+
func WithArrayField(arrayField [4]int) UserOption {
42+
return func(user *User) {
43+
user.ArrayField = arrayField
44+
}
45+
}
46+
47+
func WithSliceField(sliceField []int) UserOption {
48+
return func(user *User) {
49+
user.SliceField = sliceField
50+
}
51+
}
52+
53+
func WithThirdPartyField(thirdPartyField third_party.ThirdParty) UserOption {
54+
return func(user *User) {
55+
user.ThirdPartyField = thirdPartyField
56+
}
57+
}
58+
59+
func WithMapField(mapField map[string]int) UserOption {
60+
return func(user *User) {
61+
user.MapField = mapField
62+
}
63+
}
64+
65+
func WithPtrField(ptrField *int) UserOption {
66+
return func(user *User) {
67+
user.PtrField = ptrField
68+
}
69+
}
70+
71+
func WithEmptyStructFiled(emptyStructFiled struct{}) UserOption {
72+
return func(user *User) {
73+
user.EmptyStructFiled = emptyStructFiled
74+
}
75+
}
76+
77+
func WithSimpleFuncField(simpleFuncField func()) UserOption {
78+
return func(user *User) {
79+
user.SimpleFuncField = simpleFuncField
80+
}
81+
}
82+
83+
func WithComplexFuncField(complexFuncField func(a int)) UserOption {
84+
return func(user *User) {
85+
user.ComplexFuncField = complexFuncField
86+
}
87+
}
88+
89+
func WithComplexFuncFieldV2(complexFuncFieldV2 func() int) UserOption {
90+
return func(user *User) {
91+
user.ComplexFuncFieldV2 = complexFuncFieldV2
92+
}
93+
}
94+
95+
func WithComplexFuncFieldV3(complexFuncFieldV3 func(a int) int) UserOption {
96+
return func(user *User) {
97+
user.ComplexFuncFieldV3 = complexFuncFieldV3
98+
}
99+
}
100+
101+
func WithComplexFuncFieldV4(complexFuncFieldV4 func(a int) (int, error)) UserOption {
102+
return func(user *User) {
103+
user.ComplexFuncFieldV4 = complexFuncFieldV4
104+
}
105+
}
106+
107+
func WithChanField(chanField chan int) UserOption {
108+
return func(user *User) {
109+
user.ChanField = chanField
110+
}
111+
}
112+
113+
func WithError(error error) UserOption {
114+
return func(user *User) {
115+
user.error = error
116+
}
117+
}

example/third_party/types.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2023 chenmingyong0423
2+
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package third_party
16+
17+
type ThirdParty struct {
18+
Name string
19+
}

example/user.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2023 chenmingyong0423
2+
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package example
16+
17+
import (
18+
"github.com/chenmingyong0423/go-optioner/example/third_party"
19+
)
20+
21+
type User struct {
22+
Username string
23+
Email string
24+
Address // combined struct
25+
ArrayField [4]int
26+
SliceField []int
27+
ThirdPartyField third_party.ThirdParty
28+
MapField map[string]int
29+
PtrField *int
30+
EmptyStructFiled struct{}
31+
SimpleFuncField func()
32+
ComplexFuncField func(a int)
33+
ComplexFuncFieldV2 func() int
34+
ComplexFuncFieldV3 func(a int) int
35+
ComplexFuncFieldV4 func(a int) (int, error)
36+
ChanField chan int
37+
error // interface
38+
}
39+
40+
type Address struct {
41+
Street string
42+
City string
43+
}

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module github.com/chenmingyong0423/go-optioner
22

3-
go 1.20
3+
go 1.21.0
44

5-
require github.com/chenmingyong0423/gkit v0.0.3 // indirect
5+
require github.com/chenmingyong0423/gkit v0.0.4 // indirect

go.sum

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,10 @@
1-
github.com/chenmingyong0423/gkit v0.0.3 h1:hjq6NjARJRiITclDsRDL3o+h/qeK5cwGrGng14ggsYY=
2-
github.com/chenmingyong0423/gkit v0.0.3/go.mod h1:vyxci/+5NnMMbp+DQPs94BUwawmbNJEK1o8ezlrS3eg=
1+
github.com/chenmingyong0423/gkit v0.0.4 h1:LEToNpfzAN3aCUxCRltfO5j7jhtRntKt9LvwzhPhayc=
2+
github.com/chenmingyong0423/gkit v0.0.4/go.mod h1:vyxci/+5NnMMbp+DQPs94BUwawmbNJEK1o8ezlrS3eg=
3+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
4+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
6+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
7+
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
8+
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
9+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
10+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

options/options_generator.go

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"log"
2929
"os"
3030
"reflect"
31+
"strconv"
3132
"strings"
3233
)
3334

@@ -59,6 +60,8 @@ type StructInfo struct {
5960
NewStructName string
6061
Fields []FieldInfo
6162
OptionalFields []FieldInfo
63+
64+
Imports []string
6265
}
6366

6467
func (g *Generator) GeneratingOptions() {
@@ -99,14 +102,19 @@ func (g *Generator) parseStruct(fileName string) bool {
99102
if structDecl, ok := typeSpec.Type.(*ast.StructType); ok {
100103
log.Printf("Generating Struct \"%s\" \n", g.StructInfo.StructName)
101104
for _, field := range structDecl.Fields.List {
105+
fieldName := ""
102106
if len(field.Names) == 0 {
103-
continue
107+
if ident, ok := field.Type.(*ast.Ident); ok { // combined struct
108+
fieldName = ident.Name
109+
} else {
110+
continue
111+
}
112+
} else {
113+
fieldName = field.Names[0].Name
104114
}
105-
106115
optionIgnore := false
107116

108-
fieldName := field.Names[0].Name
109-
fieldType := field.Type.(*ast.Ident).Name
117+
fieldType := g.getTypeName(field.Type)
110118
if field.Tag != nil {
111119
tags := strings.Replace(field.Tag.Value, "`", "", -1)
112120
tag := reflect.StructTag(tags).Get("opt")
@@ -126,6 +134,8 @@ func (g *Generator) parseStruct(fileName string) bool {
126134
}
127135
log.Printf("Generating Struct Field \"%s\" of type \"%s\"\n", fieldName, fieldType)
128136
}
137+
// 收集 package 信息
138+
g.CollectImports(file)
129139
return true
130140
} else {
131141
log.Fatal(fmt.Sprintf("Target[%s] type is not a struct", g.StructInfo.StructName))
@@ -136,7 +146,7 @@ func (g *Generator) parseStruct(fileName string) bool {
136146
}
137147

138148
func (g *Generator) GenerateCodeByTemplate() {
139-
tmpl, err := template.New("options").Funcs(template.FuncMap{"bigCamelToSmallCamel": stringx.BigCamelToSmallCamel}).Parse(templates.OptionsTemplateCode)
149+
tmpl, err := template.New("options").Funcs(template.FuncMap{"bigCamelToSmallCamel": stringx.BigCamelToSmallCamel, "capitalizeFirstLetter": stringx.CapitalizeFirstLetter}).Parse(templates.OptionsTemplateCode)
140150
if err != nil {
141151
fmt.Println("Failed to parse template:", err)
142152
os.Exit(1)
@@ -173,3 +183,77 @@ func (g *Generator) SetOutPath(outPath *string) {
173183
g.outPath = fileName
174184
}
175185
}
186+
187+
func (g *Generator) getTypeName(expr ast.Expr) string {
188+
switch t := expr.(type) {
189+
case *ast.Ident:
190+
return t.Name
191+
case *ast.SelectorExpr:
192+
return fmt.Sprintf("%s.%s", g.getTypeName(t.X), t.Sel.Name)
193+
case *ast.ArrayType:
194+
if t.Len == nil {
195+
return "[]" + g.getTypeName(t.Elt)
196+
}
197+
if basicLit, ok := t.Len.(*ast.BasicLit); ok && basicLit.Kind == token.INT {
198+
return "[" + basicLit.Value + "]" + g.getTypeName(t.Elt)
199+
} else {
200+
log.Fatalf("Array len error: %T", t)
201+
return ""
202+
}
203+
case *ast.MapType:
204+
return fmt.Sprintf("map[%s]%s", g.getTypeName(t.Key), g.getTypeName(t.Value))
205+
case *ast.StarExpr:
206+
return "*" + g.getTypeName(t.X)
207+
//case *ast.InterfaceType:
208+
// return "" // ignore
209+
case *ast.StructType:
210+
return "struct{}"
211+
case *ast.FuncType:
212+
return g.parseFuncType(t)
213+
case *ast.ChanType:
214+
return "chan " + g.getTypeName(t.Value)
215+
default:
216+
log.Fatalf("Unsupported type for field: %T", t)
217+
return ""
218+
}
219+
}
220+
221+
func (g *Generator) parseFuncType(f *ast.FuncType) string {
222+
var params, results []string
223+
if f.Params != nil {
224+
for _, field := range f.Params.List {
225+
paramType := g.getTypeName(field.Type)
226+
for _, name := range field.Names {
227+
params = append(params, fmt.Sprintf("%s %s", name.Name, paramType))
228+
}
229+
}
230+
}
231+
232+
if f.Results != nil {
233+
for _, field := range f.Results.List {
234+
resultType := g.getTypeName(field.Type)
235+
if len(field.Names) > 0 {
236+
for _, name := range field.Names {
237+
results = append(results, fmt.Sprintf("%s %s", name.Name, resultType))
238+
}
239+
} else {
240+
results = append(results, resultType)
241+
}
242+
}
243+
}
244+
245+
if len(results) == 1 {
246+
return fmt.Sprintf("func(%s) %s", strings.Join(params, ", "), results[0])
247+
}
248+
return fmt.Sprintf("func(%s) (%s)", strings.Join(params, ", "), strings.Join(results, ", "))
249+
}
250+
251+
func (g *Generator) CollectImports(file *ast.File) {
252+
for _, imp := range file.Imports {
253+
path, err := strconv.Unquote(imp.Path.Value)
254+
if err != nil {
255+
log.Fatalf("Failed to unquote import path: %v", err)
256+
}
257+
g.StructInfo.Imports = append(g.StructInfo.Imports, path)
258+
}
259+
}

templates/options_templates.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ const OptionsTemplateCode = `
2121
2222
package {{ .PackageName }}
2323
24+
{{ if .Imports }}
25+
import (
26+
{{ range .Imports }}
27+
"{{ . }}"
28+
{{ end }}
29+
)
30+
{{ end }}
31+
2432
type {{ .StructName }}Option func(*{{ .StructName }})
2533
2634
func New{{ .StructName }}({{ range $index, $field := .Fields }}{{ $field.Name | bigCamelToSmallCamel }} {{ $field.Type }},{{ end }} opts ...{{ .StructName }}Option) *{{ .StructName }} {
@@ -38,7 +46,7 @@ func New{{ .StructName }}({{ range $index, $field := .Fields }}{{ $field.Name |
3846
3947
{{ if .OptionalFields }}
4048
{{ range $field := .OptionalFields }}
41-
func With{{ $field.Name }}({{ $field.Name | bigCamelToSmallCamel }} {{ $field.Type }}) {{ $.StructName }}Option {
49+
func With{{ $field.Name | capitalizeFirstLetter }}({{ $field.Name | bigCamelToSmallCamel }} {{ $field.Type }}) {{ $.StructName }}Option {
4250
return func({{ $.NewStructName }} *{{ $.StructName }}) {
4351
{{ $.NewStructName }}.{{ $field.Name }} = {{ $field.Name | bigCamelToSmallCamel }}
4452
}

0 commit comments

Comments
 (0)