github.com/mavryk-network/mvgo@v1.19.9/contract/bind/marshal.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  type PrimMarshaler interface {
    15  	MarshalPrim(optimized bool) (micheline.Prim, error)
    16  }
    17  
    18  // MarshalPrim marshals v into a Prim by using reflection.
    19  //
    20  // If true, timestamps ,addresses, keys and signatures will be
    21  // marshaled in their optimized format.
    22  // See https://protocol.mavryk.org/active/michelson.html#differences-with-the-formal-notation.
    23  func MarshalPrim(v any, optimized bool) (micheline.Prim, error) {
    24  	// Handle types that we can process with a type switch
    25  	switch t := v.(type) {
    26  	case micheline.Prim:
    27  		return t, nil
    28  	case PrimMarshaler:
    29  		return t.MarshalPrim(optimized)
    30  	case *big.Int:
    31  		return micheline.NewBig(t), nil
    32  	case string:
    33  		return micheline.NewString(t), nil
    34  	case bool:
    35  		if t {
    36  			return micheline.NewCode(micheline.D_TRUE), nil
    37  		}
    38  		return micheline.NewCode(micheline.D_FALSE), nil
    39  	case []byte:
    40  		return micheline.NewBytes(t), nil
    41  	case time.Time:
    42  		if optimized {
    43  			return micheline.NewInt64(t.Unix()), nil
    44  		}
    45  		return micheline.NewString(t.Format(time.RFC3339)), nil
    46  	case mavryk.Address:
    47  		if optimized {
    48  			return micheline.NewAddress(t), nil
    49  		}
    50  		return micheline.NewString(t.String()), nil
    51  	case mavryk.Key:
    52  		if optimized {
    53  			return micheline.NewBytes(t.Bytes()), nil
    54  		}
    55  		return micheline.NewString(t.String()), nil
    56  	case mavryk.Signature:
    57  		if optimized {
    58  			return micheline.NewBytes(t.Bytes()), nil
    59  		}
    60  		return micheline.NewString(t.String()), nil
    61  	case mavryk.ChainIdHash:
    62  		return micheline.NewString(t.String()), nil
    63  	}
    64  
    65  	// Container types
    66  	val := reflect.ValueOf(v)
    67  	if val.Kind() == reflect.Slice {
    68  		n := val.Len()
    69  		prims := make([]micheline.Prim, 0, n)
    70  		for i := 0; i < n; i++ {
    71  			prim, err := MarshalPrim(val.Index(i).Interface(), optimized)
    72  			if err != nil {
    73  				return micheline.Prim{}, err
    74  			}
    75  			prims = append(prims, prim)
    76  		}
    77  		return micheline.NewSeq(prims...), nil
    78  	}
    79  
    80  	return micheline.Prim{}, errors.Errorf("type not handled: %T", v)
    81  }
    82  
    83  // MarshalParams marshals the provided params into a folded Prim.
    84  func MarshalParams(optimized bool, params ...any) (micheline.Prim, error) {
    85  	if len(params) == 0 {
    86  		return micheline.NewPrim(micheline.D_UNIT), nil
    87  	}
    88  
    89  	prims := make([]micheline.Prim, 0, len(params))
    90  	for _, p := range params {
    91  		prim, err := MarshalPrim(p, optimized)
    92  		if err != nil {
    93  			return micheline.Prim{}, err
    94  		}
    95  		prims = append(prims, prim)
    96  	}
    97  
    98  	return foldRightComb(prims...), nil
    99  }
   100  
   101  // foldRightComb folds a list of prims into nested Pairs, by following the right-comb convention.
   102  func foldRightComb(prims ...micheline.Prim) micheline.Prim {
   103  	n := len(prims)
   104  	switch n {
   105  	case 0:
   106  		return micheline.NewPrim(micheline.D_UNIT)
   107  	case 1:
   108  		return prims[0]
   109  	default:
   110  		return foldRightComb(append(prims[:n-2], micheline.NewPair(prims[n-2], prims[n-1]))...)
   111  	}
   112  }
   113  
   114  // MarshalParamsPath marshals the provided params into a Prim tree at specified paths.
   115  // This function is useful to render any kind of structs (records) into a type-conform
   116  // prim tree. It requires a list of tree positions in the form of paths. Both paths
   117  // and params must have the same length.
   118  func MarshalParamsPath(optimized bool, paths [][]int, params ...any) (micheline.Prim, error) {
   119  	if len(paths) != len(params) {
   120  		return micheline.Prim{}, errors.Errorf("invalid paths length")
   121  	}
   122  	root := emptyPair()
   123  	for i, p := range params {
   124  		prim, err := MarshalPrim(p, optimized)
   125  		if err != nil {
   126  			return micheline.Prim{}, err
   127  		}
   128  		insertPrim(&root, prim, paths[i])
   129  	}
   130  	return root, nil
   131  }
   132  
   133  // emptyPair builds a valid pair with empty invalid children
   134  func emptyPair() micheline.Prim {
   135  	return micheline.NewPair(micheline.Prim{}, micheline.Prim{})
   136  }
   137  
   138  // insertPrim builds a tree of pairs in dest and inserts the src pair at specified path.
   139  func insertPrim(dst *micheline.Prim, src micheline.Prim, path []int) {
   140  	if !dst.IsValid() {
   141  		*dst = emptyPair()
   142  	}
   143  
   144  	if len(path) == 1 {
   145  		dst.Args[path[0]] = src
   146  		return
   147  	}
   148  
   149  	insertPrim(&dst.Args[path[0]], src, path[1:])
   150  }