github.com/mavryk-network/mvgo@v1.19.9/contract/bind/unmarshal.go (about)

     1  package bind
     2  
     3  import (
     4  	"math/big"
     5  	"reflect"
     6  	"time"
     7  
     8  	"github.com/mavryk-network/mvgo/mavryk"
     9  	"github.com/mavryk-network/mvgo/micheline"
    10  
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  // UnmarshalPrimPath unmarshals a nested prim contained in root, obtained using the given path,
    15  // into v.
    16  //
    17  // v must be a non-nil pointer to the expected result.
    18  func UnmarshalPrimPath(root micheline.Prim, path string, v any) error {
    19  	prim, err := root.GetPath(path)
    20  	if err != nil {
    21  		return errors.Wrap(err, "failed to get path")
    22  	}
    23  	return UnmarshalPrim(prim, v)
    24  }
    25  
    26  // UnmarshalPrimPaths unmarshals a Prim into a map of (path => destination).
    27  func UnmarshalPrimPaths(root micheline.Prim, dst map[string]any) error {
    28  	for path, v := range dst {
    29  		if err := UnmarshalPrimPath(root, path, v); err != nil {
    30  			return errors.Wrapf(err, "failed to unmarshal for path %s", path)
    31  		}
    32  	}
    33  	return nil
    34  }
    35  
    36  // UnmarshalPrim unmarshals a prim into v.
    37  //
    38  // v must be a non-nil pointer to the expected result.
    39  func UnmarshalPrim(prim micheline.Prim, v any) error {
    40  	val := reflect.ValueOf(v)
    41  	if val.Kind() != reflect.Ptr || val.IsNil() {
    42  		return errors.New("v should be a non-nil pointer")
    43  	}
    44  
    45  	return unmarshalPrimVal(prim, val)
    46  }
    47  
    48  var _ micheline.PrimUnmarshaler = &Bigmap[string, []byte]{}
    49  
    50  func unmarshalPrimVal(prim micheline.Prim, val reflect.Value) error {
    51  	// Init Elem value if val is Nil
    52  	if val.Kind() == reflect.Ptr && val.IsNil() {
    53  		val.Set(reflect.New(val.Type().Elem()))
    54  	}
    55  
    56  	// Check PrimUnmarshaler interface first
    57  	if unmarshaler, ok := val.Interface().(micheline.PrimUnmarshaler); ok {
    58  		return unmarshaler.UnmarshalPrim(prim)
    59  	} else if _, ok = val.Elem().Interface().(micheline.PrimUnmarshaler); ok {
    60  		return unmarshalPrimVal(prim, val.Elem())
    61  	}
    62  
    63  	// Check scalar and container types
    64  	switch prim.Type {
    65  	case micheline.PrimInt:
    66  		return unmarshalInt(prim.Int, val)
    67  	case micheline.PrimString:
    68  		return unmarshalString(prim.String, val)
    69  	case micheline.PrimBytes:
    70  		return unmarshalBytes(prim.Bytes, val)
    71  	case micheline.PrimSequence:
    72  		return unmarshalSlice(prim, val)
    73  	case micheline.PrimNullary:
    74  		switch prim.OpCode {
    75  		case micheline.D_TRUE, micheline.D_FALSE:
    76  			return unmarshalBool(prim.OpCode == micheline.D_TRUE, val)
    77  		case micheline.D_UNIT:
    78  			// v should be a struct{}, so we don't have to set anything
    79  			return nil
    80  		}
    81  	}
    82  
    83  	// Wasn't handled with *T, try with T
    84  	if val.Kind() == reflect.Ptr {
    85  		return unmarshalPrimVal(prim, val.Elem())
    86  	}
    87  
    88  	return errors.Errorf("prim type not handled: %s", prim.Type)
    89  }
    90  
    91  func unmarshalInt(i *big.Int, v reflect.Value) error {
    92  	if v.Kind() == reflect.Ptr && v.Type() != tBigInt {
    93  		return unmarshalInt(i, v.Elem())
    94  	}
    95  
    96  	switch v.Type() {
    97  	case tBigInt:
    98  		v.Set(reflect.ValueOf(i))
    99  	case tTime:
   100  		v.Set(reflect.ValueOf(time.Unix(i.Int64(), 0)))
   101  	default:
   102  		return errors.Errorf("unexpected type for int prim: %T", v.Type())
   103  	}
   104  
   105  	return nil
   106  }
   107  
   108  func unmarshalString(str string, v reflect.Value) error {
   109  	if v.Kind() == reflect.Ptr {
   110  		return unmarshalString(str, v.Elem())
   111  	}
   112  
   113  	switch v.Type() {
   114  	case tString:
   115  		v.SetString(str)
   116  	case tTime:
   117  		t, err := time.Parse(time.RFC3339, str)
   118  		if err != nil {
   119  			return errors.Wrapf(err, "failed to parse time: %s", str)
   120  		}
   121  		v.Set(reflect.ValueOf(t))
   122  	case tAddress:
   123  		address, err := mavryk.ParseAddress(str)
   124  		if err != nil {
   125  			return errors.Wrapf(err, "failed to parse address: %s", str)
   126  		}
   127  		v.Set(reflect.ValueOf(address))
   128  	case tKey:
   129  		key, err := mavryk.ParseKey(str)
   130  		if err != nil {
   131  			return errors.Wrapf(err, "failed to parse key: %s", str)
   132  		}
   133  		v.Set(reflect.ValueOf(key))
   134  	case tSignature:
   135  		sig, err := mavryk.ParseSignature(str)
   136  		if err != nil {
   137  			return errors.Wrapf(err, "failed to parse signature: %s", str)
   138  		}
   139  		v.Set(reflect.ValueOf(sig))
   140  	case tChainIdHash:
   141  		chainID, err := mavryk.ParseChainIdHash(str)
   142  		if err != nil {
   143  			return errors.Wrapf(err, "failed to parse chainID: %s", str)
   144  		}
   145  		v.Set(reflect.ValueOf(chainID))
   146  	default:
   147  		return errors.Errorf("unexpected type for string prim: %T", v.Type())
   148  	}
   149  	return nil
   150  }
   151  
   152  func unmarshalBytes(b []byte, v reflect.Value) error {
   153  	if v.Kind() == reflect.Ptr {
   154  		return unmarshalBytes(b, v.Elem())
   155  	}
   156  
   157  	switch v.Type() {
   158  	case tBytes:
   159  		v.Set(reflect.ValueOf(b))
   160  	case tAddress:
   161  		var addr mavryk.Address
   162  		if err := addr.UnmarshalBinary(b); err != nil {
   163  			return errors.Wrapf(err, "failed to parse address: %v", b)
   164  		}
   165  		v.Set(reflect.ValueOf(addr))
   166  	case tKey:
   167  		var key mavryk.Key
   168  		if err := key.UnmarshalBinary(b); err != nil {
   169  			return errors.Wrapf(err, "failed to parse key: %v", b)
   170  		}
   171  		v.Set(reflect.ValueOf(key))
   172  	case tSignature:
   173  		var sig mavryk.Signature
   174  		if err := sig.UnmarshalBinary(b); err != nil {
   175  			return errors.Wrapf(err, "failed to parse key: %v", b)
   176  		}
   177  		v.Set(reflect.ValueOf(sig))
   178  	case tChainIdHash:
   179  		var chainID mavryk.ChainIdHash
   180  		if err := chainID.UnmarshalBinary(b); err != nil {
   181  			return errors.Wrapf(err, "failed to parse chainID: %v", b)
   182  		}
   183  		v.Set(reflect.ValueOf(chainID))
   184  	default:
   185  		return errors.Errorf("unexpected type for bytes prim: %T", v.Type())
   186  	}
   187  	return nil
   188  }
   189  
   190  func unmarshalBool(b bool, v reflect.Value) error {
   191  	if v.Kind() == reflect.Ptr {
   192  		return unmarshalBool(b, v.Elem())
   193  	}
   194  
   195  	if v.Type() != tBool {
   196  		return errors.Errorf("unexpected type for bool prim: %T", v.Type())
   197  	}
   198  
   199  	v.Set(reflect.ValueOf(b))
   200  	return nil
   201  }
   202  
   203  // unmarshalSlice unmarshals a Prim sequence, into a slice (through a reflect.Value).
   204  func unmarshalSlice(prim micheline.Prim, v reflect.Value) error {
   205  	if v.Kind() == reflect.Ptr {
   206  		return unmarshalSlice(prim, v.Elem())
   207  	}
   208  
   209  	if v.Kind() != reflect.Slice {
   210  		return errors.Errorf("unexpected type for sequence prim: %T", v.Type())
   211  	}
   212  
   213  	elemType := v.Type().Elem()
   214  
   215  	// probably improvable
   216  	lenArgs := len(prim.Args)
   217  	newSlice := reflect.MakeSlice(v.Type(), lenArgs, lenArgs)
   218  	for i, arg := range prim.Args {
   219  		elem := reflect.New(elemType)
   220  		err := UnmarshalPrim(arg, elem.Interface())
   221  		if err != nil {
   222  			return err
   223  		}
   224  		newSlice.Index(i).Set(elem.Elem())
   225  	}
   226  	v.Set(newSlice)
   227  
   228  	return nil
   229  }
   230  
   231  // pre-computed reflect types
   232  var (
   233  	tBigInt      = reflect.TypeOf((*big.Int)(nil))
   234  	tString      = reflect.TypeOf("")
   235  	tBytes       = reflect.TypeOf(([]byte)(nil))
   236  	tBool        = reflect.TypeOf(true)
   237  	tTime        = reflect.TypeOf(time.Time{})
   238  	tAddress     = reflect.TypeOf(mavryk.Address{})
   239  	tKey         = reflect.TypeOf(mavryk.Key{})
   240  	tSignature   = reflect.TypeOf(mavryk.Signature{})
   241  	tChainIdHash = reflect.TypeOf(mavryk.ChainIdHash{})
   242  )