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

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

10
	key "github.com/ipfs/go-ipfs/blocks/key"
11 12 13 14 15
	cmds "github.com/ipfs/go-ipfs/commands"
	"github.com/ipfs/go-ipfs/core"
	dag "github.com/ipfs/go-ipfs/merkledag"
	path "github.com/ipfs/go-ipfs/path"
	u "github.com/ipfs/go-ipfs/util"
Jeromy's avatar
Jeromy committed
16
	context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
17 18
)

19 20
// KeyList is a general type for outputting lists of keys
type KeyList struct {
21
	Keys []key.Key
22 23 24
}

// KeyListTextMarshaler outputs a KeyList as plaintext, one key per line
25
func KeyListTextMarshaler(res cmds.Response) (io.Reader, error) {
26
	output := res.Output().(*KeyList)
27
	buf := new(bytes.Buffer)
28
	for _, key := range output.Keys {
29
		buf.WriteString(key.B58String() + "\n")
30
	}
31
	return buf, nil
32 33
}

34
var RefsCmd = &cmds.Command{
35
	Helptext: cmds.HelpText{
rht's avatar
rht committed
36
		Tagline: "Lists links (references) from an object.",
37 38
		ShortDescription: `
Retrieves the object named by <ipfs-path> and displays the link
39
hashes it contains, with the following format:
40

41 42
  <link base58 hash>

43 44 45
Note: list all refs recursively with -r.
`,
	},
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
46 47 48
	Subcommands: map[string]*cmds.Command{
		"local": RefsLocalCmd,
	},
49
	Arguments: []cmds.Argument{
50
		cmds.StringArg("ipfs-path", true, true, "Path to the object(s) to list refs from.").EnableStdin(),
51 52
	},
	Options: []cmds.Option{
Richard Littauer's avatar
Richard Littauer committed
53
		cmds.StringOption("format", "Emit edges with given format. Available tokens: <src> <dst> <linkname>."),
54 55 56
		cmds.BoolOption("edges", "e", "Emit edge format: `<from> -> <to>`."),
		cmds.BoolOption("unique", "u", "Omit duplicate refs from output."),
		cmds.BoolOption("recursive", "r", "Recursively list links of child nodes."),
57
	},
58
	Run: func(req cmds.Request, res cmds.Response) {
Jeromy's avatar
Jeromy committed
59 60
		ctx := req.Context()
		n, err := req.InvocContext().GetNode()
61
		if err != nil {
62 63
			res.SetError(err, cmds.ErrNormal)
			return
64
		}
65

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
66
		unique, _, err := req.Option("unique").Bool()
67
		if err != nil {
68 69
			res.SetError(err, cmds.ErrNormal)
			return
70
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
71 72 73

		recursive, _, err := req.Option("recursive").Bool()
		if err != nil {
74 75
			res.SetError(err, cmds.ErrNormal)
			return
76 77
		}

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
84 85
		format, _, err := req.Option("format").String()
		if err != nil {
86 87
			res.SetError(err, cmds.ErrNormal)
			return
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
88 89
		}

90
		objs, err := objectsForPaths(ctx, n, req.Arguments())
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
91
		if err != nil {
92 93
			res.SetError(err, cmds.ErrNormal)
			return
94
		}
95

Jeromy's avatar
Jeromy committed
96 97
		out := make(chan interface{})
		res.SetOutput((<-chan interface{})(out))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
98 99

		go func() {
Jeromy's avatar
Jeromy committed
100
			defer close(out)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
101 102

			rw := RefWriter{
Jeromy's avatar
Jeromy committed
103
				out:       out,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
104
				DAG:       n.DAG,
105
				Ctx:       ctx,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
106 107
				Unique:    unique,
				PrintEdge: edges,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
108
				PrintFmt:  format,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
109 110 111 112 113
				Recursive: recursive,
			}

			for _, o := range objs {
				if _, err := rw.WriteRefs(o); err != nil {
Jeromy's avatar
Jeromy committed
114
					out <- &RefWrapper{Err: err.Error()}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
115 116 117 118
					return
				}
			}
		}()
Jeromy's avatar
Jeromy committed
119 120 121 122 123 124 125
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			outChan, ok := res.Output().(<-chan interface{})
			if !ok {
				return nil, u.ErrCast()
			}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
126

Jeromy's avatar
Jeromy committed
127 128 129 130 131 132 133 134 135 136 137
			marshal := func(v interface{}) (io.Reader, error) {
				obj, ok := v.(*RefWrapper)
				if !ok {
					fmt.Println("%#v", v)
					return nil, u.ErrCast()
				}

				if obj.Err != "" {
					return nil, errors.New(obj.Err)
				}

Jeromy's avatar
Jeromy committed
138
				return strings.NewReader(obj.Ref + "\n"), nil
Jeromy's avatar
Jeromy committed
139 140 141 142 143
			}

			return &cmds.ChannelMarshaler{
				Channel:   outChan,
				Marshaler: marshal,
144
				Res:       res,
Jeromy's avatar
Jeromy committed
145 146
			}, nil
		},
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
147
	},
Jeromy's avatar
Jeromy committed
148
	Type: RefWrapper{},
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
149 150 151 152
}

var RefsLocalCmd = &cmds.Command{
	Helptext: cmds.HelpText{
rht's avatar
rht committed
153
		Tagline: "Lists all local references.",
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
154 155 156 157 158
		ShortDescription: `
Displays the hashes of all local objects.
`,
	},

159
	Run: func(req cmds.Request, res cmds.Response) {
Jeromy's avatar
Jeromy committed
160 161
		ctx := req.Context()
		n, err := req.InvocContext().GetNode()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
162
		if err != nil {
163 164
			res.SetError(err, cmds.ErrNormal)
			return
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
165 166 167
		}

		// todo: make async
168
		allKeys, err := n.Blockstore.AllKeysChan(ctx)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
169
		if err != nil {
170 171
			res.SetError(err, cmds.ErrNormal)
			return
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
172 173 174 175 176 177 178
		}

		piper, pipew := io.Pipe()

		go func() {
			defer pipew.Close()

179
			for k := range allKeys {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
180 181
				s := k.Pretty() + "\n"
				if _, err := pipew.Write([]byte(s)); err != nil {
Jeromy's avatar
Jeromy committed
182
					log.Error("pipe write error: ", err)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
183
					return
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
184 185 186 187
				}
			}
		}()

Jeromy's avatar
Jeromy committed
188
		res.SetOutput(piper)
189 190 191
	},
}

192
func objectsForPaths(ctx context.Context, n *core.IpfsNode, paths []string) ([]*dag.Node, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
193 194
	objects := make([]*dag.Node, len(paths))
	for i, p := range paths {
195
		o, err := core.Resolve(ctx, n, path.Path(p))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
196 197 198 199 200 201 202 203
		if err != nil {
			return nil, err
		}
		objects[i] = o
	}
	return objects, nil
}

Jeromy's avatar
Jeromy committed
204 205 206
type RefWrapper struct {
	Ref string
	Err string
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
207 208 209
}

type RefWriter struct {
Jeromy's avatar
Jeromy committed
210
	out chan interface{}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
211 212
	DAG dag.DAGService
	Ctx context.Context
213

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
214 215 216
	Unique    bool
	Recursive bool
	PrintEdge bool
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
217
	PrintFmt  string
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
218

219
	seen map[key.Key]struct{}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
220 221 222 223
}

// WriteRefs writes refs of the given object to the underlying writer.
func (rw *RefWriter) WriteRefs(n *dag.Node) (int, error) {
224 225 226
	if rw.Recursive {
		return rw.writeRefsRecursive(n)
	}
rht's avatar
rht committed
227
	return rw.writeRefsSingle(n)
228 229 230
}

func (rw *RefWriter) writeRefsRecursive(n *dag.Node) (int, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
231 232 233 234 235
	nkey, err := n.Key()
	if err != nil {
		return 0, err
	}

236 237
	var count int
	for i, ng := range rw.DAG.GetDAG(rw.Ctx, n) {
238
		lk := key.Key(n.Links[i].Hash)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
239 240 241 242
		if rw.skip(lk) {
			continue
		}

243
		if err := rw.WriteEdge(nkey, lk, n.Links[i].Name); err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
244 245
			return count, err
		}
246

247
		nd, err := ng.Get(rw.Ctx)
248
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
249
			return count, err
250 251
		}

252
		c, err := rw.writeRefsRecursive(nd)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
253
		count += c
254
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
255
			return count, err
256 257
		}
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
258
	return count, nil
259 260
}

261 262 263 264 265 266 267 268 269 270 271 272
func (rw *RefWriter) writeRefsSingle(n *dag.Node) (int, error) {
	nkey, err := n.Key()
	if err != nil {
		return 0, err
	}

	if rw.skip(nkey) {
		return 0, nil
	}

	count := 0
	for _, l := range n.Links {
273
		lk := key.Key(l.Hash)
274 275 276 277 278 279 280 281 282 283 284 285 286

		if rw.skip(lk) {
			continue
		}

		if err := rw.WriteEdge(nkey, lk, l.Name); err != nil {
			return count, err
		}
		count++
	}
	return count, nil
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
287
// skip returns whether to skip a key
288
func (rw *RefWriter) skip(k key.Key) bool {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
289 290 291
	if !rw.Unique {
		return false
	}
292

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
293
	if rw.seen == nil {
294
		rw.seen = make(map[key.Key]struct{})
295 296
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
297 298 299 300 301
	_, found := rw.seen[k]
	if !found {
		rw.seen[k] = struct{}{}
	}
	return found
302 303
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
304
// Write one edge
305
func (rw *RefWriter) WriteEdge(from, to key.Key, linkname string) error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
306 307 308 309 310
	if rw.Ctx != nil {
		select {
		case <-rw.Ctx.Done(): // just in case.
			return rw.Ctx.Err()
		default:
311 312 313
		}
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
314
	var s string
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
315 316 317 318 319 320 321 322 323 324
	switch {
	case rw.PrintFmt != "":
		s = rw.PrintFmt
		s = strings.Replace(s, "<src>", from.Pretty(), -1)
		s = strings.Replace(s, "<dst>", to.Pretty(), -1)
		s = strings.Replace(s, "<linkname>", linkname, -1)
	case rw.PrintEdge:
		s = from.Pretty() + " -> " + to.Pretty()
	default:
		s += to.Pretty()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
325 326
	}

Jeromy's avatar
Jeromy committed
327
	rw.out <- &RefWrapper{Ref: s}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
328
	return nil
329
}