github.com/urso/go-structform@v0.0.2/gotype/fold_inline.go (about) 1 package gotype 2 3 import ( 4 "reflect" 5 6 "github.com/urso/go-structform" 7 "github.com/urso/go-structform/visitors" 8 ) 9 10 // getReflectFoldMapKeys implements inline fold of a map[string]X type, 11 // not reporting object start/end events 12 func getReflectFoldMapKeys(c *foldContext, t reflect.Type) (reFoldFn, error) { 13 if t.Key().Kind() != reflect.String { 14 return nil, errMapRequiresStringKey 15 } 16 17 f := getMapInlineByPrimitiveElem(t.Elem()) 18 if f != nil { 19 return f, nil 20 } 21 22 elemVisitor, err := getReflectFold(c, t.Elem()) 23 if err != nil { 24 return nil, err 25 } 26 27 return makeMapKeysFold(elemVisitor), nil 28 } 29 30 func makeMapKeysFold(elemVisitor reFoldFn) reFoldFn { 31 return func(C *foldContext, rv reflect.Value) error { 32 if rv.IsNil() || !rv.IsValid() { 33 return nil 34 } 35 36 for _, k := range rv.MapKeys() { 37 if err := C.OnKey(k.String()); err != nil { 38 return err 39 } 40 if err := elemVisitor(C, rv.MapIndex(k)); err != nil { 41 return err 42 } 43 } 44 return nil 45 } 46 } 47 48 // getReflectFoldInlineInterface create an inline folder for an yet unknown type. 49 // The actual types folder must open/close an object 50 func getReflectFoldInlineInterface(C *foldContext, t reflect.Type) (reFoldFn, error) { 51 var ( 52 // cache last used folder 53 lastType reflect.Type 54 lastVisitor reFoldFn 55 ) 56 57 return embeddObjReFold(C, func(C *foldContext, rv reflect.Value) error { 58 if rv.Type() != lastType { 59 elemVisitor, err := getReflectFold(C, rv.Type()) 60 if err != nil { 61 return err 62 } 63 64 lastVisitor = elemVisitor 65 lastType = rv.Type() 66 } 67 return lastVisitor(C, rv) 68 }), nil 69 } 70 71 func embeddObjReFold(C *foldContext, objFold reFoldFn) reFoldFn { 72 var ( 73 ctx = *C 74 vs = visitors.NewExpectObjVisitor(nil) 75 ) 76 77 ctx.visitor = structform.EnsureExtVisitor(vs).(visitor) 78 return func(C *foldContext, rv reflect.Value) error { 79 // don't inline missing/empty object 80 if rv.IsNil() || !rv.IsValid() { 81 return nil 82 } 83 84 vs.SetActive(C.visitor) 85 err := objFold(&ctx, rv) 86 if err == nil && !vs.Done() { 87 err = errExpectedObjectClose 88 } 89 90 vs.SetActive(nil) 91 return err 92 } 93 }