github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/common/channelconfig/standardvalues.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package channelconfig
     8  
     9  import (
    10  	"fmt"
    11  	"reflect"
    12  
    13  	"github.com/golang/protobuf/proto"
    14  	cb "github.com/hyperledger/fabric-protos-go/common"
    15  )
    16  
    17  // DeserializeGroup deserializes the value for all values in a config group
    18  func DeserializeProtoValuesFromGroup(group *cb.ConfigGroup, protosStructs ...interface{}) error {
    19  	sv, err := NewStandardValues(protosStructs...)
    20  	if err != nil {
    21  		logger.Panicf("This is a compile time bug only, the proto structures are somehow invalid: %s", err)
    22  	}
    23  
    24  	for key, value := range group.Values {
    25  		if _, err := sv.Deserialize(key, value.Value); err != nil {
    26  			return err
    27  		}
    28  	}
    29  	return nil
    30  }
    31  
    32  type StandardValues struct {
    33  	lookup map[string]proto.Message
    34  }
    35  
    36  // NewStandardValues accepts a structure which must contain only protobuf message
    37  // types.  The structure may embed other (non-pointer) structures which satisfy
    38  // the same condition.  NewStandard values will instantiate memory for all the proto
    39  // messages and build a lookup map from structure field name to proto message instance
    40  // This is a useful way to easily implement the Values interface
    41  func NewStandardValues(protosStructs ...interface{}) (*StandardValues, error) {
    42  	sv := &StandardValues{
    43  		lookup: make(map[string]proto.Message),
    44  	}
    45  
    46  	for _, protosStruct := range protosStructs {
    47  		logger.Debugf("Initializing protos for %T\n", protosStruct)
    48  		if err := sv.initializeProtosStruct(reflect.ValueOf(protosStruct)); err != nil {
    49  			return nil, err
    50  		}
    51  	}
    52  
    53  	return sv, nil
    54  }
    55  
    56  // Deserialize looks up the backing Values proto of the given name, unmarshals the given bytes
    57  // to populate the backing message structure, and returns a referenced to the retained deserialized
    58  // message (or an error, either because the key did not exist, or there was an an error unmarshaling
    59  func (sv *StandardValues) Deserialize(key string, value []byte) (proto.Message, error) {
    60  	msg, ok := sv.lookup[key]
    61  	if !ok {
    62  		return nil, fmt.Errorf("Unexpected key %s", key)
    63  	}
    64  
    65  	err := proto.Unmarshal(value, msg)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	return msg, nil
    71  }
    72  
    73  func (sv *StandardValues) initializeProtosStruct(objValue reflect.Value) error {
    74  	objType := objValue.Type()
    75  	if objType.Kind() != reflect.Ptr {
    76  		return fmt.Errorf("Non pointer type")
    77  	}
    78  	if objType.Elem().Kind() != reflect.Struct {
    79  		return fmt.Errorf("Non struct type")
    80  	}
    81  
    82  	numFields := objValue.Elem().NumField()
    83  	for i := 0; i < numFields; i++ {
    84  		structField := objType.Elem().Field(i)
    85  		logger.Debugf("Processing field: %s\n", structField.Name)
    86  		switch structField.Type.Kind() {
    87  		case reflect.Ptr:
    88  			fieldPtr := objValue.Elem().Field(i)
    89  			if !fieldPtr.CanSet() {
    90  				return fmt.Errorf("Cannot set structure field %s (unexported?)", structField.Name)
    91  			}
    92  			fieldPtr.Set(reflect.New(structField.Type.Elem()))
    93  		default:
    94  			return fmt.Errorf("Bad type supplied: %s", structField.Type.Kind())
    95  		}
    96  
    97  		proto, ok := objValue.Elem().Field(i).Interface().(proto.Message)
    98  		if !ok {
    99  			return fmt.Errorf("Field type %T does not implement proto.Message", objValue.Elem().Field(i))
   100  		}
   101  
   102  		_, ok = sv.lookup[structField.Name]
   103  		if ok {
   104  			return fmt.Errorf("Ambiguous field name specified, multiple occurrences of %s", structField.Name)
   105  		}
   106  
   107  		sv.lookup[structField.Name] = proto
   108  	}
   109  
   110  	return nil
   111  }