Skip to content

Instantly share code, notes, and snippets.

@initialed85
Last active May 28, 2024 16:23
Show Gist options
  • Save initialed85/a237dc0ef96a46957bb4a0b89002658c to your computer and use it in GitHub Desktop.
Save initialed85/a237dc0ef96a46957bb4a0b89002658c to your computer and use it in GitHub Desktop.
A thing that converts complex ogent structs to simple structs
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