github.com/hxx258456/fabric-ca-gm@v0.0.3-0.20221111064038-a268ad7e3a37/internal/pkg/util/struct.go (about)

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