Skip to content

Commit 12810e0

Browse files
committed
feat: add gplus cmd tool
1 parent 0a1bcd9 commit 12810e0

File tree

6 files changed

+679
-27
lines changed

6 files changed

+679
-27
lines changed

cmd/gplus/gen.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"go/ast"
7+
"go/format"
8+
"go/types"
9+
"io"
10+
11+
"github.com/dave/jennifer/jen"
12+
"sigs.k8s.io/controller-tools/pkg/genall"
13+
"sigs.k8s.io/controller-tools/pkg/loader"
14+
"sigs.k8s.io/controller-tools/pkg/markers"
15+
)
16+
17+
type StructInfo struct {
18+
StructName string
19+
Fields []string
20+
FieldTags []string
21+
}
22+
23+
type Generator struct{}
24+
25+
var enableTypeMarker = markers.Must(markers.MakeDefinition("gplus:column", markers.DescribesType, false))
26+
27+
func (Generator) RegisterMarkers(into *markers.Registry) error {
28+
if err := markers.RegisterAll(into, enableTypeMarker); err != nil {
29+
return err
30+
}
31+
return nil
32+
}
33+
34+
func enabledOnType(info *markers.TypeInfo) bool {
35+
if typeMarker := info.Markers.Get(enableTypeMarker.Name); typeMarker != nil {
36+
return typeMarker.(bool)
37+
}
38+
return false
39+
}
40+
41+
func (Generator) Generate(ctx *genall.GenerationContext) error {
42+
for _, root := range ctx.Roots {
43+
ctx.Checker.Check(root, func(node ast.Node) bool {
44+
// ignore interfaces
45+
_, isIface := node.(*ast.InterfaceType)
46+
return !isIface
47+
})
48+
49+
root.NeedTypesInfo()
50+
51+
var allStructInfos []StructInfo
52+
if err := markers.EachType(ctx.Collector, root, func(info *markers.TypeInfo) {
53+
if !enabledOnType(info) {
54+
return
55+
}
56+
allStructInfos = append(allStructInfos, buildStructInfo(info, root)...)
57+
}); err != nil {
58+
root.AddError(err)
59+
return nil
60+
}
61+
62+
if len(allStructInfos) > 0 {
63+
genFile := buildGenFile(root, allStructInfos)
64+
var b bytes.Buffer
65+
err := genFile.Render(&b)
66+
if err != nil {
67+
root.AddError(err)
68+
return nil
69+
}
70+
columnContent, err := format.Source(b.Bytes())
71+
if err != nil {
72+
root.AddError(err)
73+
return nil
74+
}
75+
writeOut(ctx, root, columnContent)
76+
}
77+
}
78+
return nil
79+
}
80+
81+
func buildGenFile(root *loader.Package, allStructInfos []StructInfo) *jen.File {
82+
genFile := jen.NewFile(root.Name)
83+
for _, s := range allStructInfos {
84+
genFile.Var().Id(s.StructName + "Column").Op("=").Id("struct").Id("{")
85+
for _, field := range s.Fields {
86+
genFile.Id(field).String()
87+
}
88+
genFile.Id("}").Id("{")
89+
for i, field := range s.Fields {
90+
tagName := s.FieldTags[i]
91+
filedName := fmt.Sprintf("\"%s\"", tagName)
92+
genFile.Id(field).Op(":").Id(filedName).Id(",")
93+
}
94+
genFile.Id("}")
95+
}
96+
return genFile
97+
}
98+
99+
func buildStructInfo(info *markers.TypeInfo, root *loader.Package) []StructInfo {
100+
var allStructInfos []StructInfo
101+
typeInfo := root.TypesInfo.TypeOf(info.RawSpec.Name)
102+
if typeInfo == types.Typ[types.Invalid] {
103+
root.AddError(loader.ErrFromNode(fmt.Errorf("unknown type %s", info.Name), info.RawSpec))
104+
}
105+
structType, ok := typeInfo.Underlying().(*types.Struct)
106+
if !ok {
107+
root.AddError(loader.ErrFromNode(fmt.Errorf("%s is not a struct type", info.Name), info.RawSpec))
108+
return allStructInfos
109+
}
110+
111+
structInfo := StructInfo{
112+
StructName: info.Name,
113+
Fields: make([]string, 0, structType.NumFields()),
114+
FieldTags: make([]string, 0, structType.NumFields()),
115+
}
116+
117+
for i := 0; i < structType.NumFields(); i++ {
118+
field := structType.Field(i)
119+
structInfo.Fields = append(structInfo.Fields, field.Name())
120+
structInfo.FieldTags = append(structInfo.FieldTags, structType.Tag(i))
121+
}
122+
123+
allStructInfos = append(allStructInfos, structInfo)
124+
return allStructInfos
125+
}
126+
127+
func writeOut(ctx *genall.GenerationContext, root *loader.Package, outBytes []byte) {
128+
outputFile, err := ctx.Open(root, "zz_gen.column.go")
129+
if err != nil {
130+
root.AddError(err)
131+
return
132+
}
133+
defer outputFile.Close()
134+
n, err := outputFile.Write(outBytes)
135+
if err != nil {
136+
root.AddError(err)
137+
return
138+
}
139+
if n < len(outBytes) {
140+
root.AddError(io.ErrShortWrite)
141+
}
142+
}

cmd/gplus/main.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"strings"
7+
8+
"github.com/spf13/cobra"
9+
"sigs.k8s.io/controller-tools/pkg/genall"
10+
"sigs.k8s.io/controller-tools/pkg/markers"
11+
)
12+
13+
var (
14+
allGenerators = map[string]genall.Generator{
15+
"gen": Generator{},
16+
}
17+
18+
allOutputRules = map[string]genall.OutputRule{
19+
"dir": genall.OutputToDirectory(""),
20+
"none": genall.OutputToNothing,
21+
"stdout": genall.OutputToStdout,
22+
"artifacts": genall.OutputArtifacts{},
23+
}
24+
25+
// optionsRegistry contains all the marker definitions used to process command line options
26+
optionsRegistry = &markers.Registry{}
27+
)
28+
29+
func init() {
30+
for genName, gen := range allGenerators {
31+
// make the generator options marker itself
32+
defn := markers.Must(markers.MakeDefinition(genName, markers.DescribesPackage, gen))
33+
if err := optionsRegistry.Register(defn); err != nil {
34+
panic(err)
35+
}
36+
if helpGiver, hasHelp := gen.(genall.HasHelp); hasHelp {
37+
if help := helpGiver.Help(); help != nil {
38+
optionsRegistry.AddHelp(defn, help)
39+
}
40+
}
41+
42+
// make per-generation output rule markers
43+
for ruleName, rule := range allOutputRules {
44+
ruleMarker := markers.Must(markers.MakeDefinition(fmt.Sprintf("output:%s:%s", genName, ruleName), markers.DescribesPackage, rule))
45+
if err := optionsRegistry.Register(ruleMarker); err != nil {
46+
panic(err)
47+
}
48+
if helpGiver, hasHelp := rule.(genall.HasHelp); hasHelp {
49+
if help := helpGiver.Help(); help != nil {
50+
optionsRegistry.AddHelp(ruleMarker, help)
51+
}
52+
}
53+
}
54+
}
55+
56+
// make "default output" output rule markers
57+
for ruleName, rule := range allOutputRules {
58+
ruleMarker := markers.Must(markers.MakeDefinition("output:"+ruleName, markers.DescribesPackage, rule))
59+
if err := optionsRegistry.Register(ruleMarker); err != nil {
60+
panic(err)
61+
}
62+
if helpGiver, hasHelp := rule.(genall.HasHelp); hasHelp {
63+
if help := helpGiver.Help(); help != nil {
64+
optionsRegistry.AddHelp(ruleMarker, help)
65+
}
66+
}
67+
}
68+
69+
// add in the common options markers
70+
if err := genall.RegisterOptionsMarkers(optionsRegistry); err != nil {
71+
panic(err)
72+
}
73+
}
74+
75+
type noUsageError struct{ error }
76+
77+
func main() {
78+
genCmd := newGenCmd()
79+
80+
if err := genCmd.Execute(); err != nil {
81+
if _, noUsage := err.(noUsageError); !noUsage {
82+
if err := genCmd.Usage(); err != nil {
83+
panic(err)
84+
}
85+
}
86+
fmt.Fprintf(genCmd.OutOrStderr(), "run `%[1]s %[2]s -w` to see all available markers, or `%[1]s %[2]s -h` for usage\n", genCmd.CalledAs(), strings.Join(os.Args[1:], " "))
87+
os.Exit(1)
88+
}
89+
}
90+
91+
func newGenCmd() *cobra.Command {
92+
genCmd := &cobra.Command{
93+
Use: "gen",
94+
Short: "Generate struct column codes.",
95+
Long: "Generate struct column codes.",
96+
Example: `Generate struct column codes.
97+
98+
# Run all the generators for a given project
99+
gplus gen
100+
`,
101+
RunE: func(c *cobra.Command, rawOpts []string) error {
102+
rt, err := genall.FromOptions(optionsRegistry, rawOpts)
103+
if err != nil {
104+
return err
105+
}
106+
if len(rt.Generators) == 0 {
107+
return fmt.Errorf("no generators specified")
108+
}
109+
110+
if hadErrs := rt.Run(); hadErrs {
111+
return noUsageError{fmt.Errorf("not all generators ran successfully")}
112+
}
113+
return nil
114+
},
115+
}
116+
genCmd.Flags().Bool("help", false, "print out usage and a summary of options")
117+
return genCmd
118+
}

example/user.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ package example
22

33
import "time"
44

5+
// +gplus:column=true
6+
57
type User struct {
6-
ID int64 `gorm:"primaryKey"`
7-
Username string `gorm:"column:username"`
8+
//ID int64 `gorm:"primaryKey"`
9+
//Username string `gorm:"column:username"`
10+
ID int64 `id`
11+
Username string `username`
812
Password string
913
Address string
1014
Age int

example/zz_gen_column.go

Lines changed: 0 additions & 25 deletions
This file was deleted.

go.mod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,20 @@ module github.com/gorm-plus/gorm-plus
33
go 1.18
44

55
require (
6+
github.com/dave/jennifer v1.4.0
7+
github.com/spf13/cobra v0.0.5
68
gorm.io/driver/mysql v1.4.4
79
gorm.io/gorm v1.24.2
10+
sigs.k8s.io/controller-tools v0.2.8
811
)
912

1013
require (
1114
github.com/go-sql-driver/mysql v1.6.0 // indirect
15+
github.com/inconshreveable/mousetrap v1.0.0 // indirect
1216
github.com/jinzhu/inflection v1.0.0 // indirect
1317
github.com/jinzhu/now v1.1.5 // indirect
18+
github.com/spf13/pflag v1.0.5 // indirect
19+
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72 // indirect
20+
gopkg.in/yaml.v2 v2.2.4 // indirect
21+
sigs.k8s.io/yaml v1.1.0 // indirect
1422
)

0 commit comments

Comments
 (0)