Skip to content

Commit

Permalink
Fixes #1
Browse files Browse the repository at this point in the history
- 生成代码时,加上 imports 信息
- 支持更多的结构体字段类型
- gkit 依赖升级至 v0.0.4
  • Loading branch information
chenmingyong0423 committed Sep 27, 2023
1 parent d8e9541 commit 59164c0
Show file tree
Hide file tree
Showing 8 changed files with 290 additions and 11 deletions.
2 changes: 1 addition & 1 deletion cmd/optioner/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func main() {
log.Printf("Target \"[%s]\" is not be found\n", g.StructInfo.StructName)
os.Exit(1)
}

fmt.Println(g.StructInfo.OptionalFields)
g.GenerateCodeByTemplate()
g.OutputToFile()
}
117 changes: 117 additions & 0 deletions example/opt_user_gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Generated by optioner -type User; DO NOT EDIT
// If you have any questions, please create issues and submit contributions at:
// https://github.com/chenmingyong0423/go-optioner

package example

import (
"github.com/chenmingyong0423/go-optioner/example/third_party"
)

type UserOption func(*User)

func NewUser(opts ...UserOption) *User {
user := &User{}

for _, opt := range opts {
opt(user)
}

return user
}

func WithUsername(username string) UserOption {
return func(user *User) {
user.Username = username
}
}

func WithEmail(email string) UserOption {
return func(user *User) {
user.Email = email
}
}

func WithAddress(address Address) UserOption {
return func(user *User) {
user.Address = address
}
}

func WithArrayField(arrayField [4]int) UserOption {
return func(user *User) {
user.ArrayField = arrayField
}
}

func WithSliceField(sliceField []int) UserOption {
return func(user *User) {
user.SliceField = sliceField
}
}

func WithThirdPartyField(thirdPartyField third_party.ThirdParty) UserOption {
return func(user *User) {
user.ThirdPartyField = thirdPartyField
}
}

func WithMapField(mapField map[string]int) UserOption {
return func(user *User) {
user.MapField = mapField
}
}

func WithPtrField(ptrField *int) UserOption {
return func(user *User) {
user.PtrField = ptrField
}
}

func WithEmptyStructFiled(emptyStructFiled struct{}) UserOption {
return func(user *User) {
user.EmptyStructFiled = emptyStructFiled
}
}

func WithSimpleFuncField(simpleFuncField func()) UserOption {
return func(user *User) {
user.SimpleFuncField = simpleFuncField
}
}

func WithComplexFuncField(complexFuncField func(a int)) UserOption {
return func(user *User) {
user.ComplexFuncField = complexFuncField
}
}

func WithComplexFuncFieldV2(complexFuncFieldV2 func() int) UserOption {
return func(user *User) {
user.ComplexFuncFieldV2 = complexFuncFieldV2
}
}

func WithComplexFuncFieldV3(complexFuncFieldV3 func(a int) int) UserOption {
return func(user *User) {
user.ComplexFuncFieldV3 = complexFuncFieldV3
}
}

func WithComplexFuncFieldV4(complexFuncFieldV4 func(a int) (int, error)) UserOption {
return func(user *User) {
user.ComplexFuncFieldV4 = complexFuncFieldV4
}
}

func WithChanField(chanField chan int) UserOption {
return func(user *User) {
user.ChanField = chanField
}
}

func WithError(error error) UserOption {
return func(user *User) {
user.error = error
}
}
19 changes: 19 additions & 0 deletions example/third_party/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2023 chenmingyong0423

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package third_party

type ThirdParty struct {
Name string
}
43 changes: 43 additions & 0 deletions example/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2023 chenmingyong0423

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package example

import (
"github.com/chenmingyong0423/go-optioner/example/third_party"
)

type User struct {
Username string
Email string
Address // combined struct
ArrayField [4]int
SliceField []int
ThirdPartyField third_party.ThirdParty
MapField map[string]int
PtrField *int
EmptyStructFiled struct{}
SimpleFuncField func()
ComplexFuncField func(a int)
ComplexFuncFieldV2 func() int
ComplexFuncFieldV3 func(a int) int
ComplexFuncFieldV4 func(a int) (int, error)
ChanField chan int
error // interface
}

type Address struct {
Street string
City string
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module github.com/chenmingyong0423/go-optioner

go 1.20
go 1.21.0

require github.com/chenmingyong0423/gkit v0.0.3 // indirect
require github.com/chenmingyong0423/gkit v0.0.4 // indirect
12 changes: 10 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
github.com/chenmingyong0423/gkit v0.0.3 h1:hjq6NjARJRiITclDsRDL3o+h/qeK5cwGrGng14ggsYY=
github.com/chenmingyong0423/gkit v0.0.3/go.mod h1:vyxci/+5NnMMbp+DQPs94BUwawmbNJEK1o8ezlrS3eg=
github.com/chenmingyong0423/gkit v0.0.4 h1:LEToNpfzAN3aCUxCRltfO5j7jhtRntKt9LvwzhPhayc=
github.com/chenmingyong0423/gkit v0.0.4/go.mod h1:vyxci/+5NnMMbp+DQPs94BUwawmbNJEK1o8ezlrS3eg=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
94 changes: 89 additions & 5 deletions options/options_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"log"
"os"
"reflect"
"strconv"
"strings"
)

Expand Down Expand Up @@ -59,6 +60,8 @@ type StructInfo struct {
NewStructName string
Fields []FieldInfo
OptionalFields []FieldInfo

Imports []string
}

func (g *Generator) GeneratingOptions() {
Expand Down Expand Up @@ -99,14 +102,19 @@ func (g *Generator) parseStruct(fileName string) bool {
if structDecl, ok := typeSpec.Type.(*ast.StructType); ok {
log.Printf("Generating Struct \"%s\" \n", g.StructInfo.StructName)
for _, field := range structDecl.Fields.List {
fieldName := ""
if len(field.Names) == 0 {
continue
if ident, ok := field.Type.(*ast.Ident); ok { // combined struct
fieldName = ident.Name
} else {
continue
}
} else {
fieldName = field.Names[0].Name
}

optionIgnore := false

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

func (g *Generator) GenerateCodeByTemplate() {
tmpl, err := template.New("options").Funcs(template.FuncMap{"bigCamelToSmallCamel": stringx.BigCamelToSmallCamel}).Parse(templates.OptionsTemplateCode)
tmpl, err := template.New("options").Funcs(template.FuncMap{"bigCamelToSmallCamel": stringx.BigCamelToSmallCamel, "capitalizeFirstLetter": stringx.CapitalizeFirstLetter}).Parse(templates.OptionsTemplateCode)
if err != nil {
fmt.Println("Failed to parse template:", err)
os.Exit(1)
Expand Down Expand Up @@ -173,3 +183,77 @@ func (g *Generator) SetOutPath(outPath *string) {
g.outPath = fileName
}
}

func (g *Generator) getTypeName(expr ast.Expr) string {
switch t := expr.(type) {
case *ast.Ident:
return t.Name
case *ast.SelectorExpr:
return fmt.Sprintf("%s.%s", g.getTypeName(t.X), t.Sel.Name)
case *ast.ArrayType:
if t.Len == nil {
return "[]" + g.getTypeName(t.Elt)
}
if basicLit, ok := t.Len.(*ast.BasicLit); ok && basicLit.Kind == token.INT {
return "[" + basicLit.Value + "]" + g.getTypeName(t.Elt)
} else {
log.Fatalf("Array len error: %T", t)
return ""
}
case *ast.MapType:
return fmt.Sprintf("map[%s]%s", g.getTypeName(t.Key), g.getTypeName(t.Value))
case *ast.StarExpr:
return "*" + g.getTypeName(t.X)
//case *ast.InterfaceType:
// return "" // ignore
case *ast.StructType:
return "struct{}"
case *ast.FuncType:
return g.parseFuncType(t)
case *ast.ChanType:
return "chan " + g.getTypeName(t.Value)
default:
log.Fatalf("Unsupported type for field: %T", t)
return ""
}
}

func (g *Generator) parseFuncType(f *ast.FuncType) string {
var params, results []string
if f.Params != nil {
for _, field := range f.Params.List {
paramType := g.getTypeName(field.Type)
for _, name := range field.Names {
params = append(params, fmt.Sprintf("%s %s", name.Name, paramType))
}
}
}

if f.Results != nil {
for _, field := range f.Results.List {
resultType := g.getTypeName(field.Type)
if len(field.Names) > 0 {
for _, name := range field.Names {
results = append(results, fmt.Sprintf("%s %s", name.Name, resultType))
}
} else {
results = append(results, resultType)
}
}
}

if len(results) == 1 {
return fmt.Sprintf("func(%s) %s", strings.Join(params, ", "), results[0])
}
return fmt.Sprintf("func(%s) (%s)", strings.Join(params, ", "), strings.Join(results, ", "))
}

func (g *Generator) CollectImports(file *ast.File) {
for _, imp := range file.Imports {
path, err := strconv.Unquote(imp.Path.Value)
if err != nil {
log.Fatalf("Failed to unquote import path: %v", err)
}
g.StructInfo.Imports = append(g.StructInfo.Imports, path)
}
}
10 changes: 9 additions & 1 deletion templates/options_templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ const OptionsTemplateCode = `
package {{ .PackageName }}
{{ if .Imports }}
import (
{{ range .Imports }}
"{{ . }}"
{{ end }}
)
{{ end }}
type {{ .StructName }}Option func(*{{ .StructName }})
func New{{ .StructName }}({{ range $index, $field := .Fields }}{{ $field.Name | bigCamelToSmallCamel }} {{ $field.Type }},{{ end }} opts ...{{ .StructName }}Option) *{{ .StructName }} {
Expand All @@ -38,7 +46,7 @@ func New{{ .StructName }}({{ range $index, $field := .Fields }}{{ $field.Name |
{{ if .OptionalFields }}
{{ range $field := .OptionalFields }}
func With{{ $field.Name }}({{ $field.Name | bigCamelToSmallCamel }} {{ $field.Type }}) {{ $.StructName }}Option {
func With{{ $field.Name | capitalizeFirstLetter }}({{ $field.Name | bigCamelToSmallCamel }} {{ $field.Type }}) {{ $.StructName }}Option {
return func({{ $.NewStructName }} *{{ $.StructName }}) {
{{ $.NewStructName }}.{{ $field.Name }} = {{ $field.Name | bigCamelToSmallCamel }}
}
Expand Down

0 comments on commit 59164c0

Please sign in to comment.