Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save krancour/1926d3b63b99142adc82 to your computer and use it in GitHub Desktop.

Select an option

Save krancour/1926d3b63b99142adc82 to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"log"
"reflect"
"strconv"
)
const (
prefix = "router.deis.io/"
tag = "router"
)
type Foo struct {
A string `router:"a"`
B int `router:"b"`
C *Bar `router:"c"`
}
type Bar struct {
D string `router:"d"`
E bool `router:"e"`
}
func main() {
data := make(map[string]string)
data[prefix+"a"] = "ABBA"
data[prefix+"b"] = "5"
data[prefix+"c.d"] = "DDD"
data[prefix+"c.e"] = "true"
foo := &Foo{C: &Bar{}}
err := FillStruct(data, foo)
if err != nil {
log.Fatal(err)
}
fmt.Println(foo.A)
fmt.Println(foo.B)
fmt.Println(foo.C.D)
fmt.Println(foo.C.E)
}
func FillStruct(data map[string]string, out interface{}) error {
rv := reflect.ValueOf(out)
return fillStruct(data, "", rv)
}
func fillStruct(data map[string]string, context string, rv reflect.Value) error {
// If rv is invalid (represents a nil litersl), we cannot proceed.
if rv.Kind() == reflect.Invalid {
return fmt.Errorf("Cannot populate a nil from the map.")
}
// We also cannot proceed if rv is not a pointer.
if rv.Kind() != reflect.Ptr {
return fmt.Errorf("Cannot populate non-pointer type %s from the map.", rv.Type())
}
// Or if it is a pointer to a nil.
if rv.IsNil() {
return fmt.Errorf("Cannot populate nil %s from the map.", rv.Type())
}
// At this point, we know we're working with a pointer that points to something.
// Get what it points to...
elem := rv.Elem()
// If the thing it points to isn't a struct, that's also a problem.
if elem.Kind() != reflect.Struct {
return fmt.Errorf("Cannot populate non-struct-pointer type %s from the map.", rv.Type())
}
rt := elem.Type()
for i := 0; i < rt.NumField(); i++ {
rf := rt.Field(i)
tagValue := rf.Tag.Get(tag)
// If no tag value is found...
if tagValue == "" {
// Just move to the next field.
continue
}
if rf.Type.Kind() == reflect.Ptr || rf.Type.Kind() == reflect.Struct {
// We're nested... use some recursion...
var nestedContext string
if context == "" {
nestedContext = tagValue
} else {
nestedContext = fmt.Sprintf("%s.%s", context, tagValue)
}
err := fillStruct(data, nestedContext, elem.Field(i))
if err != nil {
return err
}
} else {
// We're not nested!
var key string
if context == "" {
key = fmt.Sprintf("%s%s", prefix, tagValue)
} else {
key = fmt.Sprintf("%s%s.%s", prefix, context, tagValue)
}
stringVal, ok := data[key]
if ok {
if rf.Type.Name() == "string" {
elem.Field(i).Set(reflect.ValueOf(stringVal))
} else if rf.Type.Name() == "int" {
intVal, err := strconv.Atoi(stringVal)
if err != nil {
return err
}
elem.Field(i).Set(reflect.ValueOf(intVal))
} else if rf.Type.Name() == "bool" {
boolVal, err := strconv.ParseBool(stringVal)
if err != nil {
return err
}
elem.Field(i).Set(reflect.ValueOf(boolVal))
}
}
}
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment