github.com/galaxyobe/gen@v0.0.0-20220910125335-392fa8f0990f/cmd/getter-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  
    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 Getter func.
    79  // will be ignored field enabled status when it's in +gen:getter: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 genGetter 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 NewGenGetter(build *parser.Builder, sanitizedName, targetPackage string, boundingDirs []string, types []*GenType, sourcePath string) generator.Generator {
   111  	return &genGetter{
   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 *genGetter) Name() string {
   125  	return "getter"
   126  }
   127  
   128  func (g *genGetter) Filter(c *generator.Context, t *types.Type) bool {
   129  	ok := g.types.allowed(t)
   130  	if !ok {
   131  		klog.V(5).Infof("Ignore generate getter function for type %v", t)
   132  	}
   133  	return ok
   134  }
   135  
   136  func (g *genGetter) 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 *genGetter) Init(c *generator.Context, w io.Writer) error {
   144  	return nil
   145  }
   146  
   147  func (g *genGetter) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
   148  	klog.V(5).Infof("Generating getter function for type %v", t)
   149  
   150  	sw := generator.NewSnippetWriter(w, c, "", "")
   151  	g.genGetFunc(sw, t)
   152  	sw.Do("\n", nil)
   153  
   154  	return sw.Error()
   155  }
   156  
   157  func (g *genGetter) isOtherPackage(pkg string) bool {
   158  	if pkg == g.targetPackage {
   159  		return false
   160  	}
   161  	if strings.HasSuffix(pkg, "\""+g.targetPackage+"\"") {
   162  		return false
   163  	}
   164  	return true
   165  }
   166  
   167  func (g *genGetter) Imports(c *generator.Context) (imports []string) {
   168  	var importLines []string
   169  	for _, singleImport := range g.imports.ImportLines() {
   170  		if g.isOtherPackage(singleImport) {
   171  			importLines = append(importLines, singleImport)
   172  		}
   173  	}
   174  	return importLines
   175  }
   176  
   177  func (g *genGetter) genGetFunc(sw *generator.SnippetWriter, t *types.Type) {
   178  	receiver := strings.ToLower(t.Name.Name[:1])
   179  	isExternalType := g.packageTypes.IsExternalType(t.Name.Package, t.Name.Name)
   180  	var methodSet = util.NewMethodSet()
   181  	var methodGen = util.NewMethodGenerate(util.GenName("Get", ""))
   182  
   183  	for idx, m := range t.Members {
   184  		if util.IsLower(m.Name) && isExternalType {
   185  			continue
   186  		}
   187  		methods := util.GetTagValues(tagMethodName, m.CommentLines)
   188  		methodSet.AddMethods("Get", methods, t.Members[idx])
   189  		if !g.types.allowedField(t, idx) {
   190  			continue
   191  		}
   192  		method := methodGen.GenName(m.Name)
   193  		if _, ok := t.Methods[method]; ok {
   194  			continue
   195  		}
   196  		args := generator.Args{
   197  			"type":     t,
   198  			"field":    m,
   199  			"receiver": receiver,
   200  			"method":   method,
   201  		}
   202  		sw.Do("func ({{.receiver}} *{{.type|public}}) {{.method}} () {{.field.Type|raw}} {\n", args)
   203  		sw.Do("return {{.receiver}}.{{.field.Name}}\n", args)
   204  		sw.Do("}\n\n", nil)
   205  	}
   206  	// add exist methods
   207  	methodGen.AddExistNames(func() []string {
   208  		var existMethods []string
   209  		for name := range t.Methods {
   210  			existMethods = append(existMethods, name)
   211  		}
   212  		return existMethods
   213  	}()...)
   214  	// gen aggregate method
   215  	for method, members := range methodSet {
   216  		if ok := methodGen.ExistName(method); ok {
   217  			klog.Fatalf("exist method: %s when generate aggregate method", method)
   218  		}
   219  		args := generator.Args{
   220  			"type":     t,
   221  			"receiver": receiver,
   222  			"method":   method,
   223  			"fields":   members,
   224  		}
   225  		sw.Do("func ({{.receiver}} *{{.type|public}}) {{.method}} () ({{ range $i, $field := .fields }} in{{$i}} {{$field.Type|raw}}, {{end}}) {\n", args)
   226  		sw.Do("{{ range $i, $field := .fields }} in{{$i}} = {{$.receiver}}.{{$field.Name}}\n {{ end }}", args)
   227  		sw.Do("return", nil)
   228  		sw.Do("}\n\n", nil)
   229  	}
   230  }