marshal.go 3.04 KB
Newer Older
Eric Myhre's avatar
Eric Myhre committed
1 2 3 4 5 6 7 8
package codec

import (
	"fmt"

	"github.com/polydawn/refmt/shared"
	"github.com/polydawn/refmt/tok"

9
	ipld "github.com/ipld/go-ipld-prime"
Eric Myhre's avatar
Eric Myhre committed
10 11 12 13 14 15 16 17 18 19 20 21
)

// Marshal provides a very general node-to-tokens marshalling feature.
// It can handle either cbor or json by being combined with a refmt TokenSink.
//
// It is valid for all the data model types except links, which are only
// supported if the nodes are typed and provide additional information
// to clarify how links should be encoded through their type info.
// (The dag-cbor and dag-json formats can be used if links are of CID
// implementation and need to be encoded in a schemafree way.)
func Marshal(n ipld.Node, sink shared.TokenSink) error {
	var tk tok.Token
22 23 24 25
	return marshal(n, &tk, sink)
}

func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink) error {
Eric Myhre's avatar
Eric Myhre committed
26 27 28 29 30
	switch n.ReprKind() {
	case ipld.ReprKind_Invalid:
		return fmt.Errorf("cannot traverse a node that is undefined")
	case ipld.ReprKind_Null:
		tk.Type = tok.TNull
31
		_, err := sink.Step(tk)
Eric Myhre's avatar
Eric Myhre committed
32 33 34 35 36
		return err
	case ipld.ReprKind_Map:
		// Emit start of map.
		tk.Type = tok.TMapOpen
		tk.Length = n.Length()
37
		if _, err := sink.Step(tk); err != nil {
Eric Myhre's avatar
Eric Myhre committed
38 39 40 41 42 43 44 45 46 47 48 49 50
			return err
		}
		// Emit map contents (and recurse).
		for itr := n.MapIterator(); !itr.Done(); {
			k, v, err := itr.Next()
			if err != nil {
				return err
			}
			tk.Type = tok.TString
			tk.Str, err = k.AsString()
			if err != nil {
				return err
			}
51
			if _, err := sink.Step(tk); err != nil {
Eric Myhre's avatar
Eric Myhre committed
52 53
				return err
			}
54
			if err := marshal(v, tk, sink); err != nil {
Eric Myhre's avatar
Eric Myhre committed
55 56 57 58 59
				return err
			}
		}
		// Emit map close.
		tk.Type = tok.TMapClose
60
		_, err := sink.Step(tk)
Eric Myhre's avatar
Eric Myhre committed
61 62 63 64 65 66
		return err
	case ipld.ReprKind_List:
		// Emit start of list.
		tk.Type = tok.TArrOpen
		l := n.Length()
		tk.Length = l
67
		if _, err := sink.Step(tk); err != nil {
Eric Myhre's avatar
Eric Myhre committed
68 69 70 71 72 73 74 75
			return err
		}
		// Emit list contents (and recurse).
		for i := 0; i < l; i++ {
			v, err := n.LookupIndex(i)
			if err != nil {
				return err
			}
76
			if err := marshal(v, tk, sink); err != nil {
Eric Myhre's avatar
Eric Myhre committed
77 78 79 80 81
				return err
			}
		}
		// Emit list close.
		tk.Type = tok.TArrClose
82
		_, err := sink.Step(tk)
Eric Myhre's avatar
Eric Myhre committed
83 84 85 86 87 88 89 90
		return err
	case ipld.ReprKind_Bool:
		v, err := n.AsBool()
		if err != nil {
			return err
		}
		tk.Type = tok.TBool
		tk.Bool = v
91
		_, err = sink.Step(tk)
Eric Myhre's avatar
Eric Myhre committed
92 93 94 95 96 97 98 99
		return err
	case ipld.ReprKind_Int:
		v, err := n.AsInt()
		if err != nil {
			return err
		}
		tk.Type = tok.TInt
		tk.Int = int64(v)
100
		_, err = sink.Step(tk)
Eric Myhre's avatar
Eric Myhre committed
101 102 103 104 105 106 107 108
		return err
	case ipld.ReprKind_Float:
		v, err := n.AsFloat()
		if err != nil {
			return err
		}
		tk.Type = tok.TFloat64
		tk.Float64 = v
109
		_, err = sink.Step(tk)
Eric Myhre's avatar
Eric Myhre committed
110 111 112 113 114 115 116 117
		return err
	case ipld.ReprKind_String:
		v, err := n.AsString()
		if err != nil {
			return err
		}
		tk.Type = tok.TString
		tk.Str = v
118
		_, err = sink.Step(tk)
Eric Myhre's avatar
Eric Myhre committed
119 120 121 122 123 124 125 126
		return err
	case ipld.ReprKind_Bytes:
		v, err := n.AsBytes()
		if err != nil {
			return err
		}
		tk.Type = tok.TBytes
		tk.Bytes = v
127
		_, err = sink.Step(tk)
Eric Myhre's avatar
Eric Myhre committed
128 129 130 131 132 133 134
		return err
	case ipld.ReprKind_Link:
		return fmt.Errorf("link emission not supported by this codec without a schema!  (maybe you want dag-cbor or dag-json)")
	default:
		panic("unreachable")
	}
}