github.com/adecaro/fabric-ca@v2.0.0-alpha+incompatible/util/struct.go (about) 1 /* 2 Copyright IBM Corp. 2017 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package util 18 19 import ( 20 "fmt" 21 "reflect" 22 "strings" 23 "time" 24 25 "github.com/pkg/errors" 26 ) 27 28 // Field is a field of an arbitrary struct 29 type Field struct { 30 Name string 31 Path string 32 Type reflect.Type 33 Kind reflect.Kind 34 Leaf bool 35 Depth int 36 Tag reflect.StructTag 37 Value interface{} 38 Addr interface{} 39 Hide string 40 } 41 42 // ParseObj parses an object structure, calling back with field info 43 // for each field 44 func ParseObj(obj interface{}, cb func(*Field) error, tags map[string]string) error { 45 if cb == nil { 46 return errors.New("nil callback") 47 } 48 return parse(obj, cb, nil, tags) 49 } 50 51 func parse(ptr interface{}, cb func(*Field) error, parent *Field, tags map[string]string) error { 52 v := reflect.ValueOf(ptr) 53 err := parse2(v, cb, parent, tags) 54 if err != nil { 55 return err 56 } 57 return nil 58 } 59 60 func parse2(val reflect.Value, cb func(*Field) error, parent *Field, tags map[string]string) error { 61 var path string 62 var depth int 63 v := val.Elem() 64 t := v.Type() 65 for i := 0; i < v.NumField(); i++ { 66 vf := v.Field(i) 67 tf := t.Field(i) 68 name := strings.ToLower(tf.Name) 69 if tf.Name[0] == name[0] { 70 continue // skip unexported fields 71 } 72 if parent != nil { 73 path = fmt.Sprintf("%s.%s", parent.Path, name) 74 depth = parent.Depth + 1 75 } else { 76 path = name 77 } 78 kind := vf.Kind() 79 leaf := kind != reflect.Struct && kind != reflect.Ptr 80 field := &Field{ 81 Name: name, 82 Path: path, 83 Type: tf.Type, 84 Kind: kind, 85 Leaf: leaf, 86 Depth: depth, 87 Tag: tf.Tag, 88 Value: vf.Interface(), 89 Addr: vf.Addr().Interface(), 90 } 91 if parent == nil || parent.Hide == "" { 92 getHideTag(field, tags) 93 } else { 94 field.Hide = parent.Hide 95 } 96 err := cb(field) 97 if err != nil { 98 return err 99 } 100 if kind == reflect.Ptr { 101 // Skip parsing the entire struct if "skip" tag is present on a struct field 102 if tf.Tag.Get(TagSkip) == "true" { 103 continue 104 } 105 rf := reflect.New(vf.Type().Elem()) 106 err := parse2(rf, cb, field, tags) 107 if err != nil { 108 return err 109 } 110 } else if kind == reflect.Struct { 111 // Skip parsing the entire struct if "skip" tag is present on a struct field 112 if tf.Tag.Get(TagSkip) == "true" { 113 continue 114 } 115 err := parse(field.Addr, cb, field, tags) 116 if err != nil { 117 return err 118 } 119 } 120 } 121 return nil 122 } 123 124 // CopyMissingValues checks the dst interface for missing values and 125 // replaces them with value from src config struct. 126 // This does a deep copy of pointers. 127 func CopyMissingValues(src, dst interface{}) { 128 s := reflect.ValueOf(src).Elem() 129 d := reflect.ValueOf(dst).Elem() 130 copyMissingValues(s, d) 131 } 132 133 func copyMissingValues(src, dst reflect.Value) { 134 if !src.IsValid() { 135 return 136 } 137 switch src.Kind() { 138 case reflect.Ptr: 139 src = src.Elem() 140 if !src.IsValid() { 141 return 142 } 143 if dst.IsNil() { 144 dst.Set(reflect.New(src.Type())) 145 } 146 copyMissingValues(src, dst.Elem()) 147 case reflect.Interface: 148 if src.IsNil() { 149 return 150 } 151 src = src.Elem() 152 if dst.IsNil() { 153 newVal := reflect.New(src.Type()).Elem() 154 copyMissingValues(src, newVal) 155 dst.Set(newVal) 156 } else { 157 copyMissingValues(src, dst.Elem()) 158 } 159 case reflect.Struct: 160 if !src.IsValid() { 161 return 162 } 163 t, ok := src.Interface().(time.Time) 164 if ok { 165 dst.Set(reflect.ValueOf(t)) 166 } 167 for i := 0; i < src.NumField(); i++ { 168 copyMissingValues(src.Field(i), dst.Field(i)) 169 } 170 case reflect.Slice: 171 if !dst.IsNil() { 172 return 173 } 174 dst.Set(reflect.MakeSlice(src.Type(), src.Len(), src.Cap())) 175 for i := 0; i < src.Len(); i++ { 176 copyMissingValues(src.Index(i), dst.Index(i)) 177 } 178 case reflect.Map: 179 if dst.IsNil() { 180 dst.Set(reflect.MakeMap(src.Type())) 181 } 182 for _, key := range src.MapKeys() { 183 sval := src.MapIndex(key) 184 dval := dst.MapIndex(key) 185 copy := !dval.IsValid() 186 if copy { 187 dval = reflect.New(sval.Type()).Elem() 188 } 189 copyMissingValues(sval, dval) 190 if copy { 191 dst.SetMapIndex(key, dval) 192 } 193 } 194 default: 195 if !dst.CanInterface() { 196 return 197 } 198 dval := dst.Interface() 199 zval := reflect.Zero(dst.Type()).Interface() 200 if reflect.DeepEqual(dval, zval) { 201 dst.Set(src) 202 } 203 } 204 } 205 206 func getHideTag(f *Field, tags map[string]string) { 207 var key, val string 208 key = fmt.Sprintf("%s.%s", "hide", f.Path) 209 if tags != nil { 210 val = tags[key] 211 } 212 if val == "" { 213 val = f.Tag.Get("hide") 214 } 215 f.Hide = val 216 }