refs.go 6.75 KB
Newer Older
1 2 3
package commands

import (
4
	"bytes"
5
	"context"
Jeromy's avatar
Jeromy committed
6
	"errors"
7
	"io"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
8
	"strings"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
9

10 11
	cmds "github.com/ipfs/go-ipfs/commands"
	"github.com/ipfs/go-ipfs/core"
Jan Winkelmann's avatar
Jan Winkelmann committed
12
	e "github.com/ipfs/go-ipfs/core/commands/e"
13 14
	dag "github.com/ipfs/go-ipfs/merkledag"
	path "github.com/ipfs/go-ipfs/path"
Jeromy's avatar
Jeromy committed
15

16 17
	cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid"
	node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format"
Jan Winkelmann's avatar
Jan Winkelmann committed
18
	"gx/ipfs/QmSNbH2A1evCCbJSDC6u3RV3GGDhgu6pRGbXHvrN89tMKf/go-ipfs-cmdkit"
19 20
)

21 22
// KeyList is a general type for outputting lists of keys
type KeyList struct {
23
	Keys []*cid.Cid
24 25 26
}

// KeyListTextMarshaler outputs a KeyList as plaintext, one key per line
27
func KeyListTextMarshaler(res cmds.Response) (io.Reader, error) {
Jan Winkelmann's avatar
Jan Winkelmann committed
28 29 30 31 32 33 34 35 36 37
	out, err := unwrapOutput(res.Output())
	if err != nil {
		return nil, err
	}

	output, ok := out.(*KeyList)
	if !ok {
		return nil, e.TypeErr(output, out)
	}

38
	buf := new(bytes.Buffer)
39
	for _, key := range output.Keys {
40
		buf.WriteString(key.String() + "\n")
41
	}
42
	return buf, nil
43 44
}

45
var RefsCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
46
	Helptext: cmdkit.HelpText{
47
		Tagline: "List links (references) from an object.",
48
		ShortDescription: `
49 50
Lists the hashes of all the links an IPFS or IPNS object(s) contains,
with the following format:
51

52 53
  <link base58 hash>

54
NOTE: List all references recursively by using the flag '-r'.
55 56
`,
	},
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
57 58 59
	Subcommands: map[string]*cmds.Command{
		"local": RefsLocalCmd,
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
60 61
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("ipfs-path", true, true, "Path to the object(s) to list refs from.").EnableStdin(),
62
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
63 64
	Options: []cmdkit.Option{
		cmdkit.StringOption("format", "Emit edges with given format. Available tokens: <src> <dst> <linkname>.").Default("<dst>"),
65 66 67
		cmdkit.BoolOption("edges", "e", "Emit edge format: `<from> -> <to>`."),
		cmdkit.BoolOption("unique", "u", "Omit duplicate refs from output."),
		cmdkit.BoolOption("recursive", "r", "Recursively list links of child nodes."),
68
	},
69
	Run: func(req cmds.Request, res cmds.Response) {
Jeromy's avatar
Jeromy committed
70 71
		ctx := req.Context()
		n, err := req.InvocContext().GetNode()
72
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
73
			res.SetError(err, cmdkit.ErrNormal)
74
			return
75
		}
76

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
77
		unique, _, err := req.Option("unique").Bool()
78
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
79
			res.SetError(err, cmdkit.ErrNormal)
80
			return
81
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
82 83 84

		recursive, _, err := req.Option("recursive").Bool()
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
85
			res.SetError(err, cmdkit.ErrNormal)
86
			return
87 88
		}

89
		format, _, err := req.Option("format").String()
90
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
91
			res.SetError(err, cmdkit.ErrNormal)
92
			return
93
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
94

95
		edges, _, err := req.Option("edges").Bool()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
96
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
97
			res.SetError(err, cmdkit.ErrNormal)
98
			return
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
99
		}
100 101 102
		if edges {
			if format != "<dst>" {
				res.SetError(errors.New("using format arguement with edges is not allowed"),
Jan Winkelmann's avatar
Jan Winkelmann committed
103
					cmdkit.ErrClient)
104 105 106 107 108
				return
			}

			format = "<src> -> <dst>"
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
109

110
		objs, err := objectsForPaths(ctx, n, req.Arguments())
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
111
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
112
			res.SetError(err, cmdkit.ErrNormal)
113
			return
114
		}
115

Jeromy's avatar
Jeromy committed
116 117
		out := make(chan interface{})
		res.SetOutput((<-chan interface{})(out))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
118 119

		go func() {
Jeromy's avatar
Jeromy committed
120
			defer close(out)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
121 122

			rw := RefWriter{
Jeromy's avatar
Jeromy committed
123
				out:       out,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
124
				DAG:       n.DAG,
125
				Ctx:       ctx,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
126
				Unique:    unique,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
127
				PrintFmt:  format,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
128 129 130 131 132
				Recursive: recursive,
			}

			for _, o := range objs {
				if _, err := rw.WriteRefs(o); err != nil {
Jeromy's avatar
Jeromy committed
133
					out <- &RefWrapper{Err: err.Error()}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
134 135 136 137
					return
				}
			}
		}()
Jeromy's avatar
Jeromy committed
138
	},
Jakub Sztandera's avatar
Jakub Sztandera committed
139 140
	Marshalers: refsMarshallerMap,
	Type:       RefWrapper{},
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
141 142 143
}

var RefsLocalCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
144
	Helptext: cmdkit.HelpText{
145
		Tagline: "List all local references.",
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
146 147 148 149 150
		ShortDescription: `
Displays the hashes of all local objects.
`,
	},

151
	Run: func(req cmds.Request, res cmds.Response) {
Jeromy's avatar
Jeromy committed
152 153
		ctx := req.Context()
		n, err := req.InvocContext().GetNode()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
154
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
155
			res.SetError(err, cmdkit.ErrNormal)
156
			return
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
157 158 159
		}

		// todo: make async
160
		allKeys, err := n.Blockstore.AllKeysChan(ctx)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
161
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
162
			res.SetError(err, cmdkit.ErrNormal)
163
			return
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
164 165
		}

Jakub Sztandera's avatar
Jakub Sztandera committed
166 167
		out := make(chan interface{})
		res.SetOutput((<-chan interface{})(out))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
168 169

		go func() {
Jakub Sztandera's avatar
Jakub Sztandera committed
170
			defer close(out)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
171

172
			for k := range allKeys {
173
				out <- &RefWrapper{Ref: k.String()}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
174 175
			}
		}()
Jakub Sztandera's avatar
Jakub Sztandera committed
176 177 178 179 180 181 182
	},
	Marshalers: refsMarshallerMap,
	Type:       RefWrapper{},
}

var refsMarshallerMap = cmds.MarshalerMap{
	cmds.Text: func(res cmds.Response) (io.Reader, error) {
Jan Winkelmann's avatar
Jan Winkelmann committed
183 184 185
		v, err := unwrapOutput(res.Output())
		if err != nil {
			return nil, err
Jakub Sztandera's avatar
Jakub Sztandera committed
186 187
		}

Jan Winkelmann's avatar
Jan Winkelmann committed
188 189 190 191
		obj, ok := v.(*RefWrapper)
		if !ok {
			return nil, e.TypeErr(obj, v)
		}
Jakub Sztandera's avatar
Jakub Sztandera committed
192

Jan Winkelmann's avatar
Jan Winkelmann committed
193 194
		if obj.Err != "" {
			return nil, errors.New(obj.Err)
Jakub Sztandera's avatar
Jakub Sztandera committed
195
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
196

Jan Winkelmann's avatar
Jan Winkelmann committed
197
		return strings.NewReader(obj.Ref + "\n"), nil
198 199 200
	},
}

Jeromy's avatar
Jeromy committed
201 202
func objectsForPaths(ctx context.Context, n *core.IpfsNode, paths []string) ([]node.Node, error) {
	objects := make([]node.Node, len(paths))
203 204 205 206 207 208 209
	for i, sp := range paths {
		p, err := path.ParsePath(sp)
		if err != nil {
			return nil, err
		}

		o, err := core.Resolve(ctx, n.Namesys, n.Resolver, p)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
210 211 212 213 214 215 216 217
		if err != nil {
			return nil, err
		}
		objects[i] = o
	}
	return objects, nil
}

Jeromy's avatar
Jeromy committed
218 219 220
type RefWrapper struct {
	Ref string
	Err string
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
221 222 223
}

type RefWriter struct {
Jeromy's avatar
Jeromy committed
224
	out chan interface{}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
225 226
	DAG dag.DAGService
	Ctx context.Context
227

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
228 229
	Unique    bool
	Recursive bool
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
230
	PrintFmt  string
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
231

Jeromy's avatar
Jeromy committed
232
	seen *cid.Set
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
233 234 235
}

// WriteRefs writes refs of the given object to the underlying writer.
Jeromy's avatar
Jeromy committed
236
func (rw *RefWriter) WriteRefs(n node.Node) (int, error) {
237 238 239
	if rw.Recursive {
		return rw.writeRefsRecursive(n)
	}
rht's avatar
rht committed
240
	return rw.writeRefsSingle(n)
241 242
}

Jeromy's avatar
Jeromy committed
243
func (rw *RefWriter) writeRefsRecursive(n node.Node) (int, error) {
Jeromy's avatar
Jeromy committed
244
	nc := n.Cid()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
245

246
	var count int
247
	for i, ng := range dag.GetDAG(rw.Ctx, rw.DAG, n) {
248
		lc := n.Links()[i].Cid
Jeromy's avatar
Jeromy committed
249
		if rw.skip(lc) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
250 251 252
			continue
		}

253
		if err := rw.WriteEdge(nc, lc, n.Links()[i].Name); err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
254 255
			return count, err
		}
256

257
		nd, err := ng.Get(rw.Ctx)
258
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
259
			return count, err
260 261
		}

262
		c, err := rw.writeRefsRecursive(nd)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
263
		count += c
264
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
265
			return count, err
266 267
		}
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
268
	return count, nil
269 270
}

Jeromy's avatar
Jeromy committed
271
func (rw *RefWriter) writeRefsSingle(n node.Node) (int, error) {
Jeromy's avatar
Jeromy committed
272
	c := n.Cid()
273

Jeromy's avatar
Jeromy committed
274
	if rw.skip(c) {
275 276 277 278
		return 0, nil
	}

	count := 0
279 280
	for _, l := range n.Links() {
		lc := l.Cid
Jeromy's avatar
Jeromy committed
281
		if rw.skip(lc) {
282 283 284
			continue
		}

Jeromy's avatar
Jeromy committed
285
		if err := rw.WriteEdge(c, lc, l.Name); err != nil {
286 287 288 289 290 291 292
			return count, err
		}
		count++
	}
	return count, nil
}

Jeromy's avatar
Jeromy committed
293 294
// skip returns whether to skip a cid
func (rw *RefWriter) skip(c *cid.Cid) bool {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
295 296 297
	if !rw.Unique {
		return false
	}
298

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
299
	if rw.seen == nil {
Jeromy's avatar
Jeromy committed
300
		rw.seen = cid.NewSet()
301 302
	}

Jeromy's avatar
Jeromy committed
303 304 305
	has := rw.seen.Has(c)
	if !has {
		rw.seen.Add(c)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
306
	}
Jeromy's avatar
Jeromy committed
307
	return has
308 309
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
310
// Write one edge
Jeromy's avatar
Jeromy committed
311
func (rw *RefWriter) WriteEdge(from, to *cid.Cid, linkname string) error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
312 313 314 315 316
	if rw.Ctx != nil {
		select {
		case <-rw.Ctx.Done(): // just in case.
			return rw.Ctx.Err()
		default:
317 318 319
		}
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
320
	var s string
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
321 322 323
	switch {
	case rw.PrintFmt != "":
		s = rw.PrintFmt
Jeromy's avatar
Jeromy committed
324 325
		s = strings.Replace(s, "<src>", from.String(), -1)
		s = strings.Replace(s, "<dst>", to.String(), -1)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
326 327
		s = strings.Replace(s, "<linkname>", linkname, -1)
	default:
Jeromy's avatar
Jeromy committed
328
		s += to.String()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
329 330
	}

Jeromy's avatar
Jeromy committed
331
	rw.out <- &RefWrapper{Ref: s}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
332
	return nil
333
}