2

I know this may seem like bad design (and I wish I didn't need to do this), but I need to read a struct that is generated automatically at run time and create a new instance of it.

The struct that I create I only need limited metadata of so that it can be passed to the Gorm Gen FieldRelateModel method (from here) where 'relModel' is the new instance I am hoping to make (see below):

    FieldRelateModel = func(relationship field.RelationshipType, fieldName string, relModel interface{}, config *field.RelateConfig) model.CreateFieldOpt {
        st := reflect.TypeOf(relModel)
        if st.Kind() == reflect.Ptr {
            st = st.Elem()
        }
        fieldType := st.String()

        if config == nil {
            config = &field.RelateConfig{}
        }
        if config.JSONTag == "" {
            config.JSONTag = ns.ColumnName("", fieldName)
        }

        return func(*model.Field) *model.Field {
            return &model.Field{
                Name:         fieldName,
                Type:         config.RelateFieldPrefix(relationship) + fieldType,
                JSONTag:      config.JSONTag,
                GORMTag:      config.GORMTag,
                NewTag:       config.NewTag,
                OverwriteTag: config.OverwriteTag,

                Relation: field.NewRelationWithModel(relationship, fieldName, fieldType, relModel),
            }
        }
    }

I am able to parse the struct and get its metadata using ast and reflect (see below), but I don't know how I can do the last step so that the 'structInstance' I return is valid when passed as the 'relModel' to the 'FieldRelateModel' function/option.

func StructExtract(filePath string) (any, error) {
    file, err := os.Open(filePath)
    if err != nil {
        fmt.Println(err)
        return nil, err
    }
    defer file.Close()

    fset := token.NewFileSet()
    f, err := parser.ParseFile(fset, filePath, file, parser.AllErrors)
    if err != nil {
        fmt.Println(err)
        return nil, err
    }

    var structType *ast.StructType

    // Iterate over the top-level declarations in the file
    for _, decl := range f.Decls {
        // Check if the declaration is a GenDecl (which can contain structs)
        genDecl, ok := decl.(*ast.GenDecl)
        if !ok {
            continue
        }

        // Iterate over the GenDecl's specs
        for _, spec := range genDecl.Specs {
            // Check if the spec is a TypeSpec (which can contain structs)
            typeSpec, ok := spec.(*ast.TypeSpec)
            if !ok {
                continue
            }

            // Check if the TypeSpec's type is a struct
            structType, ok = typeSpec.Type.(*ast.StructType)
            if !ok {
                continue
            }

            break
        }
    }

    if structType == nil {
        fmt.Println("No struct found in file.")
        return nil, err
    }

    structInstance := reflect.New(reflect.TypeOf(structType)).Elem().Interface()

    return structInstance, nil
}

Here is a go playground that I thought might make it easier if someone were to help, however, due to the nature of the problem it won't be able to run within the playground (for various reasons).

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Charlie Clarke
  • 177
  • 1
  • 9
  • 2
    This is mostly out of my depth, but I'll share what I know. Create [struct fields](https://pkg.go.dev/reflect#StructField) from the type definitions. Create the instance with `reflect.New(reflect.StructOf(fields)).Elem().Interface()`. – Penny Stevens Jan 25 '23 at 02:35

0 Answers0