object.go 15.1 KB
Newer Older
1
package objectcmd
2 3 4

import (
	"bytes"
5
	"encoding/base64"
6
	"errors"
7
	"fmt"
8 9
	"io"
	"io/ioutil"
10
	"strings"
11
	"text/tabwriter"
12

13 14
	oldcmds "github.com/ipfs/go-ipfs/commands"
	lgc "github.com/ipfs/go-ipfs/commands/legacy"
Jan Winkelmann's avatar
Jan Winkelmann committed
15
	e "github.com/ipfs/go-ipfs/core/commands/e"
16 17
	coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
	"github.com/ipfs/go-ipfs/core/coreapi/interface/options"
18

19
	cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid"
Steven Allen's avatar
Steven Allen committed
20
	dag "gx/ipfs/QmY8BMUSpCwNiTmFhACmC9Bt1qT63cHP35AoQAus4x14qH/go-merkledag"
21
	ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format"
Steven Allen's avatar
Steven Allen committed
22
	cmds "gx/ipfs/QmdTmGruUz23vgzym3uWpnAEQdGdGifQqBvP8UXSRjG8gZ/go-ipfs-cmds"
23
	cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
24 25
)

kpcyrd's avatar
kpcyrd committed
26
const inputLimit = 2 << 20
27

28 29
type Node struct {
	Links []Link
30
	Data  string
31 32
}

33 34 35 36 37 38
type Link struct {
	Name, Hash string
	Size       uint64
}

type Object struct {
Jeromy's avatar
Jeromy committed
39 40
	Hash  string `json:"Hash,omitempty"`
	Links []Link `json:"Links,omitempty"`
41 42
}

Kejie Zhang's avatar
Kejie Zhang committed
43
var ErrDataEncoding = errors.New("unkown data field encoding")
44

45
var ObjectCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
46
	Helptext: cmdkit.HelpText{
47
		Tagline: "Interact with IPFS objects.",
Matt Bell's avatar
Matt Bell committed
48 49 50
		ShortDescription: `
'ipfs object' is a plumbing command used to manipulate DAG objects
directly.`,
51
	},
52 53

	Subcommands: map[string]*cmds.Command{
54 55 56 57 58
		"data":  lgc.NewCommand(ObjectDataCmd),
		"diff":  lgc.NewCommand(ObjectDiffCmd),
		"get":   lgc.NewCommand(ObjectGetCmd),
		"links": lgc.NewCommand(ObjectLinksCmd),
		"new":   lgc.NewCommand(ObjectNewCmd),
59
		"patch": ObjectPatchCmd,
60 61
		"put":   lgc.NewCommand(ObjectPutCmd),
		"stat":  lgc.NewCommand(ObjectStatCmd),
62 63 64
	},
}

65
var ObjectDataCmd = &oldcmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
66
	Helptext: cmdkit.HelpText{
67
		Tagline: "Output the raw bytes of an IPFS object.",
68
		ShortDescription: `
69 70
'ipfs object data' is a plumbing command for retrieving the raw bytes stored
in a DAG node. It outputs to stdout, and <key> is a base58 encoded multihash.
71 72
`,
		LongDescription: `
73 74
'ipfs object data' is a plumbing command for retrieving the raw bytes stored
in a DAG node. It outputs to stdout, and <key> is a base58 encoded multihash.
75

76 77
Note that the "--encoding" option does not affect the output, since the output
is the raw data of the object.
78
`,
79
	},
80

Jan Winkelmann's avatar
Jan Winkelmann committed
81 82
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format.").EnableStdin(),
83
	},
84
	Run: func(req oldcmds.Request, res oldcmds.Response) {
85
		api, err := req.InvocContext().GetApi()
86
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
87
			res.SetError(err, cmdkit.ErrNormal)
88
			return
89
		}
90

91
		path, err := coreiface.ParsePath(req.Arguments()[0])
Jeromy's avatar
Jeromy committed
92
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
93
			res.SetError(err, cmdkit.ErrNormal)
Jeromy's avatar
Jeromy committed
94 95 96
			return
		}

97
		data, err := api.Object().Data(req.Context(), path)
98
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
99
			res.SetError(err, cmdkit.ErrNormal)
100 101
			return
		}
102

103
		res.SetOutput(data)
104 105 106
	},
}

107
var ObjectLinksCmd = &oldcmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
108
	Helptext: cmdkit.HelpText{
109
		Tagline: "Output the links pointed to by the specified object.",
110
		ShortDescription: `
rht's avatar
rht committed
111
'ipfs object links' is a plumbing command for retrieving the links from
Matt Bell's avatar
Matt Bell committed
112 113
a DAG node. It outputs to stdout, and <key> is a base58 encoded
multihash.
114 115
`,
	},
116

Jan Winkelmann's avatar
Jan Winkelmann committed
117 118
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format.").EnableStdin(),
119
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
120
	Options: []cmdkit.Option{
121
		cmdkit.BoolOption("headers", "v", "Print table headers (Hash, Size, Name)."),
palkeo's avatar
palkeo committed
122
	},
123
	Run: func(req oldcmds.Request, res oldcmds.Response) {
124
		api, err := req.InvocContext().GetApi()
125
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
126
			res.SetError(err, cmdkit.ErrNormal)
127
			return
128
		}
129

130 131
		// get options early -> exit early in case of error
		if _, _, err := req.Option("headers").Bool(); err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
132
			res.SetError(err, cmdkit.ErrNormal)
133 134 135
			return
		}

136 137 138 139 140 141 142
		path, err := coreiface.ParsePath(req.Arguments()[0])
		if err != nil {
			res.SetError(err, cmdkit.ErrNormal)
			return
		}

		rp, err := api.ResolvePath(req.Context(), path)
143
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
144
			res.SetError(err, cmdkit.ErrNormal)
145 146
			return
		}
147

148
		links, err := api.Object().Links(req.Context(), rp)
149
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
150
			res.SetError(err, cmdkit.ErrNormal)
151 152
			return
		}
153 154 155 156 157 158 159 160 161 162

		outLinks := make([]Link, len(links))
		for i, link := range links {
			outLinks[i] = Link{
				Hash: link.Cid.String(),
				Name: link.Name,
				Size: link.Size,
			}
		}

Steven Allen's avatar
Steven Allen committed
163
		out := &Object{
164 165 166 167 168
			Hash:  rp.Cid().String(),
			Links: outLinks,
		}

		res.SetOutput(out)
169
	},
170 171
	Marshalers: oldcmds.MarshalerMap{
		oldcmds.Text: func(res oldcmds.Response) (io.Reader, error) {
Jan Winkelmann's avatar
Jan Winkelmann committed
172 173 174 175 176 177 178 179 180 181
			v, err := unwrapOutput(res.Output())
			if err != nil {
				return nil, err
			}

			object, ok := v.(*Object)
			if !ok {
				return nil, e.TypeErr(object, v)
			}

182 183
			buf := new(bytes.Buffer)
			w := tabwriter.NewWriter(buf, 1, 2, 1, ' ', 0)
184 185
			headers, _, _ := res.Request().Option("headers").Bool()
			if headers {
186
				fmt.Fprintln(w, "Hash\tSize\tName")
187
			}
188
			for _, link := range object.Links {
189
				fmt.Fprintf(w, "%s\t%v\t%s\n", link.Hash, link.Size, link.Name)
190
			}
191
			w.Flush()
192
			return buf, nil
193 194
		},
	},
keks's avatar
keks committed
195
	Type: &Object{},
196 197
}

198
var ObjectGetCmd = &oldcmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
199
	Helptext: cmdkit.HelpText{
rht's avatar
rht committed
200
		Tagline: "Get and serialize the DAG node named by <key>.",
201
		ShortDescription: `
rht's avatar
rht committed
202
'ipfs object get' is a plumbing command for retrieving DAG nodes.
203 204
It serializes the DAG node to the format specified by the "--encoding"
flag. It outputs to stdout, and <key> is a base58 encoded multihash.
205 206
`,
		LongDescription: `
rht's avatar
rht committed
207
'ipfs object get' is a plumbing command for retrieving DAG nodes.
208 209
It serializes the DAG node to the format specified by the "--encoding"
flag. It outputs to stdout, and <key> is a base58 encoded multihash.
210

211 212 213 214
This command outputs data in the following encodings:
  * "protobuf"
  * "json"
  * "xml"
215 216 217 218 219 220 221 222 223
(Specified by the "--encoding" or "--enc" flag)

The encoding of the object's data field can be specifed by using the
--data-encoding flag

Supported values are:
	* "text" (default)
	* "base64"
`,
224
	},
225

Jan Winkelmann's avatar
Jan Winkelmann committed
226 227
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format.").EnableStdin(),
228
	},
229 230 231
	Options: []cmdkit.Option{
		cmdkit.StringOption("data-encoding", "Encoding type of the data field, either \"text\" or \"base64\".").WithDefault("text"),
	},
232
	Run: func(req oldcmds.Request, res oldcmds.Response) {
233
		api, err := req.InvocContext().GetApi()
234
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
235
			res.SetError(err, cmdkit.ErrNormal)
236
			return
237
		}
238

239 240 241 242 243
		path, err := coreiface.ParsePath(req.Arguments()[0])
		if err != nil {
			res.SetError(err, cmdkit.ErrNormal)
			return
		}
244

245 246 247 248 249 250
		datafieldenc, _, err := req.Option("data-encoding").String()
		if err != nil {
			res.SetError(err, cmdkit.ErrNormal)
			return
		}

251
		nd, err := api.Object().Get(req.Context(), path)
252
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
253
			res.SetError(err, cmdkit.ErrNormal)
254
			return
255 256
		}

257 258 259
		r, err := api.Object().Data(req.Context(), path)
		if err != nil {
			res.SetError(err, cmdkit.ErrNormal)
260 261 262
			return
		}

263 264 265 266 267 268 269
		data, err := ioutil.ReadAll(r)
		if err != nil {
			res.SetError(err, cmdkit.ErrNormal)
			return
		}

		out, err := encodeData(data, datafieldenc)
270 271 272 273 274
		if err != nil {
			res.SetError(err, cmdkit.ErrNormal)
			return
		}

275
		node := &Node{
276 277
			Links: make([]Link, len(nd.Links())),
			Data:  out,
278 279
		}

280
		for i, link := range nd.Links() {
281
			node.Links[i] = Link{
282
				Hash: link.Cid.String(),
283 284 285 286 287
				Name: link.Name,
				Size: link.Size,
			}
		}

288
		res.SetOutput(node)
289
	},
290
	Type: Node{},
291 292
	Marshalers: oldcmds.MarshalerMap{
		oldcmds.Protobuf: func(res oldcmds.Response) (io.Reader, error) {
Jan Winkelmann's avatar
Jan Winkelmann committed
293 294 295 296 297 298 299 300 301 302
			v, err := unwrapOutput(res.Output())
			if err != nil {
				return nil, err
			}

			node, ok := v.(*Node)
			if !ok {
				return nil, e.TypeErr(node, v)
			}

303 304
			// deserialize the Data field as text as this was the standard behaviour
			object, err := deserializeNode(node, "text")
305 306
			if err != nil {
				return nil, err
307
			}
308 309 310 311 312 313

			marshaled, err := object.Marshal()
			if err != nil {
				return nil, err
			}
			return bytes.NewReader(marshaled), nil
314 315 316 317
		},
	},
}

318
var ObjectStatCmd = &oldcmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
319
	Helptext: cmdkit.HelpText{
rht's avatar
rht committed
320
		Tagline: "Get stats for the DAG node named by <key>.",
321 322 323 324 325 326 327 328 329 330 331 332
		ShortDescription: `
'ipfs object stat' is a plumbing command to print DAG node statistics.
<key> is a base58 encoded multihash. It outputs to stdout:

	NumLinks        int number of links in link table
	BlockSize       int size of the raw, encoded data
	LinksSize       int size of the links segment
	DataSize        int size of the data segment
	CumulativeSize  int cumulative size of object and its references
`,
	},

Jan Winkelmann's avatar
Jan Winkelmann committed
333 334
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format.").EnableStdin(),
335
	},
336
	Run: func(req oldcmds.Request, res oldcmds.Response) {
337
		api, err := req.InvocContext().GetApi()
338
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
339
			res.SetError(err, cmdkit.ErrNormal)
340
			return
341 342
		}

343
		path, err := coreiface.ParsePath(req.Arguments()[0])
344
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
345
			res.SetError(err, cmdkit.ErrNormal)
346
			return
347 348
		}

349
		ns, err := api.Object().Stat(req.Context(), path)
350
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
351
			res.SetError(err, cmdkit.ErrNormal)
352
			return
353 354
		}

355 356 357 358 359 360 361 362 363 364
		oldStat := &ipld.NodeStat{
			Hash:           ns.Cid.String(),
			NumLinks:       ns.NumLinks,
			BlockSize:      ns.BlockSize,
			LinksSize:      ns.LinksSize,
			DataSize:       ns.DataSize,
			CumulativeSize: ns.CumulativeSize,
		}

		res.SetOutput(oldStat)
365
	},
366
	Type: ipld.NodeStat{},
367 368
	Marshalers: oldcmds.MarshalerMap{
		oldcmds.Text: func(res oldcmds.Response) (io.Reader, error) {
Jan Winkelmann's avatar
Jan Winkelmann committed
369 370 371 372 373
			v, err := unwrapOutput(res.Output())
			if err != nil {
				return nil, err
			}

374
			ns, ok := v.(*ipld.NodeStat)
Jan Winkelmann's avatar
Jan Winkelmann committed
375 376 377
			if !ok {
				return nil, e.TypeErr(ns, v)
			}
378

379
			buf := new(bytes.Buffer)
380
			w := func(s string, n int) {
381
				fmt.Fprintf(buf, "%s: %d\n", s, n)
382 383 384 385 386 387 388
			}
			w("NumLinks", ns.NumLinks)
			w("BlockSize", ns.BlockSize)
			w("LinksSize", ns.LinksSize)
			w("DataSize", ns.DataSize)
			w("CumulativeSize", ns.CumulativeSize)

389
			return buf, nil
390 391 392 393
		},
	},
}

394
var ObjectPutCmd = &oldcmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
395
	Helptext: cmdkit.HelpText{
396
		Tagline: "Store input as a DAG object, print its key.",
397 398 399 400 401 402
		ShortDescription: `
'ipfs object put' is a plumbing command for storing DAG nodes.
It reads from stdin, and the output is a base58 encoded multihash.
`,
		LongDescription: `
'ipfs object put' is a plumbing command for storing DAG nodes.
403 404
It reads from stdin, and the output is a base58 encoded multihash.

Henry's avatar
Henry committed
405 406
Data should be in the format specified by the --inputenc flag.
--inputenc may be one of the following:
Matt Bell's avatar
Matt Bell committed
407
	* "protobuf"
Henry's avatar
Henry committed
408
	* "json" (default)
Dylan Powers's avatar
Dylan Powers committed
409 410 411

Examples:

412
	$ echo '{ "Data": "abc" }' | ipfs object put
Dylan Powers's avatar
Dylan Powers committed
413

414 415
This creates a node with the data 'abc' and no links. For an object with
links, create a file named 'node.json' with the contents:
Dylan Powers's avatar
Dylan Powers committed
416 417 418 419 420 421 422 423 424 425

    {
        "Data": "another",
        "Links": [ {
            "Name": "some link",
            "Hash": "QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V",
            "Size": 8
        } ]
    }

426
And then run:
Dylan Powers's avatar
Dylan Powers committed
427

428
	$ ipfs object put node.json
429
`,
430
	},
431

Jan Winkelmann's avatar
Jan Winkelmann committed
432 433
	Arguments: []cmdkit.Argument{
		cmdkit.FileArg("data", true, false, "Data to be stored as a DAG object.").EnableStdin(),
Henry's avatar
Henry committed
434
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
435
	Options: []cmdkit.Option{
436 437
		cmdkit.StringOption("inputenc", "Encoding type of input data. One of: {\"protobuf\", \"json\"}.").WithDefault("json"),
		cmdkit.StringOption("datafieldenc", "Encoding type of the data field, either \"text\" or \"base64\".").WithDefault("text"),
438
		cmdkit.BoolOption("pin", "Pin this object when adding."),
Łukasz Magiera's avatar
Łukasz Magiera committed
439
		cmdkit.BoolOption("quiet", "q", "Write minimal output."),
440
	},
441
	Run: func(req oldcmds.Request, res oldcmds.Response) {
442
		api, err := req.InvocContext().GetApi()
443
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
444
			res.SetError(err, cmdkit.ErrNormal)
445
			return
446
		}
447

448 449
		input, err := req.Files().NextFile()
		if err != nil && err != io.EOF {
Jan Winkelmann's avatar
Jan Winkelmann committed
450
			res.SetError(err, cmdkit.ErrNormal)
451
			return
452 453
		}

454
		inputenc, _, err := req.Option("inputenc").String()
Henry's avatar
Henry committed
455
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
456
			res.SetError(err, cmdkit.ErrNormal)
Henry's avatar
Henry committed
457 458
			return
		}
459

460
		datafieldenc, _, err := req.Option("datafieldenc").String()
461
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
462
			res.SetError(err, cmdkit.ErrNormal)
463 464
			return
		}
slothbag's avatar
slothbag committed
465

Łukasz Magiera's avatar
Łukasz Magiera committed
466 467
		dopin, _, err := req.Option("pin").Bool()
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
468
			res.SetError(err, cmdkit.ErrNormal)
Łukasz Magiera's avatar
Łukasz Magiera committed
469 470 471
			return
		}

472 473 474 475
		p, err := api.Object().Put(req.Context(), input,
			options.Object.DataType(datafieldenc),
			options.Object.InputEnc(inputenc),
			options.Object.Pin(dopin))
476
		if err != nil {
477
			res.SetError(err, cmdkit.ErrNormal)
478
			return
479 480
		}

481
		res.SetOutput(&Object{Hash: p.Cid().String()})
482
	},
483 484
	Marshalers: oldcmds.MarshalerMap{
		oldcmds.Text: func(res oldcmds.Response) (io.Reader, error) {
Łukasz Magiera's avatar
Łukasz Magiera committed
485 486
			quiet, _, _ := res.Request().Option("quiet").Bool()

Jan Winkelmann's avatar
Jan Winkelmann committed
487 488 489 490 491 492 493 494 495
			v, err := unwrapOutput(res.Output())
			if err != nil {
				return nil, err
			}
			obj, ok := v.(*Object)
			if !ok {
				return nil, e.TypeErr(obj, v)
			}

Łukasz Magiera's avatar
Łukasz Magiera committed
496 497 498 499 500 501
			out := obj.Hash + "\n"
			if !quiet {
				out = "added " + out
			}

			return strings.NewReader(out), nil
502 503
		},
	},
504
	Type: Object{},
505 506
}

507
var ObjectNewCmd = &oldcmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
508
	Helptext: cmdkit.HelpText{
509
		Tagline: "Create a new object from an ipfs template.",
510 511 512 513 514 515 516 517
		ShortDescription: `
'ipfs object new' is a plumbing command for creating new DAG nodes.
`,
		LongDescription: `
'ipfs object new' is a plumbing command for creating new DAG nodes.
By default it creates and returns a new empty merkledag node, but
you may pass an optional template argument to create a preformatted
node.
518 519 520

Available templates:
	* unixfs-dir
521 522
`,
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
523 524
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("template", false, false, "Template to use. Optional."),
525
	},
526
	Run: func(req oldcmds.Request, res oldcmds.Response) {
527
		api, err := req.InvocContext().GetApi()
528
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
529
			res.SetError(err, cmdkit.ErrNormal)
530 531 532
			return
		}

533
		template := "empty"
534
		if len(req.Arguments()) == 1 {
535
			template = req.Arguments()[0]
536 537
		}

538 539
		nd, err := api.Object().New(req.Context(), options.Object.Type(template))
		if err != nil && err != io.EOF {
Jan Winkelmann's avatar
Jan Winkelmann committed
540
			res.SetError(err, cmdkit.ErrNormal)
541 542
			return
		}
543 544

		res.SetOutput(&Object{Hash: nd.Cid().String()})
545
	},
546 547
	Marshalers: oldcmds.MarshalerMap{
		oldcmds.Text: func(res oldcmds.Response) (io.Reader, error) {
Jan Winkelmann's avatar
Jan Winkelmann committed
548 549 550 551 552 553 554 555 556 557 558
			v, err := unwrapOutput(res.Output())
			if err != nil {
				return nil, err
			}

			obj, ok := v.(*Object)
			if !ok {
				return nil, e.TypeErr(obj, v)
			}

			return strings.NewReader(obj.Hash + "\n"), nil
559 560 561 562 563
		},
	},
	Type: Object{},
}

564
// converts the Node object into a real dag.ProtoNode
Jeromy's avatar
Jeromy committed
565
func deserializeNode(nd *Node, dataFieldEncoding string) (*dag.ProtoNode, error) {
566
	dagnode := new(dag.ProtoNode)
slothbag's avatar
slothbag committed
567 568
	switch dataFieldEncoding {
	case "text":
Jeromy's avatar
Jeromy committed
569
		dagnode.SetData([]byte(nd.Data))
slothbag's avatar
slothbag committed
570
	case "base64":
Steven Allen's avatar
Steven Allen committed
571 572 573 574
		data, err := base64.StdEncoding.DecodeString(nd.Data)
		if err != nil {
			return nil, err
		}
575
		dagnode.SetData(data)
slothbag's avatar
slothbag committed
576
	default:
577
		return nil, ErrDataEncoding
578 579
	}

Steven Allen's avatar
Steven Allen committed
580
	links := make([]*ipld.Link, len(nd.Links))
Jeromy's avatar
Jeromy committed
581
	for i, link := range nd.Links {
582
		c, err := cid.Decode(link.Hash)
583 584 585
		if err != nil {
			return nil, err
		}
Steven Allen's avatar
Steven Allen committed
586
		links[i] = &ipld.Link{
587 588
			Name: link.Name,
			Size: link.Size,
589
			Cid:  c,
590 591
		}
	}
Steven Allen's avatar
Steven Allen committed
592
	dagnode.SetLinks(links)
593 594 595

	return dagnode, nil
}
596

Jan Winkelmann's avatar
Jan Winkelmann committed
597 598 599 600 601 602 603 604 605 606 607 608 609
// copy+pasted from ../commands.go
func unwrapOutput(i interface{}) (interface{}, error) {
	var (
		ch <-chan interface{}
		ok bool
	)

	if ch, ok = i.(<-chan interface{}); !ok {
		return nil, e.TypeErr(ch, i)
	}

	return <-ch, nil
}
610 611 612 613 614 615 616 617 618

func encodeData(data []byte, encoding string) (string, error) {
	switch encoding {
	case "text":
		return string(data), nil
	case "base64":
		return base64.StdEncoding.EncodeToString(data), nil
	}

619
	return "", ErrDataEncoding
620
}