refs edges + stream fix
Juan Batiz-Benet committed
Jan 7, 2015 at 12:56 UTC
f46120b194f8031451e829b0c9adecc7d53ed0ff
1 file changed
+159
-50
core/commands/refs.go
+159
-50
@@ -2,10 +2,11 @@ package commands
2
3
import (
4
"bytes"
5
- "fmt"
5
"io"
6
+ "sync"
7
+
8
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
9
8
- mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
10
cmds "github.com/jbenet/go-ipfs/commands"
11
"github.com/jbenet/go-ipfs/core"
12
dag "github.com/jbenet/go-ipfs/merkledag"
@@ -44,6 +45,7 @@ Note: list all refs recursively with -r.
45
cmds.StringArg("ipfs-path", true, true, "Path to the object(s) to list refs from"),
46
},
47
Options: []cmds.Option{
48
+ cmds.BoolOption("edges", "e", "Emit edge format: <from> -> <to>"),
49
cmds.BoolOption("unique", "u", "Omit duplicate refs from output"),
50
cmds.BoolOption("recursive", "r", "Recursively list links of child nodes"),
51
},
@@ -53,84 +55,191 @@ Note: list all refs recursively with -r.
55
return nil, err
56
}
57
56
- unique, found, err := req.Option("unique").Bool()
58
+ unique, _, err := req.Option("unique").Bool()
59
if err != nil {
60
return nil, err
61
}
60
- if !found {
61
- unique = false
62
+
63
+ recursive, _, err := req.Option("recursive").Bool()
64
+ if err != nil {
65
+ return nil, err
66
}
67
64
- recursive, found, err := req.Option("recursive").Bool()
68
+ edges, _, err := req.Option("edges").Bool()
69
if err != nil {
70
return nil, err
71
}
68
- if !found {
69
- recursive = false
72
+
73
+ objs, err := objectsForPaths(n, req.Arguments())
74
+ if err != nil {
75
+ return nil, err
76
}
77
72
- return getRefs(n, req.Arguments(), unique, recursive)
73
- },
74
- Type: KeyList{},
75
- Marshalers: cmds.MarshalerMap{
76
- cmds.Text: KeyListTextMarshaler,
78
+ piper, pipew := io.Pipe()
79
+ eptr := &ErrPassThroughReader{R: piper}
80
+
81
+ go func() {
82
+ defer pipew.Close()
83
+
84
+ rw := RefWriter{
85
+ W: pipew,
86
+ DAG: n.DAG,
87
+ Ctx: n.Context(),
88
+ Unique: unique,
89
+ PrintEdge: edges,
90
+ Recursive: recursive,
91
+ }
92
+
93
+ for _, o := range objs {
94
+ if _, err := rw.WriteRefs(o); err != nil {
95
+ eptr.SetError(err)
96
+ }
97
+ }
98
+ }()
99
+
100
+ return eptr, nil
101
},
102
}
103
80
-func getRefs(n *core.IpfsNode, paths []string, unique, recursive bool) (*KeyList, error) {
81
- var refsSeen map[u.Key]bool
82
- if unique {
83
- refsSeen = make(map[u.Key]bool)
104
+func objectsForPaths(n *core.IpfsNode, paths []string) ([]*dag.Node, error) {
105
+ objects := make([]*dag.Node, len(paths))
106
+ for i, p := range paths {
107
+ o, err := n.Resolver.ResolvePath(p)
108
+ if err != nil {
109
+ return nil, err
110
+ }
111
+ objects[i] = o
112
+ }
113
+ return objects, nil
114
+}
115
+
116
+// ErrPassThroughReader is a reader that may return an externally set error.
117
+type ErrPassThroughReader struct {
118
+ R io.ReadCloser
119
+ err error
120
+
121
+ sync.RWMutex
122
+}
123
+
124
+func (r *ErrPassThroughReader) Error() error {
125
+ r.RLock()
126
+ defer r.RUnlock()
127
+ return r.err
128
+}
129
+
130
+func (r *ErrPassThroughReader) SetError(err error) {
131
+ r.Lock()
132
+ r.err = err
133
+ r.Unlock()
134
+}
135
+
136
+func (r *ErrPassThroughReader) Read(buf []byte) (int, error) {
137
+ err := r.Error()
138
+ if err != nil {
139
+ return 0, err
140
+ }
141
+
142
+ return r.R.Read(buf)
143
+}
144
+
145
+func (r *ErrPassThroughReader) Close() error {
146
+ err1 := r.R.Close()
147
+ err2 := r.Error()
148
+ if err2 != nil {
149
+ return err2
150
}
151
+ return err1
152
+}
153
+
154
+type RefWriter struct {
155
+ W io.Writer
156
+ DAG dag.DAGService
157
+ Ctx context.Context
158
86
- refs := make([]u.Key, 0)
159
+ Unique bool
160
+ Recursive bool
161
+ PrintEdge bool
162
+
163
+ seen map[u.Key]struct{}
164
+}
165
+
166
+// WriteRefs writes refs of the given object to the underlying writer.
167
+func (rw *RefWriter) WriteRefs(n *dag.Node) (int, error) {
168
+ nkey, err := n.Key()
169
+ if err != nil {
170
+ return 0, err
171
+ }
172
+
173
+ if rw.skip(nkey) {
174
+ return 0, nil
175
+ }
176
+
177
+ count := 0
178
+ for _, l := range n.Links {
179
+ lk := u.Key(l.Hash)
180
+
181
+ if rw.skip(lk) {
182
+ continue
183
+ }
184
+
185
+ if err := rw.WriteEdge(nkey, lk); err != nil {
186
+ return count, err
187
+ }
188
+ count++
189
88
- for _, path := range paths {
89
- object, err := n.Resolver.ResolvePath(path)
190
+ if !rw.Recursive {
191
+ continue
192
+ }
193
+
194
+ child, err := l.GetNode(rw.DAG)
195
if err != nil {
91
- return nil, err
196
+ return count, err
197
}
198
94
- refs, err = addRefs(n, object, refs, refsSeen, recursive)
199
+ c, err := rw.WriteRefs(child)
200
+ count += c
201
if err != nil {
96
- return nil, err
202
+ return count, err
203
}
204
}
99
-
100
- return &KeyList{refs}, nil
205
+ return count, nil
206
}
207
103
-func addRefs(n *core.IpfsNode, object *dag.Node, refs []u.Key, refsSeen map[u.Key]bool, recursive bool) ([]u.Key, error) {
104
- for _, link := range object.Links {
105
- var found bool
106
- found, refs = addRef(link.Hash, refs, refsSeen)
107
-
108
- if recursive && !found {
109
- child, err := n.DAG.Get(u.Key(link.Hash))
110
- if err != nil {
111
- return nil, fmt.Errorf("cannot retrieve %s (%s)", link.Hash.B58String(), err)
112
- }
208
+// skip returns whether to skip a key
209
+func (rw *RefWriter) skip(k u.Key) bool {
210
+ if !rw.Unique {
211
+ return false
212
+ }
213
114
- refs, err = addRefs(n, child, refs, refsSeen, recursive)
115
- if err != nil {
116
- return nil, err
117
- }
118
- }
214
+ if rw.seen == nil {
215
+ rw.seen = make(map[u.Key]struct{})
216
}
217
121
- return refs, nil
218
+ _, found := rw.seen[k]
219
+ if !found {
220
+ rw.seen[k] = struct{}{}
221
+ }
222
+ return found
223
}
224
124
-func addRef(h mh.Multihash, refs []u.Key, refsSeen map[u.Key]bool) (bool, []u.Key) {
125
- key := u.Key(h)
126
- if refsSeen != nil {
127
- _, found := refsSeen[key]
128
- if found {
129
- return true, refs
225
+// Write one edge
226
+func (rw *RefWriter) WriteEdge(from, to u.Key) error {
227
+ if rw.Ctx != nil {
228
+ select {
229
+ case <-rw.Ctx.Done(): // just in case.
230
+ return rw.Ctx.Err()
231
+ default:
232
}
131
- refsSeen[key] = true
233
}
234
134
- refs = append(refs, key)
135
- return false, refs
235
+ var s string
236
+ if rw.PrintEdge {
237
+ s = from.Pretty() + " -> "
238
+ }
239
+ s += to.Pretty() + "\n"
240
+
241
+ if _, err := rw.W.Write([]byte(s)); err != nil {
242
+ return err
243
+ }
244
+ return nil
245
}