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 }