1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-06-20 02:21:48 +08:00

refs edges + stream fix

This commit is contained in:
Juan Batiz-Benet
2015-01-07 12:56:08 -08:00
parent cb108f4897
commit f46120b194

View File

@ -2,10 +2,11 @@ package commands
import ( import (
"bytes" "bytes"
"fmt"
"io" "io"
"sync"
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
cmds "github.com/jbenet/go-ipfs/commands" cmds "github.com/jbenet/go-ipfs/commands"
"github.com/jbenet/go-ipfs/core" "github.com/jbenet/go-ipfs/core"
dag "github.com/jbenet/go-ipfs/merkledag" dag "github.com/jbenet/go-ipfs/merkledag"
@ -44,6 +45,7 @@ Note: list all refs recursively with -r.
cmds.StringArg("ipfs-path", true, true, "Path to the object(s) to list refs from"), cmds.StringArg("ipfs-path", true, true, "Path to the object(s) to list refs from"),
}, },
Options: []cmds.Option{ Options: []cmds.Option{
cmds.BoolOption("edges", "e", "Emit edge format: <from> -> <to>"),
cmds.BoolOption("unique", "u", "Omit duplicate refs from output"), cmds.BoolOption("unique", "u", "Omit duplicate refs from output"),
cmds.BoolOption("recursive", "r", "Recursively list links of child nodes"), cmds.BoolOption("recursive", "r", "Recursively list links of child nodes"),
}, },
@ -53,84 +55,191 @@ Note: list all refs recursively with -r.
return nil, err return nil, err
} }
unique, found, err := req.Option("unique").Bool() unique, _, err := req.Option("unique").Bool()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !found {
unique = false
}
recursive, found, err := req.Option("recursive").Bool() recursive, _, err := req.Option("recursive").Bool()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !found {
recursive = false edges, _, err := req.Option("edges").Bool()
if err != nil {
return nil, err
} }
return getRefs(n, req.Arguments(), unique, recursive) objs, err := objectsForPaths(n, req.Arguments())
}, if err != nil {
Type: KeyList{}, return nil, err
Marshalers: cmds.MarshalerMap{ }
cmds.Text: KeyListTextMarshaler,
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,
Recursive: recursive,
}
for _, o := range objs {
if _, err := rw.WriteRefs(o); err != nil {
eptr.SetError(err)
}
}
}()
return eptr, nil
}, },
} }
func getRefs(n *core.IpfsNode, paths []string, unique, recursive bool) (*KeyList, error) { func objectsForPaths(n *core.IpfsNode, paths []string) ([]*dag.Node, error) {
var refsSeen map[u.Key]bool objects := make([]*dag.Node, len(paths))
if unique { for i, p := range paths {
refsSeen = make(map[u.Key]bool) o, err := n.Resolver.ResolvePath(p)
}
refs := make([]u.Key, 0)
for _, path := range paths {
object, err := n.Resolver.ResolvePath(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
objects[i] = o
}
return objects, nil
}
refs, err = addRefs(n, object, refs, refsSeen, recursive) // 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 { if err != nil {
return nil, err return 0, err
}
} }
return &KeyList{refs}, nil return r.R.Read(buf)
} }
func addRefs(n *core.IpfsNode, object *dag.Node, refs []u.Key, refsSeen map[u.Key]bool, recursive bool) ([]u.Key, error) { func (r *ErrPassThroughReader) Close() error {
for _, link := range object.Links { err1 := r.R.Close()
var found bool err2 := r.Error()
found, refs = addRef(link.Hash, refs, refsSeen) if err2 != nil {
return err2
}
return err1
}
if recursive && !found { type RefWriter struct {
child, err := n.DAG.Get(u.Key(link.Hash)) W io.Writer
DAG dag.DAGService
Ctx context.Context
Unique bool
Recursive bool
PrintEdge bool
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 { if err != nil {
return nil, fmt.Errorf("cannot retrieve %s (%s)", link.Hash.B58String(), err) return 0, err
} }
refs, err = addRefs(n, child, refs, refsSeen, recursive) if rw.skip(nkey) {
return 0, nil
}
count := 0
for _, l := range n.Links {
lk := u.Key(l.Hash)
if rw.skip(lk) {
continue
}
if err := rw.WriteEdge(nkey, lk); err != nil {
return count, err
}
count++
if !rw.Recursive {
continue
}
child, err := l.GetNode(rw.DAG)
if err != nil { if err != nil {
return nil, err return count, err
} }
c, err := rw.WriteRefs(child)
count += c
if err != nil {
return count, err
}
}
return count, nil
}
// skip returns whether to skip a key
func (rw *RefWriter) skip(k u.Key) bool {
if !rw.Unique {
return false
}
if rw.seen == nil {
rw.seen = make(map[u.Key]struct{})
}
_, found := rw.seen[k]
if !found {
rw.seen[k] = struct{}{}
}
return found
}
// Write one edge
func (rw *RefWriter) WriteEdge(from, to u.Key) error {
if rw.Ctx != nil {
select {
case <-rw.Ctx.Done(): // just in case.
return rw.Ctx.Err()
default:
} }
} }
return refs, nil var s string
if rw.PrintEdge {
s = from.Pretty() + " -> "
} }
s += to.Pretty() + "\n"
func addRef(h mh.Multihash, refs []u.Key, refsSeen map[u.Key]bool) (bool, []u.Key) { if _, err := rw.W.Write([]byte(s)); err != nil {
key := u.Key(h) return err
if refsSeen != nil {
_, found := refsSeen[key]
if found {
return true, refs
} }
refsSeen[key] = true return nil
}
refs = append(refs, key)
return false, refs
} }