marshal.go 3.03 KB
Newer Older
Eric Myhre's avatar
Eric Myhre committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
package dagcbor

import (
	"fmt"

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

	ipld "github.com/ipld/go-ipld-prime"
	cidlink "github.com/ipld/go-ipld-prime/linking/cid"
)

// This should be identical to the general feature in the parent package,
// except for the `case ipld.ReprKind_Link` block,
// which is dag-cbor's special sauce for schemafree links.
func Marshal(n ipld.Node, sink shared.TokenSink) error {
	var tk tok.Token
18 19 20 21
	return marshal(n, &tk, sink)
}

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