Last active
May 28, 2024 16:23
-
-
Save initialed85/a237dc0ef96a46957bb4a0b89002658c to your computer and use it in GitHub Desktop.
A thing that converts complex ogent structs to simple structs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package post_generate | |
import ( | |
"fmt" | |
"go/format" | |
"reflect" | |
"strings" | |
"time" | |
"github.com/google/uuid" | |
) | |
var unnamedCounter = 0 | |
type Stringer interface { | |
String() string | |
} | |
type Object struct { | |
Name string | |
Tag string | |
Kind reflect.Kind | |
Key reflect.Type | |
Value reflect.Type | |
IsPointer bool | |
Fields []*Object | |
Zero any | |
Parent *Object | |
handled bool | |
isBuiltin bool | |
} | |
func (o *Object) PrettyFormat(indents ...int) string { | |
indent := 0 | |
if len(indents) > 0 { | |
indent = indents[0] | |
} | |
getIndent := func() string { | |
s := "" | |
for i := 0; i < indent; i++ { | |
s += " " | |
} | |
return s | |
} | |
pointer := "" | |
if o.IsPointer { | |
pointer = "*" | |
} | |
output := "" | |
if o.isBuiltin { | |
output += fmt.Sprintf("%s%s", pointer, reflect.TypeOf(o.Zero).String()) | |
if o.Parent.Kind == reflect.Struct && o.Tag != "" { | |
output += fmt.Sprintf("`json:\"%v,omitempty\"`", o.Tag) | |
} | |
} else { | |
switch o.Kind { | |
case reflect.Struct: | |
if o.Parent == nil { | |
output += fmt.Sprintf("%stype %s ", getIndent(), o.Name) | |
} | |
output += fmt.Sprintf("%sstruct {\n", pointer) | |
for _, field := range o.Fields { | |
output += fmt.Sprintf("%s %s %s\n", getIndent(), field.Name, field.PrettyFormat(indent+1)) | |
} | |
output += fmt.Sprintf("%s}", getIndent()) | |
case reflect.Slice: | |
if o.Parent == nil { | |
output += fmt.Sprintf("%svar %s ", getIndent(), o.Name) | |
} | |
output += fmt.Sprintf("%s[]", pointer) | |
for _, field := range o.Fields { | |
output += fmt.Sprintf("%s %s\n", getIndent(), field.PrettyFormat(indent+1)) | |
} | |
case reflect.Map: | |
if o.Parent == nil { | |
output += fmt.Sprintf("%svar %s ", getIndent(), o.Name) | |
} | |
output += fmt.Sprintf("%smap[%s]", pointer, o.Key) | |
for _, field := range o.Fields { | |
output += fmt.Sprintf("%s %s\n", getIndent(), field.PrettyFormat(indent+1)) | |
} | |
default: | |
output += o.Value.Name() | |
if o.Parent.Kind == reflect.Struct && o.Tag != "" { | |
output += fmt.Sprintf("`json:\"%v,omitempty\"`", o.Tag) | |
} | |
} | |
} | |
if indent == 0 { | |
rawOutput, err := format.Source([]byte(output)) | |
if err != nil { | |
panic(err) | |
} | |
_ = rawOutput | |
_ = err | |
output = string(rawOutput) | |
} | |
return output | |
} | |
func introspectComplexType( | |
complexType any, | |
name string, | |
parent *Object, | |
) (*Object, error) { | |
typeOf := reflect.TypeOf(complexType) | |
if name == "" { | |
name = typeOf.Name() | |
} | |
if name == "" { | |
name = fmt.Sprintf("Unnamed%d", unnamedCounter) | |
unnamedCounter++ | |
} | |
object := &Object{ | |
Name: name, | |
Kind: typeOf.Kind(), | |
Value: typeOf, | |
Parent: parent, | |
} | |
var err error | |
switch typeOf.Kind() { | |
case reflect.Pointer: | |
var item *Object | |
item, err = introspectComplexType( | |
reflect.New(typeOf.Elem()).Elem().Interface(), | |
name, | |
object, | |
) | |
object.Name = item.Name | |
object.Tag = item.Tag | |
object.Kind = item.Kind | |
object.Key = item.Key | |
object.Value = item.Value | |
object.IsPointer = true | |
object.Fields = item.Fields | |
object.Zero = item.Zero | |
object.handled = object.Zero != nil | |
case reflect.Struct: | |
if strings.HasPrefix(object.Value.String(), "ogent.Opt") { | |
for i := 0; i < typeOf.NumField(); i++ { | |
if typeOf.Field(i).Name != "Value" { | |
continue | |
} | |
var field *Object | |
field, err = introspectComplexType( | |
reflect.New(typeOf.Field(i).Type).Elem().Interface(), | |
typeOf.Field(i).Name, | |
object, | |
) | |
if err != nil { | |
return nil, err | |
} | |
object.Tag = field.Tag | |
object.Kind = field.Kind | |
object.Key = field.Key | |
object.Value = field.Value | |
object.IsPointer = true | |
object.Fields = field.Fields | |
object.Zero = field.Zero | |
object.handled = object.Zero != nil | |
} | |
break | |
} | |
object.Fields = make([]*Object, 0) | |
for i := 0; i < typeOf.NumField(); i++ { | |
var field *Object | |
field, err = introspectComplexType( | |
reflect.New(typeOf.Field(i).Type).Elem().Interface(), | |
typeOf.Field(i).Name, | |
object, | |
) | |
if err != nil { | |
return nil, err | |
} | |
object.Fields = append(object.Fields, field) | |
field.Tag = typeOf.Field(i).Tag.Get("json") | |
} | |
structFields := make([]reflect.StructField, 0) | |
for _, field := range object.Fields { | |
structField := reflect.StructField{ | |
Name: field.Name, | |
PkgPath: "bitbucket.org/wevoltaustralia-admin/ocpp-server/pkg/ent/ogent_simple", | |
Type: field.Value, | |
} | |
if field.Tag != "" { | |
structField.Tag = reflect.StructTag(fmt.Sprintf(`json:"%v,omitempty"`, field.Tag)) | |
} | |
structFields = append(structFields, structField) | |
} | |
object.Zero = reflect.New(reflect.StructOf(structFields)).Elem().Interface() | |
object.handled = true | |
case reflect.Array, reflect.Slice: | |
var item *Object | |
item, err = introspectComplexType( | |
reflect.New(typeOf.Elem()).Elem().Interface(), | |
name, | |
object, | |
) | |
object.Tag = item.Tag | |
object.Value = item.Value | |
object.Fields = append(object.Fields, item) | |
object.Zero = reflect.New(reflect.SliceOf(reflect.TypeOf(item.Zero))).Elem().Interface() | |
object.handled = item.Zero != nil | |
case reflect.Map: | |
var item *Object | |
item, err = introspectComplexType( | |
reflect.New(typeOf.Elem()).Elem().Interface(), | |
name, | |
object, | |
) | |
object.Tag = item.Tag | |
object.Key = typeOf.Key() | |
object.Value = item.Value | |
object.Fields = append(object.Fields, item) | |
object.Zero = reflect.New(reflect.MapOf(object.Key, reflect.TypeOf(item.Zero))).Elem().Interface() | |
object.handled = item.Zero != nil | |
case reflect.Bool, | |
reflect.Int, | |
reflect.Int8, | |
reflect.Int16, | |
reflect.Int32, | |
reflect.Int64, | |
reflect.Uint, | |
reflect.Uint8, | |
reflect.Uint16, | |
reflect.Uint32, | |
reflect.Uint64, | |
reflect.Uintptr, | |
reflect.Float32, | |
reflect.Float64, | |
reflect.Complex64, | |
reflect.Complex128, | |
reflect.String: | |
object.Zero = reflect.New(object.Value).Elem().Interface() | |
object.handled = true | |
case reflect.Func, reflect.Interface, reflect.Chan, reflect.UnsafePointer: | |
err = fmt.Errorf("unsupported complexType %#+v", complexType) | |
} | |
if err != nil { | |
return nil, err | |
} | |
switch reflect.New(object.Value).Elem().Interface().(type) { | |
case time.Time, time.Location, time.Duration, uuid.UUID: | |
object.Fields = nil | |
object.Zero = reflect.New(object.Value).Elem().Interface() | |
object.handled = true | |
object.isBuiltin = true | |
} | |
return object, nil | |
} | |
func GetSimpleType(complexType any, name string) (any, string, error) { | |
object, err := introspectComplexType(complexType, name, nil) | |
if err != nil { | |
return nil, "", err | |
} | |
return object.Zero, object.PrettyFormat(), nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment