0
votes

Let's say I have a base struct that's a composite of two others:

type Config struct {
    common.Config
    common.ServerConfig
    APIPath      string `type:"string" name:"apipath" default:"localhost:8443" desc:"Path to the gRPC API server"`
    ServePath    string `type:"string" name:"servepath" default:"/" desc:"Path to serve the API from"`
    Organization string `type:"string" name:"organization" default:"" desc:"Default organization name"`
}

I'd like to iterate through every field in the struct and pull values into the struct:

func NewConfig(c Configer) {
    setConfigFlags(reflect.TypeOf(c).Elem(), c.GetViper())
}

So I'd call it so:

conf := Config{}
common.NewConfig(&conf)

My setConfigFlags is doing some recursion:

func setConfigFlags(t reflect.Type, viper *viper.Viper) {
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        switch field.Type.Kind() {
        case reflect.Struct:
            setConfigFlags(field.Type, viper)
            continue
        case reflect.String:
            fieldValue := reflect.ValueOf(&field).Elem()
            if !fieldValue.IsValid() || !fieldValue.CanSet() {
                return
            }
            fieldValue.SetString(
                viper.GetString(field.Tag.Get("name")),
            )
        }
    }
}

Now once I hit the SetString line towards the bottom I'm getting this error:

panic: reflect: call of reflect.Value.SetString on struct Value

When I look at the type of fieldValue at that point it's a structfield. I'm having trouble trying to address the value and change the value of the field. I believe I'm not grabbing the address of the actual struct field properly. The examples and documentation surely isn't helping.

In this particular example I'm pulling viper values. Basically looking for a way to define my viper definitions via struct tags, that's the ultimately goal.

1
reflect.TypeOf(c).Elem()... You're passing in the type of c not its value, you won't be able to set anything to a type. reflect.Type as the argument to your set config func is the first problem, it should be reflect.Value and it should be addressable. - mkopriva
This is so confusing lol - ddibiase
Apologies. You want to populate the fields of the ‘c’ value right? Well that is not possible through its type. That means that ‘reflects.Type’ is the wrong choice for the argument type to your setconfigflags function. Is that a bit more clear? - mkopriva
Yes, it was totally clear. I'm trying to re-adapt my example now to use reflect.Value as the param. I'm on my way :p - ddibiase

1 Answers

3
votes

As comments by mkopriva have noted, the code is attempting to set a value on a type. Use a value.

func setConfigFlags(v reflect.Value, viper *viper.Viper) {
    t := v.Type()
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        switch field.Type.Kind() {
        case reflect.Struct:
            setConfigFlags(v.Field(i), viper)
            continue
        case reflect.String:
            v.Field(i).SetString(viper.GetString(field.Tag.Get("name")))
        }
    }
}

Call it like this:

func NewConfig(c Configer) {
    setConfigFlags(reflect.Value(c).Elem(), c.GetViper())
}