github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/kit/enumgen/enum.go (about)

     1  package enumgen
     2  
     3  import (
     4  	"path/filepath"
     5  	"runtime"
     6  	"strings"
     7  
     8  	. "github.com/machinefi/w3bstream/pkg/depends/gen/codegen"
     9  	"github.com/machinefi/w3bstream/pkg/depends/x/misc/must"
    10  	"github.com/machinefi/w3bstream/pkg/depends/x/pkgx"
    11  	"github.com/machinefi/w3bstream/pkg/depends/x/stringsx"
    12  )
    13  
    14  type Enum struct {
    15  	Path    string
    16  	Name    string
    17  	Options Options
    18  }
    19  
    20  func NewEnum(name string, options Options) *Enum {
    21  	enum := &Enum{Options: options}
    22  	parts := strings.Split(name, ".")
    23  	switch len(parts) {
    24  	case 1:
    25  		enum.Path = ""
    26  		enum.Name = parts[0]
    27  	default:
    28  		enum.Path = strings.Join(parts[0:len(parts)-1], ".")
    29  		enum.Name = parts[len(parts)-1]
    30  	}
    31  	return enum
    32  }
    33  
    34  func (e Enum) ConstUnknown() Snippet {
    35  	return Ident(stringsx.UpperSnakeCase(e.Name) + "_UNKNOWN")
    36  }
    37  
    38  func (e Enum) ConstName(value string) Snippet {
    39  	return Ident(stringsx.UpperSnakeCase(e.Name) + "__" + value)
    40  }
    41  
    42  func (e Enum) VarInvalidError() Snippet { return Ident("Invalid" + e.Name) }
    43  
    44  func (e Enum) Receiver(name string) *SnippetField { return Var(Type(e.Name), name) }
    45  
    46  func (e Enum) StarReceiver(name string) *SnippetField { return Var(Star(Type(e.Name)), name) }
    47  
    48  func (e Enum) WriteToFile(f *File) {
    49  	f.WriteSnippet(
    50  		e.Errors(f),
    51  		e.StringParser(f),
    52  		e.LabelParser(f),
    53  		e.Integer(f),
    54  		e.Stringer(f),
    55  		e.Labeler(f),
    56  		e.TypeName(f),
    57  		e.ConstValues(f),
    58  		e.TextMarshaler(f),
    59  		e.TextUnmarshaler(f),
    60  		e.Scanner(f),
    61  		e.Valuer(f),
    62  	)
    63  }
    64  
    65  func (e Enum) Errors(f *File) Snippet {
    66  	return Exprer(
    67  		`var ? = ?("invalid ? type")`,
    68  		e.VarInvalidError(),
    69  		Ident(f.Use("errors", "New")),
    70  		Ident(e.Name),
    71  	)
    72  }
    73  
    74  func (e Enum) StringParser(f *File) Snippet {
    75  	clauses := []*SnippetCaseClause{
    76  		CaseClause().Do(Return(e.ConstUnknown(), e.VarInvalidError())),
    77  		CaseClause(f.Value("")).Do(Return(e.ConstUnknown(), Nil)),
    78  	}
    79  
    80  	for _, o := range e.Options {
    81  		clauses = append(
    82  			clauses,
    83  			CaseClause(f.Value(*o.Str)).Do(Return(e.ConstName(*o.Str), Nil)),
    84  		)
    85  	}
    86  
    87  	return Func(Var(String, "s")).
    88  		Named("Parse"+e.Name+"FromString").
    89  		Return(Var(Type(e.Name)), Var(Error)).
    90  		Do(Switch(Ident("s")).When(clauses...))
    91  }
    92  
    93  func (e Enum) LabelParser(f *File) Snippet {
    94  	clauses := []*SnippetCaseClause{
    95  		CaseClause().Do(Return(e.ConstUnknown(), e.VarInvalidError())),
    96  		CaseClause(f.Value("")).Do(Return(e.ConstUnknown(), Nil)),
    97  	}
    98  
    99  	for _, o := range e.Options {
   100  		clauses = append(
   101  			clauses,
   102  			CaseClause(f.Value(o.Label)).Do(Return(e.ConstName(*o.Str), Nil)),
   103  		)
   104  	}
   105  
   106  	return Func(Var(String, "s")).
   107  		Named("Parse"+e.Name+"FromLabel").
   108  		Return(Var(Type(e.Name)), Var(Error)).
   109  		Do(Switch(Ident("s")).When(clauses...))
   110  }
   111  
   112  func (e Enum) Stringer(f *File) Snippet {
   113  	clauses := []*SnippetCaseClause{
   114  		CaseClause().Do(Return(f.Value("UNKNOWN"))),
   115  		CaseClause(e.ConstUnknown()).Do(Return(f.Value(""))),
   116  	}
   117  
   118  	for _, o := range e.Options {
   119  		clauses = append(
   120  			clauses,
   121  			CaseClause(e.ConstName(*o.Str)).Do(Return(f.Value(*o.Str))),
   122  		)
   123  	}
   124  
   125  	return Func().
   126  		MethodOf(e.Receiver("v")).
   127  		Named("String").
   128  		Return(Var(String)).
   129  		Do(Switch(Ident("v")).When(clauses...))
   130  }
   131  
   132  func (e Enum) Integer(f *File) Snippet {
   133  	return Func().
   134  		MethodOf(e.Receiver("v")).
   135  		Named("Int").
   136  		Return(Var(Int)).
   137  		Do(Return(Casting(Int, Ident("v"))))
   138  }
   139  
   140  func (e Enum) Labeler(f *File) Snippet {
   141  	clauses := []*SnippetCaseClause{
   142  		CaseClause().Do(Return(f.Value("UNKNOWN"))),
   143  		CaseClause(e.ConstUnknown()).Do(Return(f.Value(""))),
   144  	}
   145  
   146  	for _, o := range e.Options {
   147  		clauses = append(
   148  			clauses,
   149  			CaseClause(e.ConstName(*o.Str)).Do(Return(f.Value(o.Label))),
   150  		)
   151  	}
   152  
   153  	return Func().
   154  		MethodOf(e.Receiver("v")).
   155  		Named("Label").
   156  		Return(Var(String)).
   157  		Do(Switch(Ident("v")).When(clauses...))
   158  }
   159  
   160  func (e Enum) TypeName(f *File) Snippet {
   161  	return Func().
   162  		MethodOf(e.Receiver("v")).
   163  		Named("TypeName").
   164  		Return(Var(String)).
   165  		Do(Return(f.Value(e.Path + "." + e.Name)))
   166  }
   167  
   168  func (e Enum) ConstValues(f *File) Snippet {
   169  	typ := Slice(Type(f.Use(PkgPath, IntStringerName)))
   170  	return Func().
   171  		MethodOf(e.Receiver("v")).
   172  		Named("ConstValues").
   173  		Return(Var(typ)).
   174  		Do(Return(func() Snippet {
   175  			lst := []interface{}{typ}
   176  			holder := "?"
   177  			for i, o := range e.Options {
   178  				if i > 0 {
   179  					holder += ", ?"
   180  				}
   181  				lst = append(lst, e.ConstName(*o.Str))
   182  			}
   183  			return f.Expr("?{"+holder+"}", lst...)
   184  		}()))
   185  }
   186  
   187  func (e Enum) TextMarshaler(f *File) Snippet {
   188  	return Func().
   189  		MethodOf(e.Receiver("v")).
   190  		Named("MarshalText").
   191  		Return(Var(Slice(Byte)), Var(Error)).
   192  		Do(
   193  			Define(Ident("s")).By(Ref(Ident("v"), Call("String"))),
   194  			If(f.Expr(`s == "UNKNOWN"`)).
   195  				Do(Return(Nil, e.VarInvalidError())),
   196  			Return(Casting(Slice(Byte), Ident("s")), Nil),
   197  		)
   198  
   199  }
   200  
   201  func (e Enum) TextUnmarshaler(f *File) Snippet {
   202  	return Func(Var(Slice(Byte), "data")).
   203  		MethodOf(e.StarReceiver("v")).
   204  		Named("UnmarshalText").
   205  		Return(Var(Error)).
   206  		Do(
   207  			Define(Ident("s")).
   208  				By(
   209  					Casting(String, Call(f.Use("bytes", "ToUpper"), Ident("data"))),
   210  				),
   211  			Define(Ident("val"), Ident("err")).
   212  				By(
   213  					CallWith(
   214  						f.Expr("Parse?FromString", Ident(e.Name)),
   215  						Ident("s"),
   216  					),
   217  				),
   218  			If(Exprer("err != nil")).Do(Return(Ident("err"))),
   219  			Assign(AccessValue(Ident("v"))).By(Ident("val")),
   220  			Return(Nil),
   221  		)
   222  }
   223  
   224  func (e Enum) Scanner(f *File) Snippet {
   225  	return Func(Var(Interface(), "src")).
   226  		MethodOf(e.StarReceiver("v")).
   227  		Named("Scan").
   228  		Return(Var(Error)).
   229  		Do(
   230  			Define(Ident("offset")).By(Valuer(0)),
   231  			Define(Ident("o"), Ident("ok")).
   232  				By(
   233  					Ref(
   234  						Casting(Interface(), Ident("v")),
   235  						Paren(Exprer(f.Use(PkgPath, ValueOffsetName))),
   236  					),
   237  				),
   238  			If(Ident("ok")).
   239  				Do(
   240  					Assign(Ident("offset")).
   241  						By(Ref(Ident("o"), Call("Offset"))),
   242  				),
   243  			Define(Ident("i"), Ident("err")).
   244  				By(
   245  					Call(
   246  						f.Use(PkgPath, ScanIntEnumStringerName),
   247  						Ident("src"), Ident("offset"),
   248  					),
   249  				),
   250  			If(Exprer("err != nil")).
   251  				Do(Return(Ident("err"))),
   252  			Assign(AccessValue(Ident("v"))).
   253  				By(
   254  					Casting(Type(e.Name), Ident("i")),
   255  				),
   256  			Return(Nil),
   257  		)
   258  }
   259  
   260  func (e Enum) Valuer(f *File) Snippet {
   261  	return Func().
   262  		MethodOf(e.Receiver("v")).
   263  		Named("Value").
   264  		Return(
   265  			Var(Type(f.Use("database/sql/driver", "Value"))),
   266  			Var(Error)).
   267  		Do(
   268  			Define(Ident("offset")).By(Valuer(0)),
   269  			Define(Ident("o"), Ident("ok")).
   270  				By(
   271  					Ref(
   272  						Casting(Interface(), Ident("v")),
   273  						Paren(Exprer(f.Use(PkgPath, ValueOffsetName))),
   274  					),
   275  				),
   276  			If(Ident("ok")).
   277  				Do(
   278  					Assign(Ident("offset")).
   279  						By(Ref(Ident("o"), Call("Offset"))),
   280  				),
   281  			Return(Exprer(`int64(v) + int64(offset)`), Nil),
   282  		)
   283  }
   284  
   285  var (
   286  	PkgPath                 string
   287  	IntStringerName         = "IntStringerEnum"
   288  	ValueOffsetName         = "ValueOffset"
   289  	ScanIntEnumStringerName = "ScanIntEnumStringer"
   290  )
   291  
   292  func init() {
   293  	_, current, _, _ := runtime.Caller(0)
   294  	dir := filepath.Join(filepath.Dir(current), "../enum")
   295  	PkgPath = must.String(pkgx.PkgIdByPath(dir))
   296  }