github.com/galaxyobe/gen@v0.0.0-20220910125335-392fa8f0990f/cmd/setter-gen/generators/generator.go (about) 1 /* 2 Copyright 2022 Galaxyobe. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package generators 18 19 import ( 20 "io" 21 "strings" 22 23 "k8s.io/gengo/generator" 24 "k8s.io/gengo/namer" 25 "k8s.io/gengo/types" 26 "k8s.io/klog/v2" 27 28 "github.com/galaxyobe/gen/pkg/util" 29 tpgenerator "github.com/galaxyobe/gen/third_party/gengo/generator" 30 "github.com/galaxyobe/gen/third_party/gengo/parser" 31 ) 32 33 type GenType struct { 34 *types.Type 35 AllowFields []string 36 } 37 38 func NewGenTypes(pkg *types.Package) (pkgEnabled bool, list GenTypes) { 39 pkgAllowed := util.CheckTag(tagPackageName, pkg.Comments, util.Package) 40 for _, t := range pkg.Types { 41 ut := util.UnderlyingType(t) 42 if ut.Kind != types.Struct { 43 continue 44 } 45 comments := t.CommentLines 46 set, enabled := util.GetTagBoolStatus(tagPackageName, comments) 47 allowedFields := util.GetTagValues(tagSelectFieldsName, comments) 48 if len(allowedFields) > 0 { 49 set = true 50 enabled = true 51 } 52 if (!pkgAllowed && !set) || !enabled { 53 continue // ignore type 54 } 55 pkgEnabled = true 56 list = append(list, &GenType{ 57 Type: t, 58 AllowFields: allowedFields, 59 }) 60 } 61 if len(list) == 0 { 62 pkgEnabled = false 63 } 64 return 65 } 66 67 type GenTypes []*GenType 68 69 func (list GenTypes) allowed(t *types.Type) bool { 70 for _, item := range list { 71 if item.Name.Name == t.Name.Name && item.Name.Package == t.Name.Package { 72 return true 73 } 74 } 75 return false 76 } 77 78 // allowedField allowed type's field to generate Setter func. 79 // will be ignored field enabled status when it's in +gen:setter:fields allowed fields. 80 func (list GenTypes) allowedField(t *types.Type, m int) bool { 81 for _, item := range list { 82 if item.Name.Name == t.Name.Name && item.Name.Package == t.Name.Package { 83 if len(t.Members) == 0 { 84 return true 85 } 86 field := t.Members[m] 87 set, enable := util.GetTagBoolStatus(tagFieldName, field.CommentLines) 88 if set && !enable && len(item.AllowFields) == 0 { 89 return false 90 } 91 if len(item.AllowFields) == 0 { 92 return true 93 } 94 return util.Exist(item.AllowFields, field.Name) 95 } 96 } 97 return false 98 } 99 100 type genSetter struct { 101 generator.DefaultGen 102 build *parser.Builder 103 targetPackage string 104 boundingDirs []string 105 imports namer.ImportTracker 106 types GenTypes 107 packageTypes util.PackageTypes 108 } 109 110 func NewGenSetter(build *parser.Builder, sanitizedName, targetPackage string, boundingDirs []string, types []*GenType, sourcePath string) generator.Generator { 111 return &genSetter{ 112 DefaultGen: generator.DefaultGen{ 113 OptionalName: sanitizedName, 114 }, 115 build: build, 116 targetPackage: targetPackage, 117 boundingDirs: boundingDirs, 118 imports: generator.NewImportTracker(), 119 types: types, 120 packageTypes: util.NewPackageTypes(build), 121 } 122 } 123 124 func (g *genSetter) Name() string { 125 return "setter" 126 } 127 128 func (g *genSetter) Filter(c *generator.Context, t *types.Type) bool { 129 ok := g.types.allowed(t) 130 if !ok { 131 klog.V(5).Infof("Ignore generate setter function for type %v", t) 132 } 133 return ok 134 } 135 136 func (g *genSetter) Namers(c *generator.Context) namer.NameSystems { 137 // Have the raw namer for this file track what it imports. 138 return namer.NameSystems{ 139 "raw": namer.NewRawNamer(g.targetPackage, g.imports), 140 } 141 } 142 143 func (g *genSetter) Init(c *generator.Context, w io.Writer) error { 144 return nil 145 } 146 147 func (g *genSetter) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { 148 klog.V(5).Infof("Generating setter function for type %v", t) 149 150 sw := tpgenerator.NewSnippetWriter(w, c, "", "") 151 sw.AddFunc("slice", func(s string) string { 152 if strings.HasPrefix(s, "[]") { 153 return strings.ReplaceAll(s, "[]", "...") 154 } 155 return s 156 }) 157 g.genSetFunc(sw, t) 158 sw.Do("\n", nil) 159 160 return sw.Error() 161 } 162 163 func (g *genSetter) isOtherPackage(pkg string) bool { 164 if pkg == g.targetPackage { 165 return false 166 } 167 if strings.HasSuffix(pkg, "\""+g.targetPackage+"\"") { 168 return false 169 } 170 return true 171 } 172 173 func (g *genSetter) Imports(c *generator.Context) (imports []string) { 174 var importLines []string 175 for _, singleImport := range g.imports.ImportLines() { 176 if g.isOtherPackage(singleImport) { 177 importLines = append(importLines, singleImport) 178 } 179 } 180 return importLines 181 } 182 183 func (g *genSetter) genSetFunc(sw *tpgenerator.SnippetWriter, t *types.Type) { 184 receiver := strings.ToLower(t.Name.Name[:1]) 185 isExternalType := g.packageTypes.IsExternalType(t.Name.Package, t.Name.Name) 186 var methodSet = util.NewMethodSet() 187 var methodGen = util.NewMethodGenerate(util.GenName("Set", "")) 188 189 for idx, m := range t.Members { 190 if util.IsLower(m.Name) && isExternalType { 191 continue 192 } 193 methods := util.GetTagValues(tagMethodName, m.CommentLines) 194 methodSet.AddMethods("Set", methods, t.Members[idx]) 195 if !g.types.allowedField(t, idx) { 196 continue 197 } 198 method := methodGen.GenName(m.Name) 199 if _, ok := t.Methods[method]; ok { 200 continue 201 } 202 args := generator.Args{ 203 "type": t, 204 "field": m, 205 "receiver": receiver, 206 "method": method, 207 } 208 sw.Do("func ({{.receiver}} *{{.type|public}}) {{.method}}(val {{.field.Type|raw|slice}}) *{{.type|public}} {\n", args) 209 sw.Do("{{.receiver}}.{{.field.Name}} = val\n", args) 210 sw.Do("return {{.receiver}}", args) 211 sw.Do("}\n\n", nil) 212 } 213 // add exist methods 214 methodGen.AddExistNames(func() []string { 215 var existMethods []string 216 for name := range t.Methods { 217 existMethods = append(existMethods, name) 218 } 219 return existMethods 220 }()...) 221 // gen aggregate method 222 for method, members := range methodSet { 223 if ok := methodGen.ExistName(method); ok { 224 klog.Fatalf("exist method: %s when generate aggregate method", method) 225 } 226 args := generator.Args{ 227 "type": t, 228 "receiver": receiver, 229 "method": method, 230 "fields": members, 231 } 232 sw.Do("func ({{.receiver}} *{{.type|public}}) {{.method}}({{ range $i, $field := .fields }} in{{$i}} {{$field.Type|raw}}, {{end}}) *{{.type|public}} {\n", args) 233 sw.Do("{{ range $i, $field := .fields }} {{$.receiver}}.{{$field.Name}} = in{{$i}} \n{{ end }}", args) 234 sw.Do("return {{.receiver}}", args) 235 sw.Do("}\n\n", nil) 236 } 237 }