object.go 15.3 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

Steven Allen's avatar
Steven Allen committed
19
	cmds "gx/ipfs/QmNueRyPRQiV7PUEpnP4GgGLuK1rKQLaRW7sfPvUetYig1/go-ipfs-cmds"
Steven Allen's avatar
Steven Allen committed
20 21
	cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid"
	ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format"
Jeromy's avatar
Jeromy committed
22
	dag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag"
23
	cmdkit "gx/ipfs/QmdE4gMduCKCGAcczM2F5ioYDfdeKuPix138wrES1YSr7f/go-ipfs-cmdkit"
24 25
)

26 27
// ErrObjectTooLarge is returned when too much data was read from stdin. current limit 2m
var ErrObjectTooLarge = errors.New("input object was too large. limit is 2mbytes")
28

kpcyrd's avatar
kpcyrd committed
29
const inputLimit = 2 << 20
30

31 32
type Node struct {
	Links []Link
33
	Data  string
34 35
}

36 37 38 39 40 41
type Link struct {
	Name, Hash string
	Size       uint64
}

type Object struct {
Jeromy's avatar
Jeromy committed
42 43
	Hash  string `json:"Hash,omitempty"`
	Links []Link `json:"Links,omitempty"`
44 45
}

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

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

66
var ObjectDataCmd = &oldcmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
67
	Helptext: cmdkit.HelpText{
68
		Tagline: "Output the raw bytes of an IPFS object.",
69
		ShortDescription: `
70 71
'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.
72 73
`,
		LongDescription: `
74 75
'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.
76

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

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

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

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

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

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

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

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

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

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

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

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

		out := Object{
			Hash:  rp.Cid().String(),
			Links: outLinks,
		}

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

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

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

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

212 213 214 215
This command outputs data in the following encodings:
  * "protobuf"
  * "json"
  * "xml"
216 217 218 219 220 221 222 223 224
(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"
`,
225
	},
226

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

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

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

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

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

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

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

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

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

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

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

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

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

319
var ObjectStatCmd = &oldcmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
320
	Helptext: cmdkit.HelpText{
rht's avatar
rht committed
321
		Tagline: "Get stats for the DAG node named by <key>.",
322 323 324 325 326 327 328 329 330 331 332 333
		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
334 335
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format.").EnableStdin(),
336
	},
337
	Run: func(req oldcmds.Request, res oldcmds.Response) {
338
		api, err := req.InvocContext().GetApi()
339
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
340
			res.SetError(err, cmdkit.ErrNormal)
341
			return
342 343
		}

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

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

356 357 358 359 360 361 362 363 364 365
		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)
366
	},
367
	Type: ipld.NodeStat{},
368 369
	Marshalers: oldcmds.MarshalerMap{
		oldcmds.Text: func(res oldcmds.Response) (io.Reader, error) {
Jan Winkelmann's avatar
Jan Winkelmann committed
370 371 372 373 374
			v, err := unwrapOutput(res.Output())
			if err != nil {
				return nil, err
			}

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

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

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

395
var ObjectPutCmd = &oldcmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
396
	Helptext: cmdkit.HelpText{
397
		Tagline: "Store input as a DAG object, print its key.",
398 399 400 401 402 403
		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.
404 405
It reads from stdin, and the output is a base58 encoded multihash.

Henry's avatar
Henry committed
406 407
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
408
	* "protobuf"
Henry's avatar
Henry committed
409
	* "json" (default)
Dylan Powers's avatar
Dylan Powers committed
410 411 412

Examples:

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

415 416
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
417 418 419 420 421 422 423 424 425 426

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

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

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

Jan Winkelmann's avatar
Jan Winkelmann committed
433 434
	Arguments: []cmdkit.Argument{
		cmdkit.FileArg("data", true, false, "Data to be stored as a DAG object.").EnableStdin(),
Henry's avatar
Henry committed
435
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
436
	Options: []cmdkit.Option{
437 438
		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"),
439
		cmdkit.BoolOption("pin", "Pin this object when adding."),
Łukasz Magiera's avatar
Łukasz Magiera committed
440
		cmdkit.BoolOption("quiet", "q", "Write minimal output."),
441
	},
442
	Run: func(req oldcmds.Request, res oldcmds.Response) {
443
		api, err := req.InvocContext().GetApi()
444
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
445
			res.SetError(err, cmdkit.ErrNormal)
446
			return
447
		}
448

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

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

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

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

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

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

Jan Winkelmann's avatar
Jan Winkelmann committed
488 489 490 491 492 493 494 495 496
			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
497 498 499 500 501 502
			out := obj.Hash + "\n"
			if !quiet {
				out = "added " + out
			}

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

508
var ObjectNewCmd = &oldcmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
509
	Helptext: cmdkit.HelpText{
510
		Tagline: "Create a new object from an ipfs template.",
511 512 513 514 515 516 517 518
		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.
519 520 521

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

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

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

		res.SetOutput(&Object{Hash: nd.Cid().String()})
546
	},
547 548
	Marshalers: oldcmds.MarshalerMap{
		oldcmds.Text: func(res oldcmds.Response) (io.Reader, error) {
Jan Winkelmann's avatar
Jan Winkelmann committed
549 550 551 552 553 554 555 556 557 558 559
			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
560 561 562 563 564
		},
	},
	Type: Object{},
}

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

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

	return dagnode, nil
}
597

Jan Winkelmann's avatar
Jan Winkelmann committed
598 599 600 601 602 603 604 605 606 607 608 609 610
// 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
}
611 612 613 614 615 616 617 618 619 620 621

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

	return "", fmt.Errorf("unkown data field encoding")
}