github.com/galaxyobe/gen@v0.0.0-20220910125335-392fa8f0990f/cmd/option-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 31 "github.com/galaxyobe/gen/third_party/gengo/parser" 32 ) 33 34 type GenType struct { 35 *types.Type 36 AllowFields []string 37 OptionName string 38 OptionSuffix string 39 } 40 41 func NewGenTypes(pkg *types.Package) (pkgEnabled bool, genTypes GenTypes) { 42 for _, t := range pkg.Types { 43 ut := util.UnderlyingType(t) 44 switch ut.Kind { 45 case types.Struct: 46 case types.Func: 47 genTypes.Functions = append(genTypes.Functions, ut.Name.Name) 48 default: 49 continue 50 } 51 comments := t.CommentLines 52 setTag, enabled := util.GetTagBoolStatus(tagName, comments) 53 setName, name := util.GetTagValueStatus(tagTypeName, comments) 54 setSuffix, suffix := util.GetTagValueStatus(tagSuffixName, comments) 55 allowedFields := util.GetTagValues(tagSelectFieldsName, comments) 56 if len(allowedFields) > 0 || setName || setSuffix { 57 setTag = true 58 enabled = true 59 } 60 if !setTag || !enabled { 61 continue // ignore type 62 } 63 optionName := getOptionName(t.Name.Name) 64 if !setName { 65 name = optionName 66 } 67 if !setSuffix { 68 suffix = name 69 } 70 pkgEnabled = true 71 genTypes.Types = append(genTypes.Types, &GenType{ 72 Type: t, 73 AllowFields: allowedFields, 74 OptionName: name, 75 OptionSuffix: suffix, 76 }) 77 } 78 if len(genTypes.Types) == 0 { 79 pkgEnabled = false 80 return 81 } 82 for name := range pkg.Functions { 83 genTypes.Functions = append(genTypes.Functions, name) 84 } 85 return 86 } 87 88 type GenTypes struct { 89 Types []*GenType 90 Functions []string 91 } 92 93 func (g GenTypes) find(t *types.Type) *GenType { 94 for _, item := range g.Types { 95 if item.Name.Name == t.Name.Name && item.Name.Package == t.Name.Package { 96 return item 97 } 98 } 99 return nil 100 } 101 102 func (g GenTypes) allowed(t *types.Type) bool { 103 return g.find(t) != nil 104 } 105 106 // allowedField allowed type's field to generate Option func. 107 // will be ignored field enabled status when it's in +gen:option:fields allowed fields. 108 func (g GenTypes) allowedField(t *types.Type, m int) bool { 109 for _, item := range g.Types { 110 if item.Name.Name == t.Name.Name && item.Name.Package == t.Name.Package { 111 if len(t.Members) == 0 { 112 return true 113 } 114 field := t.Members[m] 115 set, enable := util.GetTagBoolStatus(tagFieldName, field.CommentLines) 116 if set && !enable && len(item.AllowFields) == 0 { 117 return false 118 } 119 if len(item.AllowFields) == 0 { 120 return true 121 } 122 return util.Exist(item.AllowFields, field.Name) 123 } 124 } 125 return false 126 } 127 128 func (g GenTypes) existMethod(funcName string) bool { 129 for _, item := range g.Functions { 130 if item == funcName { 131 return true 132 } 133 } 134 return false 135 } 136 137 type genOption struct { 138 generator.DefaultGen 139 build *parser.Builder 140 targetPackage string 141 boundingDirs []string 142 imports namer.ImportTracker 143 genTypes GenTypes 144 packageTypes util.PackageTypes 145 } 146 147 func NewGenOption(build *parser.Builder, sanitizedName, targetPackage string, boundingDirs []string, genTypes GenTypes, sourcePath string) generator.Generator { 148 return &genOption{ 149 DefaultGen: generator.DefaultGen{ 150 OptionalName: sanitizedName, 151 }, 152 build: build, 153 targetPackage: targetPackage, 154 boundingDirs: boundingDirs, 155 imports: generator.NewImportTracker(), 156 genTypes: genTypes, 157 packageTypes: util.NewPackageTypes(build), 158 } 159 } 160 161 func (g *genOption) Name() string { 162 return "option" 163 } 164 165 func (g *genOption) Filter(c *generator.Context, t *types.Type) bool { 166 ok := g.genTypes.allowed(t) 167 if !ok { 168 klog.V(5).Infof("Ignore generate option function for type %v", t) 169 } 170 return ok 171 } 172 173 func (g *genOption) Namers(c *generator.Context) namer.NameSystems { 174 // Have the raw namer for this file track what it imports. 175 return namer.NameSystems{ 176 "raw": namer.NewRawNamer(g.targetPackage, g.imports), 177 } 178 } 179 180 func (g *genOption) Init(c *generator.Context, w io.Writer) error { 181 return nil 182 } 183 184 func (g *genOption) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { 185 klog.V(5).Infof("Generating option function for type %v", t) 186 sw := tpgenerator.NewSnippetWriter(w, c, "", "") 187 sw.AddFunc("slice", func(s string) string { 188 if strings.HasPrefix(s, "[]") { 189 return strings.ReplaceAll(s, "[]", "...") 190 } 191 return s 192 }) 193 g.genOptionType(sw, t) 194 g.genWithFieldFunc(sw, t) 195 sw.Do("\n", nil) 196 197 return sw.Error() 198 } 199 200 func (g *genOption) isOtherPackage(pkg string) bool { 201 if pkg == g.targetPackage { 202 return false 203 } 204 if strings.HasSuffix(pkg, "\""+g.targetPackage+"\"") { 205 return false 206 } 207 return true 208 } 209 210 func (g *genOption) Imports(c *generator.Context) (imports []string) { 211 var importLines []string 212 for _, singleImport := range g.imports.ImportLines() { 213 if g.isOtherPackage(singleImport) { 214 importLines = append(importLines, singleImport) 215 } 216 } 217 return importLines 218 } 219 220 func getOptionName(typeName string) string { 221 if strings.HasSuffix(typeName, "Option") { 222 return typeName 223 } 224 return typeName + "Option" 225 } 226 227 func (g *genOption) getOptionSuffixName(t *types.Type) string { 228 suffix := getOptionName(t.Name.Name) 229 if v := g.genTypes.find(t); v != nil { 230 suffix = v.OptionSuffix 231 } 232 return suffix 233 } 234 235 func (g *genOption) getOptionTypeName(t *types.Type) string { 236 name := getOptionName(t.Name.Name) 237 if v := g.genTypes.find(t); v != nil { 238 name = v.OptionName 239 } 240 return name 241 } 242 243 func (g *genOption) genOptionType(sw *tpgenerator.SnippetWriter, t *types.Type) { 244 name := g.getOptionTypeName(t) 245 if g.genTypes.existMethod(name) { 246 return 247 } 248 args := generator.Args{ 249 "type": t, 250 "name": name, 251 } 252 sw.Do("type {{.name}} func(* {{.type|public}})\n", args) 253 } 254 255 func (g *genOption) genWithFieldFunc(sw *tpgenerator.SnippetWriter, t *types.Type) { 256 isExternalType := g.packageTypes.IsExternalType(t.Name.Package, t.Name.Name) 257 option := g.getOptionTypeName(t) 258 suffix := g.getOptionSuffixName(t) 259 methodGen := util.NewMethodGenerate(util.GenName("With", suffix)) 260 for idx, m := range t.Members { 261 if util.IsLower(m.Name) && isExternalType { 262 continue 263 } 264 if !g.genTypes.allowedField(t, idx) { 265 continue 266 } 267 method := methodGen.GenName(m.Name) 268 if g.genTypes.existMethod(method) { 269 continue 270 } 271 args := generator.Args{ 272 "type": t, 273 "option": option, 274 "field": m, 275 "method": method, 276 } 277 sw.Do("func {{.method}} (val {{.field.Type|raw|slice}}) {{.option}} {\n", args) 278 sw.Do("return func(object * {{.type|public}}) {\n", args) 279 sw.Do("object.{{.field.Name}} = val\n", args) 280 sw.Do("}\n", nil) 281 sw.Do("}\n\n", nil) 282 } 283 }