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  }