github.com/octohelm/storage@v0.0.0-20240516030302-1ac2cc1ea347/devpkg/enumgen/gen.go (about)

     1  package enumgen
     2  
     3  import (
     4  	"fmt"
     5  	"go/types"
     6  
     7  	"github.com/octohelm/gengo/pkg/gengo"
     8  )
     9  
    10  func init() {
    11  	gengo.Register(&enumGen{})
    12  }
    13  
    14  type enumGen struct {
    15  	enumTypes EnumTypes
    16  }
    17  
    18  func (*enumGen) Name() string {
    19  	return "enum"
    20  }
    21  
    22  func (*enumGen) New(ctx gengo.Context) gengo.Generator {
    23  	g := &enumGen{
    24  		enumTypes: EnumTypes{},
    25  	}
    26  	g.enumTypes.Walk(ctx, ctx.Package("").Pkg().Path())
    27  	return g
    28  }
    29  
    30  func (g *enumGen) GenerateType(c gengo.Context, named *types.Named) error {
    31  	if enum, ok := g.enumTypes.ResolveEnumType(named); ok {
    32  		if enum.IsIntStringer() {
    33  			g.genIntStringerEnums(c, named, enum)
    34  			return nil
    35  		}
    36  		g.genEnums(c, named, enum)
    37  	}
    38  	return nil
    39  }
    40  
    41  func (g *enumGen) genEnums(c gengo.Context, named *types.Named, enum *EnumType) {
    42  	options := make([]Option, len(enum.Constants))
    43  	tpeObj := named.Obj()
    44  
    45  	for i := range enum.Constants {
    46  		options[i].Name = enum.Constants[i].Name()
    47  		options[i].Value = fmt.Sprintf("%v", enum.Value(enum.Constants[i]))
    48  		options[i].Label = enum.Label(enum.Constants[i])
    49  	}
    50  
    51  	c.Render(gengo.Snippet{gengo.T: `
    52  var Invalid@Type = @errorsNew("invalid @Type")
    53  
    54  func (@Type) EnumValues() []any {
    55  	return []any{
    56  		@constValues
    57  	}
    58  }
    59  `,
    60  
    61  		"Type":      gengo.ID(tpeObj),
    62  		"errorsNew": gengo.ID("github.com/pkg/errors.New"),
    63  		"constValues": gengo.MapSnippet(options, func(o Option) gengo.Snippet {
    64  			return gengo.Snippet{
    65  				gengo.T:     "@ConstName,",
    66  				"ConstName": gengo.ID(o.Name),
    67  			}
    68  		}),
    69  	})
    70  
    71  	g.genLabel(c, tpeObj, enum, options)
    72  }
    73  
    74  type Option struct {
    75  	Name  string
    76  	Label string
    77  	Value any
    78  }
    79  
    80  func (g *enumGen) genLabel(c gengo.Context, typ *types.TypeName, enum *EnumType, options []Option) {
    81  	c.Render(gengo.Snippet{gengo.T: `
    82  func Parse@Type'LabelString(label string) (@Type, error) {
    83  	switch label {
    84  		@labelToConstCases
    85  		default:
    86  			return @ConstUnknown, Invalid@Type
    87  	}
    88  }
    89  
    90  func (v @Type) Label() string {
    91  	switch v {
    92  		@constToLabelCases
    93  		default:
    94  			return "UNKNOWN"
    95  	}
    96  }
    97  
    98  `,
    99  
   100  		"Type": gengo.ID(typ.Name()),
   101  		"ConstUnknown": func() gengo.Name {
   102  			if enum.ConstUnknown != nil {
   103  				return gengo.ID(enum.ConstUnknown)
   104  			}
   105  			return gengo.ID(`""`)
   106  		}(),
   107  		"labelToConstCases": gengo.MapSnippet(options, func(o Option) gengo.Snippet {
   108  			return gengo.Snippet{
   109  				gengo.T: `
   110  case @labelValue:
   111  	return @ConstName, nil
   112  `,
   113  				"labelValue": o.Label,
   114  				"ConstName":  gengo.ID(o.Name),
   115  			}
   116  		}),
   117  		"constToLabelCases": gengo.MapSnippet(options, func(o Option) gengo.Snippet {
   118  			return gengo.Snippet{
   119  				gengo.T: `
   120  case @ConstName:
   121  	return @labelValue
   122  `,
   123  				"labelValue": o.Label,
   124  				"ConstName":  gengo.ID(o.Name),
   125  			}
   126  		}),
   127  	})
   128  }
   129  
   130  func (g *enumGen) genIntStringerEnums(c gengo.Context, tpe types.Type, enum *EnumType) {
   131  	options := make([]Option, len(enum.Constants))
   132  	tpeObj := tpe.(*types.Named).Obj()
   133  
   134  	for i := range enum.Constants {
   135  		options[i].Name = enum.Constants[i].Name()
   136  		options[i].Value = fmt.Sprintf("%v", enum.Value(enum.Constants[i]))
   137  		options[i].Label = enum.Label(enum.Constants[i])
   138  	}
   139  
   140  	c.Render(gengo.Snippet{gengo.T: `
   141  var Invalid@Type = @errorsNew("invalid @Type")
   142  
   143  func (@Type) EnumValues() []any {
   144  	return []any{
   145  		@constValues
   146  	}
   147  }
   148  `,
   149  
   150  		"Type":      gengo.ID(tpeObj.Name()),
   151  		"errorsNew": gengo.ID("github.com/pkg/errors.New"),
   152  		"constValues": gengo.MapSnippet(options, func(o Option) gengo.Snippet {
   153  			return gengo.Snippet{
   154  				gengo.T:     "@ConstName,",
   155  				"ConstName": gengo.ID(o.Name),
   156  			}
   157  		}),
   158  	})
   159  
   160  	c.Render(gengo.Snippet{gengo.T: `
   161  func (v @Type) MarshalText() ([]byte, error) {
   162  	str := v.String()
   163  	if str == "UNKNOWN" {
   164  		return nil, Invalid@Type
   165  	}
   166  	return []byte(str), nil
   167  }
   168  
   169  func (v *@Type) UnmarshalText(data []byte) (error) {
   170  	vv, err := Parse@Type'FromString(string(@bytesToUpper(data)))
   171  	if err != nil {
   172  		return err
   173  	}
   174  	*v = vv
   175  	return nil
   176  }
   177  
   178  func Parse@Type'FromString(s string) (@Type, error) {
   179  	switch s {
   180  		@strValueToConstCases
   181  		default:
   182  			return @ConstUnknown, Invalid@Type
   183  	}
   184  }
   185  
   186  func (v @Type) String() string {
   187  	switch v {
   188  		@constToStrValueCases
   189  		default:
   190  			return "UNKNOWN"
   191  	}
   192  }
   193  
   194  `,
   195  
   196  		"Type":         gengo.ID(tpeObj.Name()),
   197  		"ConstUnknown": gengo.ID(enum.ConstUnknown),
   198  		"bytesToUpper": gengo.ID("bytes.ToUpper"),
   199  		"strValueToConstCases": gengo.MapSnippet(options, func(o Option) gengo.Snippet {
   200  			return gengo.Snippet{
   201  				gengo.T: `
   202  case @strValue:
   203  	return @ConstName, nil
   204  `,
   205  				"strValue":  o.Value,
   206  				"ConstName": gengo.ID(o.Name),
   207  			}
   208  		}),
   209  		"constToStrValueCases": gengo.MapSnippet(options, func(o Option) gengo.Snippet {
   210  			return gengo.Snippet{
   211  				gengo.T: `
   212  case @ConstName:
   213  	return @strValue
   214  `,
   215  				"strValue":  o.Value,
   216  				"ConstName": gengo.ID(o.Name),
   217  			}
   218  		}),
   219  	})
   220  
   221  	g.genLabel(c, tpeObj, enum, options)
   222  
   223  	c.Render(gengo.Snippet{gengo.T: `
   224  func (v @Type) Value() (@driverValue, error) {
   225  	offset := 0
   226  	if o, ok := any(v).(@enumerationDriverValueOffset); ok {
   227  		offset = o.Offset()
   228  	}
   229  	return int64(v) + int64(offset), nil
   230  }
   231  
   232  func (v *@Type) Scan(src any) error {
   233  	offset := 0
   234  	if o, ok := any(v).(@enumerationDriverValueOffset); ok {
   235  		offset = o.Offset()
   236  	}
   237  
   238  	i, err := @enumerationScanIntEnumStringer(src, offset)
   239  	if err != nil {
   240  		return err
   241  	}
   242  	*v = @Type(i)
   243  	return nil
   244  }
   245  
   246  `,
   247  		"Type":                           gengo.ID(tpeObj),
   248  		"driverValue":                    gengo.ID("database/sql/driver.Value"),
   249  		"enumerationScanIntEnumStringer": gengo.ID("github.com/octohelm/storage/pkg/enumeration.ScanIntEnumStringer"),
   250  		"enumerationDriverValueOffset":   gengo.ID("github.com/octohelm/storage/pkg/enumeration.DriverValueOffset"),
   251  	})
   252  }