forked from ipld/go-ipld-prime
/
marshal.go
155 lines (150 loc) · 3.24 KB
/
marshal.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package dagjson
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.Kind_Link` block,
// which is dag-json's special sauce for schemafree links.
func Marshal(n ipld.Node, sink shared.TokenSink) error {
var tk tok.Token
switch n.Kind() {
case ipld.Kind_Invalid:
return fmt.Errorf("cannot traverse a node that is absent")
case ipld.Kind_Null:
tk.Type = tok.TNull
_, err := sink.Step(&tk)
return err
case ipld.Kind_Map:
// Emit start of map.
tk.Type = tok.TMapOpen
tk.Length = int(n.Length()) // TODO: overflow check
if _, err := sink.Step(&tk); err != nil {
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
}
if _, err := sink.Step(&tk); err != nil {
return err
}
if err := Marshal(v, sink); err != nil {
return err
}
}
// Emit map close.
tk.Type = tok.TMapClose
_, err := sink.Step(&tk)
return err
case ipld.Kind_List:
// Emit start of list.
tk.Type = tok.TArrOpen
l := n.Length()
tk.Length = int(l) // TODO: overflow check
if _, err := sink.Step(&tk); err != nil {
return err
}
// Emit list contents (and recurse).
for i := int64(0); i < l; i++ {
v, err := n.LookupByIndex(i)
if err != nil {
return err
}
if err := Marshal(v, sink); err != nil {
return err
}
}
// Emit list close.
tk.Type = tok.TArrClose
_, err := sink.Step(&tk)
return err
case ipld.Kind_Bool:
v, err := n.AsBool()
if err != nil {
return err
}
tk.Type = tok.TBool
tk.Bool = v
_, err = sink.Step(&tk)
return err
case ipld.Kind_Int:
v, err := n.AsInt()
if err != nil {
return err
}
tk.Type = tok.TInt
tk.Int = int64(v)
_, err = sink.Step(&tk)
return err
case ipld.Kind_Float:
v, err := n.AsFloat()
if err != nil {
return err
}
tk.Type = tok.TFloat64
tk.Float64 = v
_, err = sink.Step(&tk)
return err
case ipld.Kind_String:
v, err := n.AsString()
if err != nil {
return err
}
tk.Type = tok.TString
tk.Str = v
_, err = sink.Step(&tk)
return err
case ipld.Kind_Bytes:
v, err := n.AsBytes()
if err != nil {
return err
}
tk.Type = tok.TBytes
tk.Bytes = v
_, err = sink.Step(&tk)
return err
case ipld.Kind_Link:
v, err := n.AsLink()
if err != nil {
return err
}
switch lnk := v.(type) {
case cidlink.Link:
// Precisely four tokens to emit:
tk.Type = tok.TMapOpen
tk.Length = 1
if _, err = sink.Step(&tk); err != nil {
return err
}
tk.Type = tok.TString
tk.Str = "/"
if _, err = sink.Step(&tk); err != nil {
return err
}
tk.Str = lnk.Cid.String()
if _, err = sink.Step(&tk); err != nil {
return err
}
tk.Type = tok.TMapClose
if _, err = sink.Step(&tk); err != nil {
return err
}
return nil
default:
return fmt.Errorf("schemafree link emission only supported by this codec for CID type links")
}
default:
panic("unreachable")
}
}