refs.go 4.99 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 34 35 36
	Helptext: cmds.HelpText{
		Tagline: "Lists link hashes from an object",
		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.
`,
	},
44 45

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

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

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

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

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

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
85 86 87 88 89 90 91 92 93 94 95 96
		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
97
				PrintFmt:  format,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
98 99 100 101 102 103 104 105 106 107 108
				Recursive: recursive,
			}

			for _, o := range objs {
				if _, err := rw.WriteRefs(o); err != nil {
					eptr.SetError(err)
				}
			}
		}()

		return eptr, nil
109 110 111
	},
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
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 148 149 150 151 152 153 154 155 156 157
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
158
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
159 160 161 162 163 164 165
	return err1
}

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
167 168 169
	Unique    bool
	Recursive bool
	PrintEdge bool
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
170
	PrintFmt  string
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193

	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
194
		if err := rw.WriteEdge(nkey, lk, l.Name); err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
195 196 197
			return count, err
		}
		count++
198

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
199 200 201 202 203
		if !rw.Recursive {
			continue
		}

		child, err := l.GetNode(rw.DAG)
204
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
205
			return count, err
206 207
		}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
208 209
		c, err := rw.WriteRefs(child)
		count += c
210
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
211
			return count, err
212 213
		}
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
214
	return count, nil
215 216
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
217 218 219 220 221
// skip returns whether to skip a key
func (rw *RefWriter) skip(k u.Key) bool {
	if !rw.Unique {
		return false
	}
222

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
223 224
	if rw.seen == nil {
		rw.seen = make(map[u.Key]struct{})
225 226
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
227 228 229 230 231
	_, found := rw.seen[k]
	if !found {
		rw.seen[k] = struct{}{}
	}
	return found
232 233
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
234
// Write one edge
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
235
func (rw *RefWriter) WriteEdge(from, to u.Key, linkname string) error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
236 237 238 239 240
	if rw.Ctx != nil {
		select {
		case <-rw.Ctx.Done(): // just in case.
			return rw.Ctx.Err()
		default:
241 242 243
		}
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
244
	var s string
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
245 246 247 248 249 250 251 252 253 254
	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
255
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
256
	s += "\n"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
257 258 259 260 261

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