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

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

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
10 11 12 13 14 15 16

	cmds "github.com/jbenet/go-ipfs/commands"
	"github.com/jbenet/go-ipfs/core"
	dag "github.com/jbenet/go-ipfs/merkledag"
	u "github.com/jbenet/go-ipfs/util"
)

17 18
// KeyList is a general type for outputting lists of keys
type KeyList struct {
19
	Keys []u.Key
20 21 22
}

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

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

39 40
  <link base58 hash>

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
62
		unique, _, err := req.Option("unique").Bool()
63 64 65
		if err != nil {
			return nil, err
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
66 67 68 69

		recursive, _, err := req.Option("recursive").Bool()
		if err != nil {
			return nil, err
70 71
		}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
72
		edges, _, err := req.Option("edges").Bool()
73 74 75
		if err != nil {
			return nil, err
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
76

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
77 78 79 80 81
		format, _, err := req.Option("format").String()
		if err != nil {
			return nil, err
		}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
82 83 84
		objs, err := objectsForPaths(n, req.Arguments())
		if err != nil {
			return nil, err
85
		}
86

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
87 88 89 90 91 92 93 94 95 96 97 98
		piper, pipew := io.Pipe()
		eptr := &ErrPassThroughReader{R: piper}

		go func() {
			defer pipew.Close()

			rw := RefWriter{
				W:         pipew,
				DAG:       n.DAG,
				Ctx:       n.Context(),
				Unique:    unique,
				PrintEdge: edges,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
99
				PrintFmt:  format,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
100 101 102 103 104
				Recursive: recursive,
			}

			for _, o := range objs {
				if _, err := rw.WriteRefs(o); err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
105
					log.Error(err)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
106
					eptr.SetError(err)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
					return
				}
			}
		}()

		return eptr, nil
	},
}

var RefsLocalCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Lists all local references",
		ShortDescription: `
Displays the hashes of all local objects.
`,
	},

	Run: func(req cmds.Request) (interface{}, error) {
		n, err := req.Context().GetNode()
		if err != nil {
			return nil, err
		}

		// todo: make async
		allKeys, err := n.Blockstore.AllKeys(0, 0)
		if err != nil {
			return nil, err
		}

		piper, pipew := io.Pipe()
		eptr := &ErrPassThroughReader{R: piper}

		go func() {
			defer pipew.Close()

			for _, k := range allKeys {
				s := k.Pretty() + "\n"
				if _, err := pipew.Write([]byte(s)); err != nil {
					log.Error(err)
					eptr.SetError(err)
					return
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
148 149 150 151 152
				}
			}
		}()

		return eptr, nil
153 154 155
	},
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
func objectsForPaths(n *core.IpfsNode, paths []string) ([]*dag.Node, error) {
	objects := make([]*dag.Node, len(paths))
	for i, p := range paths {
		o, err := n.Resolver.ResolvePath(p)
		if err != nil {
			return nil, err
		}
		objects[i] = o
	}
	return objects, nil
}

// ErrPassThroughReader is a reader that may return an externally set error.
type ErrPassThroughReader struct {
	R   io.ReadCloser
	err error

	sync.RWMutex
}

func (r *ErrPassThroughReader) Error() error {
	r.RLock()
	defer r.RUnlock()
	return r.err
}

func (r *ErrPassThroughReader) SetError(err error) {
	r.Lock()
	r.err = err
	r.Unlock()
}

func (r *ErrPassThroughReader) Read(buf []byte) (int, error) {
	err := r.Error()
	if err != nil {
		return 0, err
	}

	return r.R.Read(buf)
}

func (r *ErrPassThroughReader) Close() error {
	err1 := r.R.Close()
	err2 := r.Error()
	if err2 != nil {
		return err2
202
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
203 204 205 206 207 208 209
	return err1
}

type RefWriter struct {
	W   io.Writer
	DAG dag.DAGService
	Ctx context.Context
210

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
211 212 213
	Unique    bool
	Recursive bool
	PrintEdge bool
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
214
	PrintFmt  string
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237

	seen map[u.Key]struct{}
}

// WriteRefs writes refs of the given object to the underlying writer.
func (rw *RefWriter) WriteRefs(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 {
		lk := u.Key(l.Hash)

		if rw.skip(lk) {
			continue
		}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
238
		if err := rw.WriteEdge(nkey, lk, l.Name); err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
239 240 241
			return count, err
		}
		count++
242

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
243 244 245 246 247
		if !rw.Recursive {
			continue
		}

		child, err := l.GetNode(rw.DAG)
248
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
249
			return count, err
250 251
		}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
252 253
		c, err := rw.WriteRefs(child)
		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
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
261 262 263 264 265
// skip returns whether to skip a key
func (rw *RefWriter) skip(k u.Key) bool {
	if !rw.Unique {
		return false
	}
266

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
267 268
	if rw.seen == nil {
		rw.seen = make(map[u.Key]struct{})
269 270
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
271 272 273 274 275
	_, found := rw.seen[k]
	if !found {
		rw.seen[k] = struct{}{}
	}
	return found
276 277
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
278
// Write one edge
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
279
func (rw *RefWriter) WriteEdge(from, to u.Key, linkname string) error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
280 281 282 283 284
	if rw.Ctx != nil {
		select {
		case <-rw.Ctx.Done(): // just in case.
			return rw.Ctx.Err()
		default:
285 286 287
		}
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
288
	var s string
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
289 290 291 292 293 294 295 296 297 298
	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
299
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
300
	s += "\n"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
301 302 303 304 305

	if _, err := rw.W.Write([]byte(s)); err != nil {
		return err
	}
	return nil
306
}