marshal.go 3.22 KB
Newer Older
Eric Myhre's avatar
Eric Myhre committed
1 2 3 4 5 6 7 8 9 10 11 12
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"
)

Eric Myhre's avatar
Eric Myhre committed
13
// This file should be identical to the general feature in the parent package,
14
// except for the `case ipld.Kind_Link` block,
Eric Myhre's avatar
Eric Myhre committed
15
// which is dag-cbor's special sauce for schemafree links.
Eric Myhre's avatar
Eric Myhre committed
16

Will Scott's avatar
Will Scott committed
17
func Marshal(n ipld.Node, sink shared.TokenSink, allowLinks bool) error {
Eric Myhre's avatar
Eric Myhre committed
18
	var tk tok.Token
Will Scott's avatar
Will Scott committed
19
	return marshal(n, &tk, sink, allowLinks)
20 21
}

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