Community,
The mission
basic
Implement a func
that patches all string
fields on an objects
details
- [done] fields shall only be patched if they match a
matcher
func
- [done] value shall be processed via
process
func
- patching shall be done recursive
- it shall also work for
[]string
,[]*string
and recursive forstructs
and[]struct
,[]*struct
whats working
process string
s on structs
, if the object
is passed via pointer
progress
see code below.
but this seems way too complicated
package main
import (
"encoding/json"
"fmt"
"pmi.at/log"
"pmi.at/str"
"reflect"
"strings"
)
type SOTag struct {
Name string `process:"yes"`
NamePtr *string `process:"yes"`
}
type SOUser struct {
ID int
Nick string
Name string `process:"yes"`
NamePtr *string `process:"yes"`
Slice []string `process:"yes"`
SlicePtr []*string `process:"yes"`
SubStruct []SOTag `process:"yes"`
SubStructPtr []*SOTag `process:"yes"`
}
func main() {
fmt.Println("try to update string values of a struct via reflection")
var NamePtr string = "golang"
testUser := SOUser{
ID: 1,
Name: "lumo",
NamePtr: &NamePtr,
SubStruct: []SOTag{SOTag{
Name: "go",
},
},
SubStructPtr: make([]*SOTag, 0),
}
err := patch( // struct to patch
&testUser,
// filter - true -> all fields that are strings
func(idx int, v reflect.Value) bool {
prefix := "-"
matches := hasTag(v.Type().Field(idx), "process", "yes")
if matches {
prefix = "+"
}
fmt.Printf("MATCHER: [%s] %v k:%v t:%v - tag `process:'yes'`\n", prefix, v.Type().Field(idx).Name, v.Field(idx).Kind(), v.Field(idx).Type())
return matches
},
// matcher
func(in string) string {
return fmt.Sprintf("!%s!", strings.ToUpper(in))
})
if err != nil {
panic(err)
}
fmt.Println("Output:")
fmt.Println(Stringify(testUser))
}
// patch shall accept i and &i and patch all fields (or *field) recursive via matcher...
func patch(i interface{}, matches func(idx int, v reflect.Value) bool, process func(s string) string) error {
v := reflect.ValueOf(i)
// if its a pointer, resolve its value
if v.Kind() == reflect.Ptr {
//v = reflect.Indirect(v)
v = v.Elem()
}
// should double-check we now have a struct (could still be anything)
if v.Kind() != reflect.Struct {
return fmt.Errorf("unexpected type: %v", v.Kind())
}
for idx := 0; idx < v.NumField(); idx++ {
f := v.Field(idx)
if matches(idx, v) {
n := v.Type().Field(idx).Name
t := f.Type().Name()
switch f.Kind() {
case reflect.String:
fmt.Println("KIND String -> Done?")
value := f.Interface().(string)
fmt.Printf(" [READ] Name: %s Kind: %s Type: %s Value: %v\n", n, f.Kind(), t, value)
processedValue := process(f.Interface().(string))
fmt.Printf(" [PATCH] Name: %s Kind: %s Type: %s Value: %v\n", n, f.Kind(), t, processedValue)
//fmt.Printf(" write the value back - but how??: %v", processedValue)
setValue(i, n, processedValue)
fmt.Printf(" [WROTE] %v\n", f.Interface())
//case reflect.Struct:
// fmt.Println("CALL Struct -> REFLECT RECURSIVE")
//case reflect.Array: ?? equal slice?
case reflect.Slice:
fmt.Println(" KIND Slice -> Done(TODO:test)")
slice := reflect.ValueOf(t)
typ := reflect.TypeOf(f.Interface()).Elem()
fmt.Printf(" XXX KIND Slice[]%s\n", typ)
switch typ.Kind() {
case reflect.String:
for i := 0; i < slice.Len(); i++ {
fmt.Println(" *** slice.Index(i) -> ", slice.Index(i))
value := f.Interface().(string)
fmt.Printf(" [READ] Name: %s Kind: %s Type: %s Value: %v\n", n, f.Kind(), t, value)
processedValue := process(f.Interface().(string))
slice.Index(i).SetString(processedValue)
}
case reflect.Ptr:
// remove pointer and process
// this seems way too complicated... is there a better way???
case reflect.Struct:
for i := 0; i < slice.Len(); i++ {
fmt.Println(slice.Index(i))
patch(slice.Index(i), matches, process)
}
}
case reflect.Ptr:
fmt.Println(" KIND Ptr -> REFLECT RECURSIVE")
fmt.Printf(" f.Type() -> %v\n", f.Type()) // prints **Foo
fmt.Printf(" f.Type().Kind() -> %v\n", f.Type().Kind()) // prints **Foo
//fmt.Println(" val:", val)
//patch(val.Interface(), matches, process)
switch f.Type().Kind() {
default:
// implement for struct -> recursion
fmt.Printf(" TYPE %s (not implemented yet) -> REFLECT RECURSIVE\n", f.Type().Kind())
}
case reflect.Int:
case reflect.Int8:
case reflect.Int16:
case reflect.Int32:
case reflect.Int64:
case reflect.Uint:
case reflect.Uint8:
case reflect.Uint16:
case reflect.Uint32:
case reflect.Uint64:
case reflect.Uintptr:
case reflect.Bool:
case reflect.Float32:
case reflect.Float64:
case reflect.Func:
case reflect.Chan:
case reflect.Complex64:
case reflect.Complex128:
case reflect.UnsafePointer:
case reflect.Interface:
case reflect.Invalid:
fmt.Printf(" KIND %s (skip)\n", f.Kind())
default:
// implement for struct -> recursion
//TODO: Array
//TODO: Map
fmt.Printf(" KIND %s (not implemented yet) -> REFLECT RECURSIVE\n", f.Kind())
}
}
}
return nil
}
func Stringify(i interface{}) string {
s, _ := json.MarshalIndent(i, "", " ")
return string(s)
}
func hasTag(typeField reflect.StructField, tagName string, tagValue string) bool {
tag := typeField.Tag
if value, ok := tag.Lookup(tagName); ok {
return value == tagValue
}
return false
}
//setValue
// @param i must be ptr
func setValue(i interface{}, fieldName string, newValue string) {
log.Tracef("setValue on %v %v->%v", str.Stringify(i, false), fieldName, newValue)
v := reflect.ValueOf(i)
// if its a pointer, resolve its value
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() == reflect.Interface {
v2 := reflect.ValueOf(v.Interface())
v2.FieldByName(fieldName).SetString(newValue)
return
}
// should double-check we now have a struct (could still be anything)
if v.Kind() != reflect.Struct {
log.Fatalf("unexpected type: %v", v.Kind())
//return
v = v.Elem()
}
// exported field
f := v.FieldByName(fieldName)
if f.IsValid() {
// A Value can be changed only if it is
// addressable and was not obtained by
// the use of unexported struct fields.
if f.CanSet() {
// change value of N
if f.Kind() == reflect.String {
f.SetString(newValue)
}
}
}
}
Aucun commentaire:
Enregistrer un commentaire