dag.go 6.38 KB
Newer Older
1 2 3 4 5
package dagcmd

import (
	"fmt"
	"io"
6
	"math"
7
	"strings"
8

Overbool's avatar
Overbool committed
9 10
	"github.com/ipfs/go-ipfs/core/commands/cmdenv"
	"github.com/ipfs/go-ipfs/core/coredag"
11

Steven Allen's avatar
Steven Allen committed
12
	iface "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core"
Steven Allen's avatar
Steven Allen committed
13
	cmds "gx/ipfs/QmPHTMcFRnDfyF8mk7RXHoZXNQ3uvBHDmuLgvkG7RLwN6t/go-ipfs-cmds"
Steven Allen's avatar
Steven Allen committed
14
	path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path"
Steven Allen's avatar
Steven Allen committed
15
	files "gx/ipfs/QmQmhotPUzVrMEWNK3x1R5jQ5ZHWyL7tVUrmRPjrBrvyCb/go-ipfs-files"
Steven Allen's avatar
Steven Allen committed
16
	cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid"
17
	ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format"
18
	cidenc "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil/cidenc"
19
	cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
Steven Allen's avatar
Steven Allen committed
20
	mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash"
21 22 23
)

var DagCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
24
	Helptext: cmdkit.HelpText{
Jeromy's avatar
Jeromy committed
25 26 27 28 29 30 31
		Tagline: "Interact with ipld dag objects.",
		ShortDescription: `
'ipfs dag' is used for creating and manipulating dag objects.

This subcommand is currently an experimental feature, but it is intended
to deprecate and replace the existing 'ipfs object' command moving forward.
		`,
32 33
	},
	Subcommands: map[string]*cmds.Command{
Łukasz Magiera's avatar
Łukasz Magiera committed
34 35 36
		"put":     DagPutCmd,
		"get":     DagGetCmd,
		"resolve": DagResolveCmd,
37 38 39
	},
}

Łukasz Magiera's avatar
Łukasz Magiera committed
40
// OutputObject is the output type of 'dag put' command
41
type OutputObject struct {
42
	Cid cid.Cid
43 44
}

Łukasz Magiera's avatar
Łukasz Magiera committed
45
// ResolveOutput is the output type of 'dag resolve' command
Łukasz Magiera's avatar
Łukasz Magiera committed
46
type ResolveOutput struct {
47
	Cid     cid.Cid
Łukasz Magiera's avatar
Łukasz Magiera committed
48 49 50
	RemPath string
}

51
var DagPutCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
52
	Helptext: cmdkit.HelpText{
53
		Tagline: "Add a dag node to ipfs.",
Jeromy's avatar
Jeromy committed
54 55 56 57
		ShortDescription: `
'ipfs dag put' accepts input from a file or stdin and parses it
into an object of the specified format.
`,
58
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
59 60
	Arguments: []cmdkit.Argument{
		cmdkit.FileArg("object data", true, true, "The object to put").EnableStdin(),
61
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
62
	Options: []cmdkit.Option{
63 64
		cmdkit.StringOption("format", "f", "Format that the object will be added as.").WithDefault("cbor"),
		cmdkit.StringOption("input-enc", "Format that the input object will be.").WithDefault("json"),
65
		cmdkit.BoolOption("pin", "Pin this object when adding."),
66
		cmdkit.StringOption("hash", "Hash function to use").WithDefault(""),
67
	},
Overbool's avatar
Overbool committed
68
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
69
		api, err := cmdenv.GetApi(env, req)
70
		if err != nil {
Overbool's avatar
Overbool committed
71
			return err
72 73
		}

Overbool's avatar
Overbool committed
74 75 76 77
		ienc, _ := req.Options["input-enc"].(string)
		format, _ := req.Options["format"].(string)
		hash, _ := req.Options["hash"].(string)
		dopin, _ := req.Options["pin"].(bool)
78

79 80 81 82 83 84 85 86
		// mhType tells inputParser which hash should be used. MaxUint64 means 'use
		// default hash' (sha256 for cbor, sha1 for git..)
		mhType := uint64(math.MaxUint64)

		if hash != "" {
			var ok bool
			mhType, ok = mh.Names[hash]
			if !ok {
Overbool's avatar
Overbool committed
87
				return fmt.Errorf("%s in not a valid multihash name", hash)
88 89 90
			}
		}

91
		var adder ipld.NodeAdder = api.Dag()
Steven Allen's avatar
Steven Allen committed
92
		if dopin {
93
			adder = api.Dag().Pinning()
Steven Allen's avatar
Steven Allen committed
94
		}
95
		b := ipld.NewBatch(req.Context, adder)
Steven Allen's avatar
Steven Allen committed
96

97
		it := req.Files.Entries()
98
		for it.Next() {
99 100
			file := files.FileFromEntry(it)
			if file == nil {
101
				return fmt.Errorf("expected a regular file")
Łukasz Magiera's avatar
Łukasz Magiera committed
102
			}
103
			nds, err := coredag.ParseInputs(ienc, format, file, mhType, -1)
Steven Allen's avatar
Steven Allen committed
104
			if err != nil {
Łukasz Magiera's avatar
Łukasz Magiera committed
105 106
				return err
			}
Steven Allen's avatar
Steven Allen committed
107 108 109
			if len(nds) == 0 {
				return fmt.Errorf("no node returned from ParseInputs")
			}
110

Steven Allen's avatar
Steven Allen committed
111
			for _, nd := range nds {
Hector Sanjuan's avatar
Hector Sanjuan committed
112
				err := b.Add(req.Context, nd)
Łukasz Magiera's avatar
Łukasz Magiera committed
113 114 115
				if err != nil {
					return err
				}
Jeromy's avatar
Jeromy committed
116
			}
117

Steven Allen's avatar
Steven Allen committed
118 119 120 121
			cid := nds[0].Cid()
			if err := res.Emit(&OutputObject{Cid: cid}); err != nil {
				return err
			}
122
		}
123
		if it.Err() != nil {
124
			return it.Err()
125
		}
126

Steven Allen's avatar
Steven Allen committed
127
		if err := b.Commit(); err != nil {
Overbool's avatar
Overbool committed
128 129
			return err
		}
130

Steven Allen's avatar
Steven Allen committed
131
		return nil
Overbool's avatar
Overbool committed
132 133 134 135
	},
	Type: OutputObject{},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *OutputObject) error {
136 137 138 139 140
			enc, err := cmdenv.GetLowLevelCidEncoder(req)
			if err != nil {
				return err
			}
			fmt.Fprintln(w, enc.Encode(out.Cid))
Overbool's avatar
Overbool committed
141 142
			return nil
		}),
143 144 145 146
	},
}

var DagGetCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
147
	Helptext: cmdkit.HelpText{
148
		Tagline: "Get a dag node from ipfs.",
Jeromy's avatar
Jeromy committed
149
		ShortDescription: `
Łukasz Magiera's avatar
Łukasz Magiera committed
150
'ipfs dag get' fetches a dag node from ipfs and prints it out in the specified
151
format.
Jeromy's avatar
Jeromy committed
152
`,
153
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
154 155
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("ref", true, false, "The object to get").EnableStdin(),
156
	},
Overbool's avatar
Overbool committed
157
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
158
		api, err := cmdenv.GetApi(env, req)
159
		if err != nil {
Overbool's avatar
Overbool committed
160
			return err
161 162
		}

163
		p, err := iface.ParsePath(req.Arguments[0])
164
		if err != nil {
Overbool's avatar
Overbool committed
165
			return err
166 167
		}

168
		rp, err := api.ResolvePath(req.Context, p)
169
		if err != nil {
Overbool's avatar
Overbool committed
170
			return err
171
		}
172 173

		obj, err := api.Dag().Get(req.Context, rp.Cid())
174
		if err != nil {
Overbool's avatar
Overbool committed
175
			return err
176 177
		}

178
		var out interface{} = obj
179 180
		if len(rp.Remainder()) > 0 {
			rem := strings.Split(rp.Remainder(), "/")
181 182
			final, _, err := obj.Resolve(rem)
			if err != nil {
Overbool's avatar
Overbool committed
183
				return err
184 185 186
			}
			out = final
		}
187
		return cmds.EmitOnce(res, &out)
188 189
	},
}
Łukasz Magiera's avatar
Łukasz Magiera committed
190

Łukasz Magiera's avatar
Łukasz Magiera committed
191
// DagResolveCmd returns address of highest block within a path and a path remainder
Łukasz Magiera's avatar
Łukasz Magiera committed
192
var DagResolveCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
193
	Helptext: cmdkit.HelpText{
Łukasz Magiera's avatar
Łukasz Magiera committed
194 195 196 197 198
		Tagline: "Resolve ipld block",
		ShortDescription: `
'ipfs dag resolve' fetches a dag node from ipfs, prints it's address and remaining path.
`,
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
199 200
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("ref", true, false, "The path to resolve").EnableStdin(),
Łukasz Magiera's avatar
Łukasz Magiera committed
201
	},
Overbool's avatar
Overbool committed
202
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
203
		api, err := cmdenv.GetApi(env, req)
Łukasz Magiera's avatar
Łukasz Magiera committed
204
		if err != nil {
Overbool's avatar
Overbool committed
205
			return err
Łukasz Magiera's avatar
Łukasz Magiera committed
206 207
		}

208
		p, err := iface.ParsePath(req.Arguments[0])
Łukasz Magiera's avatar
Łukasz Magiera committed
209
		if err != nil {
Overbool's avatar
Overbool committed
210
			return err
Łukasz Magiera's avatar
Łukasz Magiera committed
211 212
		}

213
		rp, err := api.ResolvePath(req.Context, p)
Łukasz Magiera's avatar
Łukasz Magiera committed
214
		if err != nil {
Overbool's avatar
Overbool committed
215
			return err
Łukasz Magiera's avatar
Łukasz Magiera committed
216 217
		}

218
		return cmds.EmitOnce(res, &ResolveOutput{
219 220
			Cid:     rp.Cid(),
			RemPath: rp.Remainder(),
Łukasz Magiera's avatar
Łukasz Magiera committed
221 222
		})
	},
Overbool's avatar
Overbool committed
223 224
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *ResolveOutput) error {
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
			var (
				enc cidenc.Encoder
				err error
			)
			switch {
			case !cmdenv.CidBaseDefined(req):
				// Not specified, check the path.
				enc, err = cmdenv.CidEncoderFromPath(req.Arguments[0])
				if err == nil {
					break
				}
				// Nope, fallback on the default.
				fallthrough
			default:
				enc, err = cmdenv.GetLowLevelCidEncoder(req)
				if err != nil {
					return err
				}
243 244
			}
			p := enc.Encode(out.Cid)
Overbool's avatar
Overbool committed
245 246
			if out.RemPath != "" {
				p = path.Join([]string{p, out.RemPath})
Łukasz Magiera's avatar
Łukasz Magiera committed
247 248
			}

Overbool's avatar
Overbool committed
249 250 251
			fmt.Fprint(w, p)
			return nil
		}),
Łukasz Magiera's avatar
Łukasz Magiera committed
252 253 254
	},
	Type: ResolveOutput{},
}